Compare commits
639 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96aacac10c | |||
| 4c32a0aba7 | |||
| 0f243b927c | |||
| fce86815f4 | |||
| 3b4f28d173 | |||
| b854f4037f | |||
| 30bd84a935 | |||
| 0a8398dd99 | |||
| 01cc0cd787 | |||
| 0d4b68239a | |||
| 68a8c42176 | |||
| 8b733728af | |||
| 1fa464ec3e | |||
| 3368bd75c7 | |||
| 827c66f4f6 | |||
| 21dc2c761d | |||
| 529f387695 | |||
| dacd08dc5d | |||
| 072ca731e2 | |||
| a3010f50be | |||
| 4be6584850 | |||
| 24b646dec0 | |||
| 7d76cbddd6 | |||
| 90ca9b9ff4 | |||
| 2b9c213f89 | |||
| adfbe0d6cd | |||
| 125ac166d1 | |||
| 1e33c4a922 | |||
| a8250db2bb | |||
| 04921dd727 | |||
| 06da60c555 | |||
| 32edc7f33f | |||
| 4f92bd5938 | |||
| 4aa029183b | |||
| 3f165040e2 | |||
| 754a32fa16 | |||
| 57b860d8ca | |||
| 0c67429a38 | |||
| aa58a5d208 | |||
| 3894c580bf | |||
| f1bf932a84 | |||
| 8ab448b119 | |||
| 39a5d0cb94 | |||
| 7d68eeac88 | |||
| 92fd59fe6a | |||
| f79683d459 | |||
| 83dba26ea6 | |||
| 809f709ba6 | |||
| 6fe1ffa3bb | |||
| d117a5209f | |||
| ed1f1d2bf3 | |||
| fd3f52ea36 | |||
| 4c61103e34 | |||
| dc3fa3e51f | |||
| e2d7c5dfb3 | |||
| 7e2cac5292 | |||
| a80ab01227 | |||
| 73e08e51ba | |||
| fd1458ce1b | |||
| 9c643f0356 | |||
| 35b78d55a4 | |||
| bd9c6989c2 | |||
| 72e4b60374 | |||
| 48b265e136 | |||
| 7865ebb6d5 | |||
| f765099503 | |||
| d5dd4937eb | |||
| 1bba276aaf | |||
| 5233c4aa21 | |||
| 0b7f35b289 | |||
| 46c59f382f | |||
| 8854720e8a | |||
| 42d3235a66 | |||
| 5cba81cf35 | |||
| a5edf487dd | |||
| 728dd5a4e0 | |||
| ac4061b9d2 | |||
| ec84a107a4 | |||
| 803d331711 | |||
| 422cf429e6 | |||
| 93de5350b9 | |||
| 9bf3ee722c | |||
| 1782208fde | |||
| 84a94a6109 | |||
| c2a4a35c2e | |||
| f3d4220cab | |||
| 8219bb374d | |||
| 80df89d0db | |||
| ecdc58c1c9 | |||
| da63b4a596 | |||
| 4e315353c8 | |||
| 720ec2beea | |||
| a235259b5e | |||
| 97c165b951 | |||
| da6f32a1c0 | |||
| d3a81f4f28 | |||
| dd51ba396b | |||
| 7501c8b483 | |||
| 5e7513d0a0 | |||
| cd2fd126c4 | |||
| 0cd375f03c | |||
| 588a6bcb4f | |||
| 72e39ba1c9 | |||
| 6534aa0c27 | |||
| e42489b3e9 | |||
| 2b9064cb6e | |||
| 197e88cdcc | |||
| 4ac4889912 | |||
| 4ee04cd444 | |||
| 43dccc8807 | |||
| df9faba689 | |||
| 5272caf7f4 | |||
| 1e04c4c689 | |||
| dd055fc858 | |||
| 5c0673049c | |||
| f240952bcc | |||
| e787a241c0 | |||
| 3867cad94d | |||
| be30e6bf8f | |||
| 1fb6428ffa | |||
| b31a54e972 | |||
| 75e2cb97f7 | |||
| ac8dbd7a56 | |||
| d8a659c6f2 | |||
| af90d89b6b | |||
| a19296e3cf | |||
| 85c03925fb | |||
| 22413b82ca | |||
| e8aaa9cb13 | |||
| 2385ab700c | |||
| 242ed45f65 | |||
| e420752e82 | |||
| 54109043b8 | |||
| ddd4751f87 | |||
| 492c75cb7b | |||
| 1e0728af64 | |||
| 1cc6983b96 | |||
| 8311b57ca6 | |||
| 510efae066 | |||
| b3ba90f5fa | |||
| e10595df31 | |||
| b51d30f84d | |||
| ea8b52ee2c | |||
| 9575eebac4 | |||
| ace8e64850 | |||
| 4ee26f4ca3 | |||
| a55c93e1f5 | |||
| 400ab8766c | |||
| 90b8d639f1 | |||
| 525b6fe168 | |||
| f96a8fe369 | |||
| 150586d7fe | |||
| 09194fd488 | |||
| e619b64175 | |||
| 795ed65797 | |||
| 5b82dd8733 | |||
| 9c5307a48a | |||
| b1fe405861 | |||
| 869637af2e | |||
| 414b56edec | |||
| 334bc4f551 | |||
| e288c3b44a | |||
| 91900b0599 | |||
| 29ae74d4bb | |||
| a2c24ff946 | |||
| 61de399806 | |||
| f48a1e3a31 | |||
| 4a8c3b50b6 | |||
| 852ad41176 | |||
| 880d555060 | |||
| 5523913a81 | |||
| a7da29804d | |||
| cae4b1a062 | |||
| 2b16ff3ca8 | |||
| 5aecce5a0d | |||
| d85ddf8aae | |||
| 788f5398b0 | |||
| 964723691a | |||
| 2475d4652c | |||
| 79eb2ea26e | |||
| f3672ab26b | |||
| f757fbce4d | |||
| e5e3b05015 | |||
| eccf679ca9 | |||
| 3fc9d3a1be | |||
| 3f071a90a4 | |||
| 413d55aaa5 | |||
| 6d8062a2a7 | |||
| 6990478369 | |||
| e37634e908 | |||
| 63c9d95b84 | |||
| 63c35248be | |||
| 6685e0a078 | |||
| 7789cc8e32 | |||
| 1d6622aa2b | |||
| eeff730034 | |||
| 23fd7451d0 | |||
| bc3a96308e | |||
| 8e976a92f7 | |||
| 7a23330ffe | |||
| 6ea3b5445f | |||
| 091e676db3 | |||
| 6ad171781a | |||
| d157d66678 | |||
| 4da6364fc2 | |||
| 7b95753cf7 | |||
| 8f3d496541 | |||
| 0a6c17c544 | |||
| f11d7eeadf | |||
| 22a7bcd5ad | |||
| ecc51c8c55 | |||
| 55a73b8df9 | |||
| c40b4ce3f2 | |||
| 47260fc70b | |||
| c38854fe3d | |||
| df6e4617e4 | |||
| 56423b9ef1 | |||
| 6fd23f0984 | |||
| 325fca5215 | |||
| 3fde27d890 | |||
| 445f63f350 | |||
| 603e3e23ac | |||
| 2011421e18 | |||
| 7c0e68a33e | |||
| f65e83c349 | |||
| 942abab8f9 | |||
| 8eb7a94e1c | |||
| 6b35a4534f | |||
| dac9379449 | |||
| a6ddeaf5f1 | |||
| 30821e978b | |||
| c0e7a5b8b0 | |||
| 8b24255557 | |||
| 46e7e96eb3 | |||
| 41cc82e84b | |||
| 2866c6cf28 | |||
| 0c7fa1b955 | |||
| aa7df8183f | |||
| 58760e6184 | |||
| 52ca70d98e | |||
| d4fc87fc9c | |||
| 2187316bce | |||
| 52dee31270 | |||
| 22d3e3b563 | |||
| 4e613c5d4b | |||
| 07c8857558 | |||
| 9bd1678299 | |||
| 19281868dc | |||
| 5133fe5115 | |||
| 2eb29d538b | |||
| 0af075b5ff | |||
| ef2b813776 | |||
| a6f93a501a | |||
| 767807cd22 | |||
| 86319127e3 | |||
| 179e1c4dbc | |||
| 945d74cbc9 | |||
| b8636a3dbf | |||
| 4e6c758daf | |||
| 4bd02eada6 | |||
| bd32a682f7 | |||
| 03274a5b95 | |||
| 6f62fed65c | |||
| cba37819d1 | |||
| 4b85104c4f | |||
| 0d91d63cf3 | |||
| abc6e37e6e | |||
| 812cb62617 | |||
| a211c2f5e4 | |||
| d25398d2c7 | |||
| 654a45833a | |||
| 455763aa9a | |||
| c7456affd5 | |||
| a1ef5e38b6 | |||
| 14bdc6c2ef | |||
| bf886444d2 | |||
| 9fc2dbe063 | |||
| 51c6d13c0f | |||
| e7ac63be4d | |||
| d7d043a5ef | |||
| 1002bc36e0 | |||
| 6ff50558a0 | |||
| fa9ab9af7c | |||
| b5aa0c0759 | |||
| 79604c3c0e | |||
| 7a974b2460 | |||
| 7f3d69fdbc | |||
| 7dafac0c34 | |||
| c24aea712b | |||
| 063ad490a2 | |||
| 59768674f1 | |||
| 2cb49e51a0 | |||
| 6b76eddbe3 | |||
| 59934b47de | |||
| 4d417f3dd4 | |||
| dce497fbca | |||
| e63e0ee596 | |||
| 494e9fd61d | |||
| b11e31fbfd | |||
| 1e93122470 | |||
| 5b11129eaa | |||
| ee3d53a673 | |||
| c346baff5b | |||
| 20bee69955 | |||
| 4cd04f192d | |||
| f5f996c14a | |||
| da1ee3baba | |||
| 1d7a5dbede | |||
| 9b878c64f9 | |||
| 1f61472e77 | |||
| b99e64a0e3 | |||
| e49607078e | |||
| 4ef60d7214 | |||
| 40bda4d93f | |||
| 8f4c078fb5 | |||
| 80cc32d77b | |||
| 28fa82378b | |||
| 6a4a88f8f5 | |||
| a114d6cbb5 | |||
| 3acd2a55ad | |||
| 3a90fd5751 | |||
| ec14c98f2d | |||
| 975c682ec3 | |||
| e240351d81 | |||
| 4ec31291c0 | |||
| 7a538141c9 | |||
| bcdc399029 | |||
| 8652ae046b | |||
| 833eface38 | |||
| 7256bc92fa | |||
| 0394d5a44f | |||
| e7217760f3 | |||
| a042dbf305 | |||
| 328fd7329d | |||
| a24a22c39a | |||
| c2710ffe87 | |||
| 782eb650c7 | |||
| 38a3ba74d4 | |||
| d1da9f1f67 | |||
| dcc5a10c7b | |||
| 0051ebace0 | |||
| 0cd61fd197 | |||
| f16ece6048 | |||
| e12059a311 | |||
| cb9fb31214 | |||
| e8c268ef09 | |||
| 6a839600b9 | |||
| 4d1795e90b | |||
| 277127829a | |||
| 58fd9d70d2 | |||
| d2cf61c99a | |||
| 6562a939ea | |||
| 031cf24880 | |||
| 38261a0f2a | |||
| c4a76af397 | |||
| f76ab02bd4 | |||
| 4d392a8b87 | |||
| c969dad6c2 | |||
| 9718502e5d | |||
| 9613b4bfe2 | |||
| 5d2196c119 | |||
| a885fd6d0e | |||
| 3ba4746ff8 | |||
| e1e1c73ae6 | |||
| fbafbe6272 | |||
| 6ae7fc5df1 | |||
| 99f0377ae3 | |||
| 932939c5a8 | |||
| 0c9c846522 | |||
| f5236ab186 | |||
| 2a7f5aa569 | |||
| 1d86aea61c | |||
| 2ded5547ba | |||
| 395278fcdb | |||
| 786a05876e | |||
| caa3b4f7f8 | |||
| 2266a9e01e | |||
| def2547e40 | |||
| 2ac0adaebb | |||
| 4ebb97bf73 | |||
| b05592b0ab | |||
| 9ee58fd454 | |||
| 4f9a0460c4 | |||
| ad61392318 | |||
| a852ecf8ce | |||
| d6d6ec03f1 | |||
| fce72610bb | |||
| 4a368ab905 | |||
| f1f7fba3b9 | |||
| ca07e210a7 | |||
| 5f3ae527c9 | |||
| db13af1935 | |||
| efd11f32a2 | |||
| 6489fb586b | |||
| 427212705f | |||
| f6276164f3 | |||
| dcb934fc2e | |||
| c2c3846930 | |||
| 03d4e4acc4 | |||
| 4e647bda07 | |||
| ab2295b68f | |||
| 940c6146e8 | |||
| b56673bbce | |||
| 2102625021 | |||
| 884fe40fd1 | |||
| 3baf9d0e81 | |||
| 248832b266 | |||
| bf8c889631 | |||
| 273dca2fa0 | |||
| e80b016cbe | |||
| 8c19497340 | |||
| 5deb1df8db | |||
| 14a0df03e8 | |||
| bc44efb91d | |||
| 799dfb732b | |||
| 813b06a3a4 | |||
| 47c9b0a17e | |||
| 44795b8614 | |||
| 5acb2e5165 | |||
| 682205a3d4 | |||
| 7441311e3d | |||
| 6f48dac4cc | |||
| bc078e5158 | |||
| 63416bf28f | |||
| 0152bc0d10 | |||
| 7e491ef2b2 | |||
| 6f532fa0e5 | |||
| 25b3ed3532 | |||
| 7a77a20a67 | |||
| 2957d85924 | |||
| 2b968766b9 | |||
| 78cb70a5f9 | |||
| 16e4e0fa7c | |||
| c031b3cc6a | |||
| bbc4ebecab | |||
| 71ad3b1715 | |||
| af8bbefcab | |||
| 26043269e4 | |||
| c9edf7cf78 | |||
| 77910afe57 | |||
| 4db1328bc5 | |||
| e634564b26 | |||
| dae5faedb0 | |||
| 1f236d8f20 | |||
| 370817ba60 | |||
| 2873abcbc0 | |||
| e89c183231 | |||
| ca9e6a4f94 | |||
| b11131ce0c | |||
| 2d2ca1d791 | |||
| 85a8f965e5 | |||
| 88545ccae5 | |||
| 3832b25f30 | |||
| 15ad5245bf | |||
| 8aa9e35f8c | |||
| 81b13f78ea | |||
| 9b9ec2c21a | |||
| f3c9bc114c | |||
| ff231b42ff | |||
| 9c33f4aea8 | |||
| f55583330b | |||
| 2c8e4467c3 | |||
| 15f9d15ba0 | |||
| 279799ad70 | |||
| ea6b384980 | |||
| ef63ec3fe9 | |||
| de8c5b1824 | |||
| 9e2eb5dc23 | |||
| 5d1207104a | |||
| d547f52d8f | |||
| 56e9cb69d3 | |||
| 70973035de | |||
| c8a30592e8 | |||
| d8258984b4 | |||
| c423b6c8b7 | |||
| 4cc1c64646 | |||
| 0e1fd34c89 | |||
| 10f2531b6c | |||
| ecd327e5e1 | |||
| 510bccb6ac | |||
| 32330c5eef | |||
| e00ef16893 | |||
| fd8143fea0 | |||
| 08a4840298 | |||
| a16f933e3a | |||
| 9d61f429a5 | |||
| b8de63d608 | |||
| dcc0f6e25d | |||
| 21684fac69 | |||
| 329333ca8b | |||
| 91e20dc12e | |||
| aecfa0b426 | |||
| 9675068706 | |||
| cbd55ad31e | |||
| 5eb1753d31 | |||
| e277f5e268 | |||
| 145bf52b7a | |||
| fe7bdc93eb | |||
| d889435334 | |||
| dad17a7d91 | |||
| 830adf6ae8 | |||
| babfa70cab | |||
| 258eadd003 | |||
| 6dd1643a70 | |||
| 48916ebd76 | |||
| 506d316f55 | |||
| 490b71378e | |||
| 0e8b0c520e | |||
| 0136df1e62 | |||
| 98731bb574 | |||
| 27dfb7fd11 | |||
| 6457df54bb | |||
| 07f3ed0419 | |||
| dabb99de8d | |||
| 254ebe5f96 | |||
| b32a366137 | |||
| 3633b65e04 | |||
| ce74098b40 | |||
| 5769efe48e | |||
| 02051ab51a | |||
| 02c0620141 | |||
| d7895db166 | |||
| 0f84e32520 | |||
| 1a911be617 | |||
| a0565dc9bb | |||
| 52569ef562 | |||
| 661bd054aa | |||
| eee4aca40b | |||
| 04cf832fe6 | |||
| 2346426b6e | |||
| 8253014fe9 | |||
| 57e516720e | |||
| d229afb83d | |||
| 3cd1f30577 | |||
| 94347d4e64 | |||
| 6fe6c370c2 | |||
| 42acafcbc3 | |||
| ebd42ab619 | |||
| b10780f119 | |||
| d657c51be8 | |||
| 30007263e5 | |||
| ff3d31e17f | |||
| d281b25723 | |||
| 2a3b5ab9e9 | |||
| d9f57c099b | |||
| 32f72c7471 | |||
| 5993ef0baa | |||
| d96be76875 | |||
| 0e2e9e9a29 | |||
| 7c88e4a16b | |||
| eac0539ea0 | |||
| b82d2cf055 | |||
| 7d4028b2df | |||
| 0349bcc500 | |||
| 2f5b86dd38 | |||
| 5ec11a017c | |||
| b3f7df6f5b | |||
| d57eca57a9 | |||
| 71ce90d083 | |||
| 296abf03de | |||
| 1ab8902621 | |||
| fd70b0bca6 | |||
| 101d284a18 | |||
| 2f66139053 | |||
| 8e64adb2d3 | |||
| c5a917b401 | |||
| 27ab921e5b | |||
| 369f52a0d7 | |||
| 9007b77355 | |||
| fc9cb2fbd8 | |||
| 46b9efc5a2 | |||
| 90cedbea49 | |||
| 0b243f1aaf | |||
| bc386af5c5 | |||
| 4ca8b4080a | |||
| c7273ada8e | |||
| 724d6267c8 | |||
| 653edd4b02 | |||
| 0b0ae75ccf | |||
| 226efffd21 | |||
| 88d76bf069 | |||
| 67f183f206 | |||
| 0408f9b7df | |||
| e3d99412a2 | |||
| cd15b1775e | |||
| 2916efee21 | |||
| 6c367cad6e | |||
| fde02abc35 | |||
| fdce192aab | |||
| a4d357de06 | |||
| 773d4664ff | |||
| 72e3398baf | |||
| f1bbf2e48a | |||
| bfcde9ae8a | |||
| b1ff2a05af | |||
| 561e95528f | |||
| 9ea32713b5 | |||
| 3d76e1c514 | |||
| 61097d467c | |||
| 46f18ba114 | |||
| ef7ce82828 | |||
| 921a820867 | |||
| e7c4681e9a | |||
| 877ecda14d | |||
| 079e4b1edb | |||
| 65bcbe0dc8 | |||
| bbabc9705e | |||
| 520938da57 | |||
| 97d09b4cee | |||
| 0a147693e8 | |||
| 55643d6370 | |||
| 051828344e | |||
| 3e56e7f562 | |||
| e8e1a05f38 | |||
| 5f50d085ad | |||
| f75020da5a | |||
| 75bf45376c | |||
| d2e485cc6d | |||
| fdd5070c3b | |||
| 5048956ff0 | |||
| 080c076643 | |||
| 3f3a3ac4f1 | |||
| 9fd3e5f529 | |||
| e0408d8edf | |||
| 5008024e73 | |||
| f9c09cc845 | |||
| 520dd5c779 | |||
| 4dd53ab942 | |||
| 0604b00360 | |||
| a931314d3f | |||
| 2987c87645 | |||
| 649bf575e2 | |||
| ad3c4b81e2 | |||
| 530002e168 | |||
| 6eadccc99d | |||
| 8a735d0ba9 | |||
| 88c0d22e7c | |||
| e5431bbef7 | |||
| de51ba088e |
+4
-6
@@ -1,7 +1,5 @@
|
||||
build
|
||||
winbuild
|
||||
win64build
|
||||
include/SLES
|
||||
include/sndio.h
|
||||
include/sys
|
||||
build*/
|
||||
winbuild/
|
||||
win64build/
|
||||
openal-soft.kdev4
|
||||
.kdev4/
|
||||
|
||||
+12
-16
@@ -9,9 +9,6 @@ matrix:
|
||||
- BUILD_ANDROID=true
|
||||
- os: osx
|
||||
sudo: required
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/android-ndk-r14
|
||||
install:
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
|
||||
@@ -27,18 +24,17 @@ install:
|
||||
fi
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
|
||||
if [[ ! -d ~/android-ndk-r14 || -z "$(ls -A ~/android-ndk-r14)" ]]; then
|
||||
curl -o ~/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r14-linux-x86_64.zip
|
||||
unzip -q ~/android-ndk.zip -d ~ \
|
||||
'android-ndk-r14/build/cmake/*' \
|
||||
'android-ndk-r14/platforms/android-9/arch-arm/*' \
|
||||
'android-ndk-r14/source.properties' \
|
||||
'android-ndk-r14/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/*' \
|
||||
'android-ndk-r14/sysroot/*' \
|
||||
'android-ndk-r14/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/*' \
|
||||
'android-ndk-r14/toolchains/llvm/prebuilt/linux-x86_64/*'
|
||||
sed -i -e 's/VERSION 3.6.0/VERSION 3.2/' ~/android-ndk-r14/build/cmake/android.toolchain.cmake
|
||||
fi
|
||||
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:
|
||||
- >
|
||||
@@ -55,7 +51,7 @@ script:
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
|
||||
cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r14/build/cmake/android.toolchain.cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r15/build/cmake/android.toolchain.cmake \
|
||||
-DALSOFT_REQUIRE_OPENSL=ON \
|
||||
-DALSOFT_EMBED_HRTF_DATA=YES \
|
||||
.
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alconfig.h"
|
||||
#include "compat.h"
|
||||
#include "bool.h"
|
||||
|
||||
@@ -365,9 +366,9 @@ static void LoadConfigFromFile(FILE *f)
|
||||
#ifdef _WIN32
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
al_string ppath = AL_STRING_INIT_STATIC();
|
||||
WCHAR buffer[MAX_PATH];
|
||||
const WCHAR *str;
|
||||
al_string ppath;
|
||||
FILE *f;
|
||||
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
@@ -386,7 +387,7 @@ void ReadALConfig(void)
|
||||
alstr_reset(&filepath);
|
||||
}
|
||||
|
||||
ppath = GetProcPath();
|
||||
GetProcBinary(&ppath, NULL);
|
||||
if(!alstr_empty(ppath))
|
||||
{
|
||||
alstr_append_cstr(&ppath, "\\alsoft.ini");
|
||||
@@ -419,9 +420,9 @@ void ReadALConfig(void)
|
||||
#else
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
al_string confpaths = AL_STRING_INIT_STATIC();
|
||||
al_string fname = AL_STRING_INIT_STATIC();
|
||||
const char *str;
|
||||
al_string ppath;
|
||||
FILE *f;
|
||||
|
||||
str = "/etc/openal/alsoft.conf";
|
||||
@@ -436,45 +437,55 @@ void ReadALConfig(void)
|
||||
|
||||
if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
|
||||
str = "/etc/xdg";
|
||||
strncpy(buffer, str, sizeof(buffer)-1);
|
||||
buffer[sizeof(buffer)-1] = 0;
|
||||
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(1)
|
||||
while(!alstr_empty(confpaths))
|
||||
{
|
||||
char *next = strrchr(buffer, ':');
|
||||
if(next) *(next++) = 0;
|
||||
else next = buffer;
|
||||
|
||||
if(next[0] != '/')
|
||||
WARN("Ignoring XDG config dir: %s\n", next);
|
||||
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
|
||||
{
|
||||
size_t len = strlen(next);
|
||||
strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len);
|
||||
buffer[sizeof(buffer)-1] = 0;
|
||||
alstr_reset(&fname);
|
||||
fname = confpaths;
|
||||
AL_STRING_INIT(confpaths);
|
||||
}
|
||||
|
||||
TRACE("Loading config %s...\n", next);
|
||||
f = al_fopen(next, "r");
|
||||
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);
|
||||
}
|
||||
}
|
||||
if(next == buffer)
|
||||
break;
|
||||
alstr_clear(&fname);
|
||||
}
|
||||
|
||||
if((str=getenv("HOME")) != NULL && *str)
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", 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", buffer);
|
||||
f = al_fopen(buffer, "r");
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
|
||||
f = al_fopen(alstr_get_cstr(fname), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
@@ -483,17 +494,25 @@ void ReadALConfig(void)
|
||||
}
|
||||
|
||||
if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf");
|
||||
{
|
||||
alstr_copy_cstr(&fname, str);
|
||||
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
|
||||
else alstr_append_cstr(&fname, "alsoft.conf");
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[0] = 0;
|
||||
alstr_clear(&fname);
|
||||
if((str=getenv("HOME")) != NULL && str[0] != 0)
|
||||
snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf");
|
||||
{
|
||||
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(buffer[0] != 0)
|
||||
if(!alstr_empty(fname))
|
||||
{
|
||||
TRACE("Loading config %s...\n", buffer);
|
||||
f = al_fopen(buffer, "r");
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
|
||||
f = al_fopen(alstr_get_cstr(fname), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
@@ -501,12 +520,15 @@ void ReadALConfig(void)
|
||||
}
|
||||
}
|
||||
|
||||
ppath = GetProcPath();
|
||||
if(!alstr_empty(ppath))
|
||||
alstr_clear(&fname);
|
||||
GetProcBinary(&fname, NULL);
|
||||
if(!alstr_empty(fname))
|
||||
{
|
||||
alstr_append_cstr(&ppath, "/alsoft.conf");
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
|
||||
f = al_fopen(alstr_get_cstr(ppath), "r");
|
||||
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);
|
||||
@@ -525,7 +547,8 @@ void ReadALConfig(void)
|
||||
}
|
||||
}
|
||||
|
||||
alstr_reset(&ppath);
|
||||
alstr_reset(&fname);
|
||||
alstr_reset(&confpaths);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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 */
|
||||
@@ -6,6 +6,10 @@
|
||||
#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)
|
||||
@@ -43,7 +47,12 @@ void alstr_append_range(al_string *str, const al_string_char_type *from, const a
|
||||
/* Windows-only methods to deal with WideChar strings. */
|
||||
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 */
|
||||
|
||||
+63
-37
@@ -26,6 +26,8 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
@@ -436,7 +438,7 @@ typedef struct ALCplaybackAlsa {
|
||||
ALvoid *buffer;
|
||||
ALsizei size;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCplaybackAlsa;
|
||||
|
||||
@@ -444,9 +446,8 @@ 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);
|
||||
@@ -464,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -483,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;
|
||||
}
|
||||
@@ -573,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;
|
||||
}
|
||||
@@ -700,11 +714,6 @@ static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *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;
|
||||
@@ -903,7 +912,7 @@ 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");
|
||||
@@ -924,10 +933,8 @@ 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);
|
||||
|
||||
al_free(self->buffer);
|
||||
@@ -971,9 +978,8 @@ typedef struct ALCcaptureAlsa {
|
||||
} 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);
|
||||
@@ -991,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -1098,8 +1123,9 @@ static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
|
||||
if(needring)
|
||||
{
|
||||
self->ring = ll_ringbuffer_create(
|
||||
device->UpdateSize*device->NumUpdates + 1,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
|
||||
device->UpdateSize*device->NumUpdates,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
|
||||
false
|
||||
);
|
||||
if(!self->ring)
|
||||
{
|
||||
@@ -1120,26 +1146,26 @@ error2:
|
||||
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);
|
||||
ll_ringbuffer_free(self->ring);
|
||||
|
||||
al_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;
|
||||
}
|
||||
|
||||
@@ -1190,7 +1216,7 @@ static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buff
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -1233,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
|
||||
@@ -1258,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)
|
||||
{
|
||||
@@ -1274,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1313,7 +1339,7 @@ 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;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
|
||||
extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
|
||||
extern inline void ALCdevice_Lock(ALCdevice *device);
|
||||
extern inline void ALCdevice_Unlock(ALCdevice *device);
|
||||
|
||||
/* Base ALCbackend method implementations. */
|
||||
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
|
||||
|
||||
+17
-4
@@ -5,6 +5,10 @@
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ClockLatency {
|
||||
ALint64 ClockTime;
|
||||
ALint64 Latency;
|
||||
@@ -43,7 +47,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*);
|
||||
@@ -63,7 +66,6 @@ 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) \
|
||||
@@ -79,7 +81,6 @@ 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, \
|
||||
@@ -143,13 +144,25 @@ ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
|
||||
|
||||
inline void ALCdevice_Lock(ALCdevice *device)
|
||||
{ V0(device->Backend,lock)(); }
|
||||
|
||||
inline void ALCdevice_Unlock(ALCdevice *device)
|
||||
{ V0(device->Backend,unlock)(); }
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AL_BACKENDS_BASE_H */
|
||||
|
||||
+66
-84
@@ -23,10 +23,10 @@
|
||||
#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>
|
||||
@@ -36,56 +36,9 @@
|
||||
#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
|
||||
|
||||
ll_ringbuffer_t *ring;
|
||||
} ca_data;
|
||||
|
||||
static const ALCchar ca_device[] = "CoreAudio Default";
|
||||
|
||||
|
||||
static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
|
||||
{
|
||||
AudioBufferList *list;
|
||||
|
||||
list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
|
||||
if(list)
|
||||
{
|
||||
list->mNumberBuffers = 1;
|
||||
|
||||
list->mBuffers[0].mNumberChannels = channelCount;
|
||||
list->mBuffers[0].mDataByteSize = byteSize;
|
||||
list->mBuffers[0].mData = malloc(byteSize);
|
||||
if(list->mBuffers[0].mData == NULL)
|
||||
{
|
||||
free(list);
|
||||
list = NULL;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static void destroy_buffer_list(AudioBufferList* list)
|
||||
{
|
||||
if(list)
|
||||
{
|
||||
UInt32 i;
|
||||
for(i = 0;i < list->mNumberBuffers;i++)
|
||||
free(list->mBuffers[i].mData);
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCcoreAudioPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
@@ -98,7 +51,6 @@ typedef struct ALCcoreAudioPlayback {
|
||||
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
|
||||
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self);
|
||||
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name);
|
||||
static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self);
|
||||
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self);
|
||||
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self);
|
||||
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self);
|
||||
@@ -123,6 +75,9 @@ static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice
|
||||
|
||||
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
AudioUnitUninitialize(self->audioUnit);
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
@@ -134,10 +89,10 @@ static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon,
|
||||
ALCcoreAudioPlayback *self = inRefCon;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
ALCdevice_Lock(device);
|
||||
ALCcoreAudioPlayback_lock(self);
|
||||
aluMixData(device, ioData->mBuffers[0].mData,
|
||||
ioData->mBuffers[0].mDataByteSize / self->frameSize);
|
||||
ALCdevice_Unlock(device);
|
||||
ALCcoreAudioPlayback_unlock(self);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
@@ -189,12 +144,6 @@ static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCch
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCcoreAudioPlayback_close(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
AudioUnitUninitialize(self->audioUnit);
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
}
|
||||
|
||||
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
@@ -382,7 +331,6 @@ typedef struct ALCcoreAudioCapture {
|
||||
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
|
||||
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
|
||||
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
|
||||
static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self);
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
|
||||
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
|
||||
@@ -396,15 +344,59 @@ 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));
|
||||
}
|
||||
|
||||
@@ -667,8 +659,8 @@ static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar
|
||||
goto error;
|
||||
|
||||
self->ring = ll_ringbuffer_create(
|
||||
device->UpdateSize*self->sampleRateRatio*device->NumUpdates + 1,
|
||||
self->frameSize
|
||||
(size_t)ceil(device->UpdateSize*self->sampleRateRatio*device->NumUpdates),
|
||||
self->frameSize, false
|
||||
);
|
||||
if(!self->ring) goto error;
|
||||
|
||||
@@ -680,30 +672,21 @@ error:
|
||||
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;
|
||||
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
|
||||
static void ALCcoreAudioCapture_close(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
free(self->resampleBuffer);
|
||||
|
||||
destroy_buffer_list(self->bufferList);
|
||||
|
||||
AudioConverterDispose(self->audioConverter);
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
}
|
||||
|
||||
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
|
||||
{
|
||||
OSStatus err = AudioOutputUnitStart(self->audioUnit);
|
||||
@@ -724,27 +707,26 @@ static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
|
||||
|
||||
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
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 = self->format.mChannelsPerFrame;
|
||||
list->mBuffers[0].mDataByteSize = samples * self->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(self->audioConverter,
|
||||
ALCcoreAudioCapture_ConvertCallback, self, &frameCount, list, NULL
|
||||
ALCcoreAudioCapture_ConvertCallback, self, &frameCount, &audiobuf.list, NULL
|
||||
);
|
||||
if(err != noErr)
|
||||
{
|
||||
|
||||
+72
-54
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
#include "alstring.h"
|
||||
@@ -184,16 +185,15 @@ 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);
|
||||
@@ -211,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +268,7 @@ 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;
|
||||
}
|
||||
@@ -248,7 +277,8 @@ FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
|
||||
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);
|
||||
@@ -263,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;
|
||||
}
|
||||
@@ -311,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;
|
||||
}
|
||||
@@ -386,24 +416,6 @@ static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *de
|
||||
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;
|
||||
@@ -626,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;
|
||||
|
||||
@@ -637,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);
|
||||
@@ -660,9 +670,8 @@ typedef struct ALCdsoundCapture {
|
||||
} 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);
|
||||
@@ -679,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -824,8 +856,8 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1,
|
||||
InputType.Format.nBlockAlign);
|
||||
self->Ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates,
|
||||
InputType.Format.nBlockAlign, false);
|
||||
if(self->Ring == NULL)
|
||||
hr = DSERR_OUTOFMEMORY;
|
||||
}
|
||||
@@ -854,22 +886,6 @@ static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *devi
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCdsoundCapture_close(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;
|
||||
}
|
||||
|
||||
IDirectSoundCapture_Release(self->DSC);
|
||||
self->DSC = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
|
||||
{
|
||||
HRESULT hr;
|
||||
@@ -878,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;
|
||||
}
|
||||
|
||||
@@ -893,7 +910,8 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,7 +930,7 @@ 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, device->AmbiOrder);
|
||||
@@ -943,11 +961,11 @@ 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 ll_ringbuffer_read_space(self->Ring);
|
||||
return (ALCuint)ll_ringbuffer_read_space(self->Ring);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+24
-57
@@ -26,6 +26,8 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
@@ -148,9 +150,9 @@ typedef struct ALCjackPlayback {
|
||||
jack_port_t *Port[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
ll_ringbuffer_t *Ring;
|
||||
alcnd_t Cond;
|
||||
alsem_t Sem;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCjackPlayback;
|
||||
|
||||
@@ -162,7 +164,6 @@ static int ALCjackPlayback_mixerProc(void *arg);
|
||||
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
|
||||
static void ALCjackPlayback_Destruct(ALCjackPlayback *self);
|
||||
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
|
||||
static void ALCjackPlayback_close(ALCjackPlayback *self);
|
||||
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
|
||||
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
|
||||
static void ALCjackPlayback_stop(ALCjackPlayback *self);
|
||||
@@ -183,14 +184,14 @@ static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
|
||||
|
||||
alcnd_init(&self->Cond);
|
||||
alsem_init(&self->Sem, 0);
|
||||
|
||||
self->Client = NULL;
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
self->Port[i] = NULL;
|
||||
self->Ring = NULL;
|
||||
|
||||
self->killNow = 1;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
|
||||
@@ -209,7 +210,7 @@ static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
|
||||
self->Client = NULL;
|
||||
}
|
||||
|
||||
alcnd_destroy(&self->Cond);
|
||||
alsem_destroy(&self->Sem);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
@@ -228,19 +229,19 @@ static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
|
||||
bufsize = device->UpdateSize;
|
||||
if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
|
||||
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
|
||||
bufsize += device->UpdateSize;
|
||||
device->NumUpdates = bufsize / device->UpdateSize;
|
||||
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)
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
|
||||
true
|
||||
);
|
||||
if(!self->Ring)
|
||||
{
|
||||
ERR("Failed to reallocate ringbuffer\n");
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to reallocate %u-sample buffer", bufsize);
|
||||
}
|
||||
ALCjackPlayback_unlock(self);
|
||||
return 0;
|
||||
@@ -286,7 +287,7 @@ static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
|
||||
}
|
||||
|
||||
ll_ringbuffer_read_advance(self->Ring, total);
|
||||
alcnd_signal(&self->Cond);
|
||||
alsem_post(&self->Sem);
|
||||
|
||||
if(numframes > total)
|
||||
{
|
||||
@@ -311,27 +312,16 @@ static int ALCjackPlayback_mixerProc(void *arg)
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
ALCjackPlayback_lock(self);
|
||||
while(!self->killNow && device->Connected)
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
ALuint todo, len1, len2;
|
||||
|
||||
/* NOTE: Unfortunately, there is an unavoidable race condition here.
|
||||
* It's possible for the process() method to run, updating the read
|
||||
* pointer and signaling the condition variable, in between the mixer
|
||||
* loop checking the write size and waiting for the condition variable.
|
||||
* This will cause the mixer loop to wait until the *next* process()
|
||||
* invocation, most likely writing silence for it.
|
||||
*
|
||||
* However, this should only happen if the mixer is running behind
|
||||
* anyway (as ideally we'll be asleep in alcnd_wait by the time the
|
||||
* process() method is invoked), so this behavior is not unwarranted.
|
||||
* It's unfortunate since it'll be wasting time sleeping that could be
|
||||
* used to catch up, but there's no way around it without blocking in
|
||||
* the process() method.
|
||||
*/
|
||||
if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
|
||||
{
|
||||
alcnd_wait(&self->Cond, &STATIC_CAST(ALCbackend,self)->mMutex);
|
||||
ALCjackPlayback_unlock(self);
|
||||
alsem_wait(&self->Sem);
|
||||
ALCjackPlayback_lock(self);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -386,20 +376,6 @@ static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCjackPlayback_close(ALCjackPlayback *self)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
if(self->Port[i])
|
||||
jack_port_unregister(self->Client, self->Port[i]);
|
||||
self->Port[i] = NULL;
|
||||
}
|
||||
jack_client_close(self->Client);
|
||||
self->Client = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
@@ -414,9 +390,7 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
|
||||
}
|
||||
|
||||
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
|
||||
* ready for when requested. Note that one period's worth of audio in the
|
||||
* ring buffer will always be left unfilled because one element of the ring
|
||||
* buffer will not be writeable, and we only write in period-sized chunks.
|
||||
* ready for when requested.
|
||||
*/
|
||||
device->Frequency = jack_get_sample_rate(self->Client);
|
||||
device->UpdateSize = jack_get_buffer_size(self->Client);
|
||||
@@ -425,8 +399,7 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
|
||||
bufsize = device->UpdateSize;
|
||||
if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
|
||||
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
|
||||
bufsize += device->UpdateSize;
|
||||
device->NumUpdates = bufsize / device->UpdateSize;
|
||||
device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize;
|
||||
|
||||
/* Force 32-bit float output. */
|
||||
device->FmtType = DevFmtFloat;
|
||||
@@ -461,7 +434,8 @@ static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = ll_ringbuffer_create(bufsize,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
|
||||
true
|
||||
);
|
||||
if(!self->Ring)
|
||||
{
|
||||
@@ -504,7 +478,7 @@ static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
|
||||
}
|
||||
jack_free(ports);
|
||||
|
||||
self->killNow = 0;
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success)
|
||||
{
|
||||
jack_deactivate(self->Client);
|
||||
@@ -518,17 +492,10 @@ static void ALCjackPlayback_stop(ALCjackPlayback *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(self->killNow)
|
||||
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
/* Lock the backend to ensure we don't flag the mixer to die and signal the
|
||||
* mixer to wake up in between it checking the flag and going to sleep and
|
||||
* wait for a wakeup (potentially leading to it never waking back up to see
|
||||
* the flag). */
|
||||
ALCjackPlayback_lock(self);
|
||||
ALCjackPlayback_unlock(self);
|
||||
alcnd_signal(&self->Cond);
|
||||
alsem_post(&self->Sem);
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
jack_deactivate(self->Client);
|
||||
|
||||
@@ -35,7 +35,6 @@ 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);
|
||||
@@ -63,10 +62,6 @@ static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCloopback_close(ALCloopback* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
static ALCboolean ALCloopback_reset(ALCloopback *self)
|
||||
{
|
||||
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
|
||||
|
||||
+7
-11
@@ -36,7 +36,7 @@
|
||||
typedef struct ALCnullBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} ALCnullBackend;
|
||||
|
||||
@@ -45,7 +45,6 @@ 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);
|
||||
@@ -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)
|
||||
{
|
||||
@@ -135,10 +137,6 @@ static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
|
||||
{
|
||||
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
|
||||
@@ -147,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;
|
||||
@@ -157,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);
|
||||
}
|
||||
|
||||
|
||||
+27
-88
@@ -26,8 +26,9 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
@@ -145,7 +146,7 @@ typedef struct ALCopenslPlayback {
|
||||
SLObjectItf mBufferQueueObj;
|
||||
|
||||
ll_ringbuffer_t *mRing;
|
||||
alcnd_t mCond;
|
||||
alsem_t mSem;
|
||||
|
||||
ALsizei mFrameSize;
|
||||
|
||||
@@ -159,7 +160,6 @@ static int ALCopenslPlayback_mixerProc(void *arg);
|
||||
static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *device);
|
||||
static void ALCopenslPlayback_Destruct(ALCopenslPlayback *self);
|
||||
static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *name);
|
||||
static void ALCopenslPlayback_close(ALCopenslPlayback *self);
|
||||
static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self);
|
||||
static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self);
|
||||
static void ALCopenslPlayback_stop(ALCopenslPlayback *self);
|
||||
@@ -184,7 +184,7 @@ static void ALCopenslPlayback_Construct(ALCopenslPlayback *self, ALCdevice *devi
|
||||
self->mBufferQueueObj = NULL;
|
||||
|
||||
self->mRing = NULL;
|
||||
alcnd_init(&self->mCond);
|
||||
alsem_init(&self->mSem, 0);
|
||||
|
||||
self->mFrameSize = 0;
|
||||
|
||||
@@ -197,19 +197,16 @@ static void ALCopenslPlayback_Destruct(ALCopenslPlayback* self)
|
||||
VCALL0(self->mBufferQueueObj,Destroy)();
|
||||
self->mBufferQueueObj = NULL;
|
||||
|
||||
if(self->mOutputMix != NULL)
|
||||
if(self->mOutputMix)
|
||||
VCALL0(self->mOutputMix,Destroy)();
|
||||
self->mOutputMix = NULL;
|
||||
|
||||
if(self->mEngineObj != NULL)
|
||||
if(self->mEngineObj)
|
||||
VCALL0(self->mEngineObj,Destroy)();
|
||||
self->mEngineObj = NULL;
|
||||
self->mEngine = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
alcnd_destroy(&self->mCond);
|
||||
alsem_destroy(&self->mSem);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
@@ -230,7 +227,7 @@ static void ALCopenslPlayback_process(SLAndroidSimpleBufferQueueItf UNUSED(bq),
|
||||
*/
|
||||
ll_ringbuffer_read_advance(self->mRing, 1);
|
||||
|
||||
alcnd_signal(&self->mCond);
|
||||
alsem_post(&self->mSem);
|
||||
}
|
||||
|
||||
|
||||
@@ -242,7 +239,6 @@ static int ALCopenslPlayback_mixerProc(void *arg)
|
||||
ll_ringbuffer_data_t data[2];
|
||||
SLPlayItf player;
|
||||
SLresult result;
|
||||
size_t padding;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
@@ -258,22 +254,18 @@ static int ALCopenslPlayback_mixerProc(void *arg)
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
ALCopenslPlayback_lock(self);
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to get playback buffer: 0x%08x", result);
|
||||
ALCopenslPlayback_unlock(self);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* NOTE: The ringbuffer will be larger than the desired buffer metrics.
|
||||
* Calculate the amount of extra space so we know how much to keep unused.
|
||||
*/
|
||||
padding = ll_ringbuffer_write_space(self->mRing) - device->NumUpdates;
|
||||
|
||||
ALCopenslPlayback_lock(self);
|
||||
while(ATOMIC_LOAD_SEQ(&self->mKillNow) == AL_FALSE && device->Connected)
|
||||
while(!ATOMIC_LOAD(&self->mKillNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
size_t todo, len0, len1;
|
||||
|
||||
if(ll_ringbuffer_write_space(self->mRing) <= padding)
|
||||
if(ll_ringbuffer_write_space(self->mRing) == 0)
|
||||
{
|
||||
SLuint32 state = 0;
|
||||
|
||||
@@ -286,34 +278,21 @@ static int ALCopenslPlayback_mixerProc(void *arg)
|
||||
}
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to start platback: 0x%08x", result);
|
||||
break;
|
||||
}
|
||||
|
||||
/* NOTE: Unfortunately, there is an unavoidable race condition
|
||||
* here. It's possible for the process() method to run, updating
|
||||
* the read pointer and signaling the condition variable, in
|
||||
* between checking the write size and waiting for the condition
|
||||
* variable here. This will cause alcnd_wait to wait until the
|
||||
* *next* process() invocation signals the condition variable
|
||||
* again.
|
||||
*
|
||||
* However, this should only happen if the mixer is running behind
|
||||
* anyway (as ideally we'll be asleep in alcnd_wait by the time the
|
||||
* process() method is invoked), so this behavior is not completely
|
||||
* unwarranted. It's unfortunate since it'll be wasting time
|
||||
* sleeping that could be used to catch up, but there's no way
|
||||
* around it without blocking in the process() method.
|
||||
*/
|
||||
if(ll_ringbuffer_write_space(self->mRing) <= padding)
|
||||
if(ll_ringbuffer_write_space(self->mRing) == 0)
|
||||
{
|
||||
alcnd_wait(&self->mCond, &STATIC_CAST(ALCbackend,self)->mMutex);
|
||||
ALCopenslPlayback_unlock(self);
|
||||
alsem_wait(&self->mSem);
|
||||
ALCopenslPlayback_lock(self);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ll_ringbuffer_get_write_vector(self->mRing, data);
|
||||
todo = data[0].len+data[1].len - padding;
|
||||
todo = data[0].len+data[1].len;
|
||||
|
||||
len0 = minu(todo, data[0].len);
|
||||
len1 = minu(todo-len0, data[1].len);
|
||||
@@ -402,20 +381,6 @@ static ALCenum ALCopenslPlayback_open(ALCopenslPlayback *self, const ALCchar *na
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCopenslPlayback_close(ALCopenslPlayback *self)
|
||||
{
|
||||
if(self->mBufferQueueObj != NULL)
|
||||
VCALL0(self->mBufferQueueObj,Destroy)();
|
||||
self->mBufferQueueObj = NULL;
|
||||
|
||||
VCALL0(self->mOutputMix,Destroy)();
|
||||
self->mOutputMix = NULL;
|
||||
|
||||
VCALL0(self->mEngineObj,Destroy)();
|
||||
self->mEngineObj = NULL;
|
||||
self->mEngine = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCopenslPlayback_reset(ALCopenslPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
@@ -601,12 +566,8 @@ static ALCboolean ALCopenslPlayback_start(ALCopenslPlayback *self)
|
||||
SLresult result;
|
||||
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
/* NOTE: Add an extra update since one period's worth of audio in the ring
|
||||
* buffer will always be left unfilled because one element of the ring
|
||||
* buffer will not be writeable, and we only write in period-sized chunks.
|
||||
*/
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates + 1,
|
||||
self->mFrameSize*device->UpdateSize);
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates, self->mFrameSize*device->UpdateSize,
|
||||
true);
|
||||
|
||||
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
@@ -640,14 +601,7 @@ static void ALCopenslPlayback_stop(ALCopenslPlayback *self)
|
||||
if(ATOMIC_EXCHANGE_SEQ(&self->mKillNow, AL_TRUE))
|
||||
return;
|
||||
|
||||
/* Lock the backend to ensure we don't flag the mixer to die and signal the
|
||||
* mixer to wake up in between it checking the flag and going to sleep and
|
||||
* wait for a wakeup (potentially leading to it never waking back up to see
|
||||
* the flag).
|
||||
*/
|
||||
ALCopenslPlayback_lock(self);
|
||||
ALCopenslPlayback_unlock(self);
|
||||
alcnd_signal(&self->mCond);
|
||||
alsem_post(&self->mSem);
|
||||
althrd_join(self->mThread, &res);
|
||||
|
||||
result = VCALL(self->mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
|
||||
@@ -721,7 +675,6 @@ static void ALCopenslCapture_process(SLAndroidSimpleBufferQueueItf bq, void *con
|
||||
static void ALCopenslCapture_Construct(ALCopenslCapture *self, ALCdevice *device);
|
||||
static void ALCopenslCapture_Destruct(ALCopenslCapture *self);
|
||||
static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name);
|
||||
static void ALCopenslCapture_close(ALCopenslCapture *self);
|
||||
static DECLARE_FORWARD(ALCopenslCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self);
|
||||
static void ALCopenslCapture_stop(ALCopenslCapture *self);
|
||||
@@ -890,8 +843,8 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
|
||||
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates + 1,
|
||||
device->UpdateSize * self->mFrameSize);
|
||||
self->mRing = ll_ringbuffer_create(device->NumUpdates, device->UpdateSize*self->mFrameSize,
|
||||
false);
|
||||
|
||||
result = VCALL(self->mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
&bufferQueue);
|
||||
@@ -940,21 +893,6 @@ static ALCenum ALCopenslCapture_open(ALCopenslCapture *self, const ALCchar *name
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCopenslCapture_close(ALCopenslCapture *self)
|
||||
{
|
||||
ll_ringbuffer_free(self->mRing);
|
||||
self->mRing = NULL;
|
||||
|
||||
if(self->mRecordObj != NULL)
|
||||
VCALL0(self->mRecordObj,Destroy)();
|
||||
self->mRecordObj = NULL;
|
||||
|
||||
if(self->mEngineObj != NULL)
|
||||
VCALL0(self->mEngineObj,Destroy)();
|
||||
self->mEngineObj = NULL;
|
||||
self->mEngine = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self)
|
||||
{
|
||||
SLRecordItf record;
|
||||
@@ -972,7 +910,8 @@ static ALCboolean ALCopenslCapture_start(ALCopenslCapture *self)
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
ALCopenslCapture_lock(self);
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice,
|
||||
"Failed to start capture: 0x%08x", result);
|
||||
ALCopenslCapture_unlock(self);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
@@ -1055,7 +994,7 @@ static ALCenum ALCopenslCapture_captureSamples(ALCopenslCapture *self, ALCvoid *
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
ALCopenslCapture_lock(self);
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to update capture buffer: 0x%08x", result);
|
||||
ALCopenslCapture_unlock(self);
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
@@ -1100,7 +1039,7 @@ static void ALCopenslBackendFactory_probe(ALCopenslBackendFactory* UNUSED(self),
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendAllDevicesList(opensl_device);
|
||||
AppendCaptureDeviceList(opensl_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+35
-25
@@ -35,6 +35,8 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
@@ -250,9 +252,8 @@ typedef struct 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);
|
||||
@@ -283,7 +284,8 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
ALCplaybackOSS_lock(self);
|
||||
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(self->fd, &wfds);
|
||||
@@ -298,7 +300,7 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
ERR("select failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
else if(sret == 0)
|
||||
@@ -318,7 +320,8 @@ static int ALCplaybackOSS_mixerProc(void *ptr)
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed writing playback samples: %s",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -337,9 +340,19 @@ 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;
|
||||
@@ -379,12 +392,6 @@ static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *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;
|
||||
@@ -517,9 +524,8 @@ typedef struct 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);
|
||||
@@ -562,7 +568,7 @@ static int ALCcaptureOSS_recordProc(void *ptr)
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
ERR("select failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to check capture samples: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
else if(sret == 0)
|
||||
@@ -579,7 +585,7 @@ static int ALCcaptureOSS_recordProc(void *ptr)
|
||||
{
|
||||
ERR("read failed: %s\n", strerror(errno));
|
||||
ALCcaptureOSS_lock(self);
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed reading capture samples: %s", strerror(errno));
|
||||
ALCcaptureOSS_unlock(self);
|
||||
break;
|
||||
}
|
||||
@@ -596,9 +602,22 @@ 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;
|
||||
@@ -710,7 +729,7 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates + 1, frameSize);
|
||||
self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, frameSize, false);
|
||||
if(!self->ring)
|
||||
{
|
||||
ERR("Ring buffer create failed\n");
|
||||
@@ -724,15 +743,6 @@ static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCcaptureOSS_close(ALCcaptureOSS *self)
|
||||
{
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
|
||||
{
|
||||
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
|
||||
|
||||
+11
-28
@@ -26,6 +26,8 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
@@ -139,7 +141,6 @@ static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBu
|
||||
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
|
||||
static void ALCportPlayback_Destruct(ALCportPlayback *self);
|
||||
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
|
||||
static void ALCportPlayback_close(ALCportPlayback *self);
|
||||
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
|
||||
static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
|
||||
static void ALCportPlayback_stop(ALCportPlayback *self);
|
||||
@@ -163,8 +164,9 @@ static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
|
||||
|
||||
static void ALCportPlayback_Destruct(ALCportPlayback *self)
|
||||
{
|
||||
if(self->stream)
|
||||
Pa_CloseStream(self->stream);
|
||||
PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
@@ -251,14 +253,6 @@ retry_open:
|
||||
|
||||
}
|
||||
|
||||
static void ALCportPlayback_close(ALCportPlayback *self)
|
||||
{
|
||||
PaError err = Pa_CloseStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
@@ -336,7 +330,6 @@ static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuff
|
||||
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
|
||||
static void ALCportCapture_Destruct(ALCportCapture *self);
|
||||
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
|
||||
static void ALCportCapture_close(ALCportCapture *self);
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCportCapture_start(ALCportCapture *self);
|
||||
static void ALCportCapture_stop(ALCportCapture *self);
|
||||
@@ -356,16 +349,17 @@ static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
|
||||
SET_VTABLE2(ALCportCapture, ALCbackend, self);
|
||||
|
||||
self->stream = NULL;
|
||||
self->ring = NULL;
|
||||
}
|
||||
|
||||
static void ALCportCapture_Destruct(ALCportCapture *self)
|
||||
{
|
||||
if(self->stream)
|
||||
Pa_CloseStream(self->stream);
|
||||
PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
|
||||
if(self->ring)
|
||||
ll_ringbuffer_free(self->ring);
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
@@ -401,7 +395,7 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
|
||||
samples = maxu(samples, 100 * device->Frequency / 1000);
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
self->ring = ll_ringbuffer_create(samples, frame_size);
|
||||
self->ring = ll_ringbuffer_create(samples, frame_size, false);
|
||||
if(self->ring == NULL) return ALC_INVALID_VALUE;
|
||||
|
||||
self->params.device = -1;
|
||||
@@ -450,17 +444,6 @@ static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCportCapture_close(ALCportCapture *self)
|
||||
{
|
||||
PaError err = Pa_CloseStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCportCapture_start(ALCportCapture *self)
|
||||
{
|
||||
|
||||
+65
-71
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
@@ -333,18 +334,20 @@ static void wait_for_operation(pa_operation *op, pa_threaded_mainloop *loop)
|
||||
static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
|
||||
{
|
||||
const char *name = "OpenAL Soft";
|
||||
char path_name[PATH_MAX];
|
||||
al_string binname = AL_STRING_INIT_STATIC();
|
||||
pa_context_state_t state;
|
||||
pa_context *context;
|
||||
int err;
|
||||
|
||||
if(pa_get_binary_name(path_name, sizeof(path_name)))
|
||||
name = pa_path_get_filename(path_name);
|
||||
GetProcBinary(NULL, &binname);
|
||||
if(!alstr_empty(binname))
|
||||
name = alstr_get_cstr(binname);
|
||||
|
||||
context = pa_context_new(pa_threaded_mainloop_get_api(loop), name);
|
||||
if(!context)
|
||||
{
|
||||
ERR("pa_context_new() failed\n");
|
||||
alstr_reset(&binname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -371,9 +374,10 @@ static pa_context *connect_context(pa_threaded_mainloop *loop, ALboolean silent)
|
||||
if(!silent)
|
||||
ERR("Context did not connect: %s\n", pa_strerror(err));
|
||||
pa_context_unref(context);
|
||||
return NULL;
|
||||
context = NULL;
|
||||
}
|
||||
|
||||
alstr_reset(&binname);
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -468,7 +472,7 @@ typedef struct ALCpulsePlayback {
|
||||
pa_stream *stream;
|
||||
pa_context *context;
|
||||
|
||||
volatile ALboolean killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCpulsePlayback;
|
||||
|
||||
@@ -491,7 +495,6 @@ static int ALCpulsePlayback_mixerProc(void *ptr);
|
||||
static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device);
|
||||
static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self);
|
||||
static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name);
|
||||
static void ALCpulsePlayback_close(ALCpulsePlayback *self);
|
||||
static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self);
|
||||
static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self);
|
||||
static void ALCpulsePlayback_stop(ALCpulsePlayback *self);
|
||||
@@ -510,11 +513,20 @@ static void ALCpulsePlayback_Construct(ALCpulsePlayback *self, ALCdevice *device
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCpulsePlayback, ALCbackend, self);
|
||||
|
||||
self->loop = NULL;
|
||||
AL_STRING_INIT(self->device_name);
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCpulsePlayback_Destruct(ALCpulsePlayback *self)
|
||||
{
|
||||
if(self->loop)
|
||||
{
|
||||
pulse_close(self->loop, self->context, self->stream);
|
||||
self->loop = NULL;
|
||||
self->context = NULL;
|
||||
self->stream = NULL;
|
||||
}
|
||||
AL_STRING_DEINIT(self->device_name);
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
@@ -639,7 +651,7 @@ static void ALCpulsePlayback_contextStateCallback(pa_context *context, void *pda
|
||||
if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
|
||||
{
|
||||
ERR("Received context failure!\n");
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Playback state failure");
|
||||
}
|
||||
pa_threaded_mainloop_signal(self->loop, 0);
|
||||
}
|
||||
@@ -650,7 +662,7 @@ static void ALCpulsePlayback_streamStateCallback(pa_stream *stream, void *pdata)
|
||||
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
|
||||
{
|
||||
ERR("Received stream failure!\n");
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Playback stream failure");
|
||||
}
|
||||
pa_threaded_mainloop_signal(self->loop, 0);
|
||||
}
|
||||
@@ -818,13 +830,17 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
frame_size = pa_frame_size(&self->spec);
|
||||
|
||||
while(!self->killNow && device->Connected)
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
void *buf;
|
||||
int ret;
|
||||
|
||||
len = pa_stream_writable_size(self->stream);
|
||||
if(len < 0)
|
||||
{
|
||||
ERR("Failed to get writable size: %ld", (long)len);
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to get writable size: %ld", (long)len);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -850,31 +866,16 @@ static int ALCpulsePlayback_mixerProc(void *ptr)
|
||||
pa_threaded_mainloop_wait(self->loop);
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= len%self->attr.minreq;
|
||||
len -= len%frame_size;
|
||||
|
||||
while(len > 0)
|
||||
{
|
||||
size_t newlen = len;
|
||||
int ret;
|
||||
void *buf;
|
||||
pa_free_cb_t free_func = NULL;
|
||||
buf = pa_xmalloc(len);
|
||||
|
||||
if(pa_stream_begin_write(self->stream, &buf, &newlen) < 0)
|
||||
{
|
||||
buf = pa_xmalloc(newlen);
|
||||
free_func = pa_xfree;
|
||||
}
|
||||
aluMixData(device, buf, len/frame_size);
|
||||
|
||||
aluMixData(device, buf, newlen/frame_size);
|
||||
|
||||
ret = pa_stream_write(self->stream, buf, newlen, free_func, 0, PA_SEEK_RELATIVE);
|
||||
if(ret != PA_OK)
|
||||
{
|
||||
ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
|
||||
break;
|
||||
}
|
||||
len -= newlen;
|
||||
}
|
||||
ret = pa_stream_write(self->stream, buf, len, pa_xfree, 0, PA_SEEK_RELATIVE);
|
||||
if(ret != PA_OK) ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret));
|
||||
}
|
||||
pa_threaded_mainloop_unlock(self->loop);
|
||||
|
||||
@@ -952,16 +953,6 @@ static ALCenum ALCpulsePlayback_open(ALCpulsePlayback *self, const ALCchar *name
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCpulsePlayback_close(ALCpulsePlayback *self)
|
||||
{
|
||||
pulse_close(self->loop, self->context, self->stream);
|
||||
self->loop = NULL;
|
||||
self->context = NULL;
|
||||
self->stream = NULL;
|
||||
|
||||
alstr_clear(&self->device_name);
|
||||
}
|
||||
|
||||
static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
@@ -1138,7 +1129,7 @@ static ALCboolean ALCpulsePlayback_reset(ALCpulsePlayback *self)
|
||||
|
||||
static ALCboolean ALCpulsePlayback_start(ALCpulsePlayback *self)
|
||||
{
|
||||
self->killNow = AL_FALSE;
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCpulsePlayback_mixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
return ALC_TRUE;
|
||||
@@ -1149,10 +1140,9 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
|
||||
pa_operation *o;
|
||||
int res;
|
||||
|
||||
if(!self->stream || self->killNow)
|
||||
if(!self->stream || ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
self->killNow = AL_TRUE;
|
||||
/* Signal the main loop in case PulseAudio isn't sending us audio requests
|
||||
* (e.g. if the device is suspended). We need to lock the mainloop in case
|
||||
* the mixer is between checking the killNow flag but before waiting for
|
||||
@@ -1174,13 +1164,16 @@ static void ALCpulsePlayback_stop(ALCpulsePlayback *self)
|
||||
|
||||
static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self)
|
||||
{
|
||||
pa_usec_t latency = 0;
|
||||
ClockLatency ret;
|
||||
pa_usec_t latency;
|
||||
int neg, err;
|
||||
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
|
||||
if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
|
||||
err = pa_stream_get_latency(self->stream, &latency, &neg);
|
||||
pa_threaded_mainloop_unlock(self->loop);
|
||||
|
||||
if(UNLIKELY(err != 0))
|
||||
{
|
||||
/* FIXME: if err = -PA_ERR_NODATA, it means we were called too soon
|
||||
* after starting the stream and no timing info has been received from
|
||||
@@ -1191,9 +1184,9 @@ static ClockLatency ALCpulsePlayback_getClockLatency(ALCpulsePlayback *self)
|
||||
latency = 0;
|
||||
neg = 0;
|
||||
}
|
||||
if(neg) latency = 0;
|
||||
ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000;
|
||||
pa_threaded_mainloop_unlock(self->loop);
|
||||
else if(UNLIKELY(neg))
|
||||
latency = 0;
|
||||
ret.Latency = (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1245,7 +1238,6 @@ static pa_stream *ALCpulseCapture_connectStream(const char *device_name,
|
||||
static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device);
|
||||
static void ALCpulseCapture_Destruct(ALCpulseCapture *self);
|
||||
static ALCenum ALCpulseCapture_open(ALCpulseCapture *self, const ALCchar *name);
|
||||
static void ALCpulseCapture_close(ALCpulseCapture *self);
|
||||
static DECLARE_FORWARD(ALCpulseCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self);
|
||||
static void ALCpulseCapture_stop(ALCpulseCapture *self);
|
||||
@@ -1264,11 +1256,19 @@ static void ALCpulseCapture_Construct(ALCpulseCapture *self, ALCdevice *device)
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCpulseCapture, ALCbackend, self);
|
||||
|
||||
self->loop = NULL;
|
||||
AL_STRING_INIT(self->device_name);
|
||||
}
|
||||
|
||||
static void ALCpulseCapture_Destruct(ALCpulseCapture *self)
|
||||
{
|
||||
if(self->loop)
|
||||
{
|
||||
pulse_close(self->loop, self->context, self->stream);
|
||||
self->loop = NULL;
|
||||
self->context = NULL;
|
||||
self->stream = NULL;
|
||||
}
|
||||
AL_STRING_DEINIT(self->device_name);
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
@@ -1380,7 +1380,7 @@ static void ALCpulseCapture_contextStateCallback(pa_context *context, void *pdat
|
||||
if(pa_context_get_state(context) == PA_CONTEXT_FAILED)
|
||||
{
|
||||
ERR("Received context failure!\n");
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Capture state failure");
|
||||
}
|
||||
pa_threaded_mainloop_signal(self->loop, 0);
|
||||
}
|
||||
@@ -1391,7 +1391,7 @@ static void ALCpulseCapture_streamStateCallback(pa_stream *stream, void *pdata)
|
||||
if(pa_stream_get_state(stream) == PA_STREAM_FAILED)
|
||||
{
|
||||
ERR("Received stream failure!\n");
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice);
|
||||
aluHandleDisconnect(STATIC_CAST(ALCbackend,self)->mDevice, "Capture stream failure");
|
||||
}
|
||||
pa_threaded_mainloop_signal(self->loop, 0);
|
||||
}
|
||||
@@ -1615,16 +1615,6 @@ fail:
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void ALCpulseCapture_close(ALCpulseCapture *self)
|
||||
{
|
||||
pulse_close(self->loop, self->context, self->stream);
|
||||
self->loop = NULL;
|
||||
self->context = NULL;
|
||||
self->stream = NULL;
|
||||
|
||||
alstr_clear(&self->device_name);
|
||||
}
|
||||
|
||||
static ALCboolean ALCpulseCapture_start(ALCpulseCapture *self)
|
||||
{
|
||||
pa_operation *o;
|
||||
@@ -1664,14 +1654,15 @@ static ALCenum ALCpulseCapture_captureSamples(ALCpulseCapture *self, ALCvoid *bu
|
||||
state = pa_stream_get_state(self->stream);
|
||||
if(!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Bad capture state: %u", state);
|
||||
break;
|
||||
}
|
||||
if(pa_stream_peek(self->stream, &self->cap_store, &self->cap_len) < 0)
|
||||
{
|
||||
ERR("pa_stream_peek() failed: %s\n",
|
||||
pa_strerror(pa_context_errno(self->context)));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed retrieving capture samples: %s",
|
||||
pa_strerror(pa_context_errno(self->context)));
|
||||
break;
|
||||
}
|
||||
self->cap_remain = self->cap_len;
|
||||
@@ -1704,7 +1695,7 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
size_t readable = self->cap_remain;
|
||||
|
||||
if(device->Connected)
|
||||
if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
ssize_t got;
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
@@ -1712,7 +1703,7 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
|
||||
if(got < 0)
|
||||
{
|
||||
ERR("pa_stream_readable_size() failed: %s\n", pa_strerror(got));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed getting readable size: %s", pa_strerror(got));
|
||||
}
|
||||
else if((size_t)got > self->cap_len)
|
||||
readable += got - self->cap_len;
|
||||
@@ -1727,21 +1718,24 @@ static ALCuint ALCpulseCapture_availableSamples(ALCpulseCapture *self)
|
||||
|
||||
static ClockLatency ALCpulseCapture_getClockLatency(ALCpulseCapture *self)
|
||||
{
|
||||
pa_usec_t latency = 0;
|
||||
ClockLatency ret;
|
||||
pa_usec_t latency;
|
||||
int neg, err;
|
||||
|
||||
pa_threaded_mainloop_lock(self->loop);
|
||||
ret.ClockTime = GetDeviceClockTime(STATIC_CAST(ALCbackend,self)->mDevice);
|
||||
if((err=pa_stream_get_latency(self->stream, &latency, &neg)) != 0)
|
||||
err = pa_stream_get_latency(self->stream, &latency, &neg);
|
||||
pa_threaded_mainloop_unlock(self->loop);
|
||||
|
||||
if(UNLIKELY(err != 0))
|
||||
{
|
||||
ERR("Failed to get stream latency: 0x%x\n", err);
|
||||
latency = 0;
|
||||
neg = 0;
|
||||
}
|
||||
if(neg) latency = 0;
|
||||
ret.Latency = minu64(latency, U64(0xffffffffffffffff)/1000) * 1000;
|
||||
pa_threaded_mainloop_unlock(self->loop);
|
||||
else if(UNLIKELY(neg))
|
||||
latency = 0;
|
||||
ret.Latency = (ALint64)minu64(latency, U64(0x7fffffffffffffff)/1000) * 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+30
-26
@@ -46,7 +46,7 @@ typedef struct {
|
||||
ALvoid* buffer;
|
||||
ALsizei size;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} qsa_data;
|
||||
|
||||
@@ -166,9 +166,8 @@ typedef struct PlaybackWrapper {
|
||||
} PlaybackWrapper;
|
||||
|
||||
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
|
||||
static void PlaybackWrapper_Destruct(PlaybackWrapper *self);
|
||||
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);
|
||||
@@ -207,7 +206,7 @@ FORCE_ALIGN static int qsa_proc_playback(void *ptr)
|
||||
);
|
||||
|
||||
V0(device->Backend,lock)();
|
||||
while(!data->killNow)
|
||||
while(!ATOMIC_LOAD(&data->killNow, almemory_order_acquire))
|
||||
{
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(data->audio_fd, &wfds);
|
||||
@@ -221,7 +220,7 @@ FORCE_ALIGN static int qsa_proc_playback(void *ptr)
|
||||
if(sret == -1)
|
||||
{
|
||||
ERR("select error: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed waiting for playback buffer: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
if(sret == 0)
|
||||
@@ -233,7 +232,7 @@ FORCE_ALIGN static int qsa_proc_playback(void *ptr)
|
||||
len = data->size;
|
||||
write_ptr = data->buffer;
|
||||
aluMixData(device, write_ptr, len/frame_size);
|
||||
while(len>0 && !data->killNow)
|
||||
while(len>0 && !ATOMIC_LOAD(&data->killNow, almemory_order_acquire))
|
||||
{
|
||||
int wrote = snd_pcm_plugin_write(data->pcmHandle, write_ptr, len);
|
||||
if(wrote <= 0)
|
||||
@@ -252,7 +251,7 @@ FORCE_ALIGN static int qsa_proc_playback(void *ptr)
|
||||
{
|
||||
if(snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK) < 0)
|
||||
{
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Playback recovery failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -283,6 +282,7 @@ static ALCenum qsa_open_playback(PlaybackWrapper *self, const ALCchar* deviceNam
|
||||
data = (qsa_data*)calloc(1, sizeof(qsa_data));
|
||||
if(data == NULL)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
ATOMIC_INIT(&data->killNow, AL_TRUE);
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = qsaDevice;
|
||||
@@ -596,7 +596,7 @@ static ALCboolean qsa_start_playback(PlaybackWrapper *self)
|
||||
{
|
||||
qsa_data *data = self->ExtraData;
|
||||
|
||||
data->killNow = 0;
|
||||
ATOMIC_STORE(&data->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&data->thread, qsa_proc_playback, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
|
||||
@@ -608,10 +608,8 @@ static void qsa_stop_playback(PlaybackWrapper *self)
|
||||
qsa_data *data = self->ExtraData;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
if(ATOMIC_EXCHANGE(&data->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
}
|
||||
|
||||
@@ -624,16 +622,19 @@ static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device)
|
||||
self->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_Destruct(PlaybackWrapper *self)
|
||||
{
|
||||
if(self->ExtraData)
|
||||
qsa_close_playback(self);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
|
||||
{
|
||||
return qsa_open_playback(self, name);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_close(PlaybackWrapper *self)
|
||||
{
|
||||
qsa_close_playback(self);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
|
||||
{
|
||||
return qsa_reset_playback(self);
|
||||
@@ -661,9 +662,8 @@ typedef struct CaptureWrapper {
|
||||
} CaptureWrapper;
|
||||
|
||||
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
|
||||
static void CaptureWrapper_Destruct(CaptureWrapper *self);
|
||||
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);
|
||||
@@ -846,7 +846,7 @@ static ALCuint qsa_available_samples(CaptureWrapper *self)
|
||||
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
|
||||
{
|
||||
ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed capture recovery: %s", snd_strerror(rstatus));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -889,7 +889,7 @@ static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuin
|
||||
switch (selectret)
|
||||
{
|
||||
case -1:
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to check capture samples");
|
||||
return ALC_INVALID_DEVICE;
|
||||
case 0:
|
||||
break;
|
||||
@@ -920,7 +920,8 @@ static ALCenum qsa_capture_samples(CaptureWrapper *self, ALCvoid *buffer, ALCuin
|
||||
if ((rstatus=snd_pcm_plugin_prepare(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE))<0)
|
||||
{
|
||||
ERR("capture prepare failed: %s\n", snd_strerror(rstatus));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed capture recovery: %s",
|
||||
snd_strerror(rstatus));
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
snd_pcm_capture_go(data->pcmHandle);
|
||||
@@ -945,16 +946,19 @@ static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device)
|
||||
self->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void CaptureWrapper_Destruct(CaptureWrapper *self)
|
||||
{
|
||||
if(self->ExtraData)
|
||||
qsa_close_capture(self);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
|
||||
{
|
||||
return qsa_open_capture(self, name);
|
||||
}
|
||||
|
||||
static void CaptureWrapper_close(CaptureWrapper *self)
|
||||
{
|
||||
qsa_close_capture(self);
|
||||
}
|
||||
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
|
||||
{
|
||||
qsa_start_capture(self);
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
/**
|
||||
* 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);
|
||||
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)
|
||||
{
|
||||
int num_devices, i;
|
||||
al_string name;
|
||||
|
||||
if(type != ALL_DEVICE_PROBE)
|
||||
return;
|
||||
|
||||
AL_STRING_INIT(name);
|
||||
num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
|
||||
|
||||
AppendAllDevicesList(defaultDeviceName);
|
||||
for(i = 0;i < num_devices;++i)
|
||||
{
|
||||
alstr_copy_cstr(&name, DEVNAME_PREFIX);
|
||||
alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE));
|
||||
AppendAllDevicesList(alstr_get_cstr(name));
|
||||
}
|
||||
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;
|
||||
}
|
||||
+11
-15
@@ -43,7 +43,7 @@ typedef struct ALCsndioBackend {
|
||||
ALvoid *mix_data;
|
||||
ALsizei data_size;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} ALCsndioBackend;
|
||||
|
||||
@@ -52,7 +52,6 @@ static int ALCsndioBackend_mixerProc(void *ptr);
|
||||
static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device);
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self);
|
||||
static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name);
|
||||
static void ALCsndioBackend_close(ALCsndioBackend *self);
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self);
|
||||
static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self);
|
||||
static void ALCsndioBackend_stop(ALCsndioBackend *self);
|
||||
@@ -73,6 +72,10 @@ static void ALCsndioBackend_Construct(ALCsndioBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCsndioBackend, ALCbackend, self);
|
||||
|
||||
self->sndHandle = NULL;
|
||||
self->mix_data = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_Destruct(ALCsndioBackend *self)
|
||||
@@ -100,7 +103,8 @@ static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
while(!self->killNow && device->Connected)
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
ALsizei len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
@@ -108,14 +112,14 @@ static int ALCsndioBackend_mixerProc(void *ptr)
|
||||
ALCsndioBackend_lock(self);
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
ALCsndioBackend_unlock(self);
|
||||
while(len > 0 && !self->killNow)
|
||||
while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -150,12 +154,6 @@ static ALCenum ALCsndioBackend_open(ALCsndioBackend *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCsndioBackend_close(ALCsndioBackend *self)
|
||||
{
|
||||
sio_close(self->sndHandle);
|
||||
self->sndHandle = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsndioBackend_reset(ALCsndioBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
@@ -257,7 +255,7 @@ static ALCboolean ALCsndioBackend_start(ALCsndioBackend *self)
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
self->killNow = 0;
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCsndioBackend_mixerProc, self) != althrd_success)
|
||||
{
|
||||
sio_stop(self->sndHandle);
|
||||
@@ -271,10 +269,8 @@ static void ALCsndioBackend_stop(ALCsndioBackend *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);
|
||||
|
||||
if(!sio_stop(self->sndHandle))
|
||||
|
||||
+7
-10
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
@@ -59,7 +60,6 @@ static int ALCsolarisBackend_mixerProc(void *ptr);
|
||||
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device);
|
||||
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self);
|
||||
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name);
|
||||
static void ALCsolarisBackend_close(ALCsolarisBackend *self);
|
||||
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self);
|
||||
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self);
|
||||
static void ALCsolarisBackend_stop(ALCsolarisBackend *self);
|
||||
@@ -84,6 +84,7 @@ static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *devi
|
||||
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
|
||||
|
||||
self->fd = -1;
|
||||
self->mix_data = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_FALSE);
|
||||
}
|
||||
|
||||
@@ -119,7 +120,8 @@ static int ALCsolarisBackend_mixerProc(void *ptr)
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
ALCsolarisBackend_lock(self);
|
||||
while(!ATOMIC_LOAD_SEQ(&self->killNow) && device->Connected)
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(self->fd, &wfds);
|
||||
@@ -134,7 +136,7 @@ static int ALCsolarisBackend_mixerProc(void *ptr)
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
ERR("select failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to wait for playback buffer: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
else if(sret == 0)
|
||||
@@ -154,7 +156,8 @@ static int ALCsolarisBackend_mixerProc(void *ptr)
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to write playback samples: %s",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -190,12 +193,6 @@ static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *na
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCsolarisBackend_close(ALCsolarisBackend *self)
|
||||
{
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
#include "alstring.h"
|
||||
@@ -70,6 +71,13 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x
|
||||
#define DEVNAME_HEAD "OpenAL Soft on "
|
||||
|
||||
|
||||
/* Scales the given value using 64-bit integer math, ceiling the result. */
|
||||
static inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
|
||||
{
|
||||
return (val*new_scale + old_scale-1) / old_scale;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
al_string name;
|
||||
al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
|
||||
@@ -336,51 +344,51 @@ static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ve
|
||||
|
||||
|
||||
/* Proxy interface used by the message handler. */
|
||||
struct ALCmmdevProxyVtable;
|
||||
struct ALCwasapiProxyVtable;
|
||||
|
||||
typedef struct ALCmmdevProxy {
|
||||
const struct ALCmmdevProxyVtable *vtbl;
|
||||
} ALCmmdevProxy;
|
||||
typedef struct ALCwasapiProxy {
|
||||
const struct ALCwasapiProxyVtable *vtbl;
|
||||
} ALCwasapiProxy;
|
||||
|
||||
struct ALCmmdevProxyVtable {
|
||||
HRESULT (*const openProxy)(ALCmmdevProxy*);
|
||||
void (*const closeProxy)(ALCmmdevProxy*);
|
||||
struct ALCwasapiProxyVtable {
|
||||
HRESULT (*const openProxy)(ALCwasapiProxy*);
|
||||
void (*const closeProxy)(ALCwasapiProxy*);
|
||||
|
||||
HRESULT (*const resetProxy)(ALCmmdevProxy*);
|
||||
HRESULT (*const startProxy)(ALCmmdevProxy*);
|
||||
void (*const stopProxy)(ALCmmdevProxy*);
|
||||
HRESULT (*const resetProxy)(ALCwasapiProxy*);
|
||||
HRESULT (*const startProxy)(ALCwasapiProxy*);
|
||||
void (*const stopProxy)(ALCwasapiProxy*);
|
||||
};
|
||||
|
||||
#define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
|
||||
DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
|
||||
DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
|
||||
DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
|
||||
DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
|
||||
#define DEFINE_ALCWASAPIPROXY_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, openProxy) \
|
||||
DECLARE_THUNK(T, ALCwasapiProxy, void, closeProxy) \
|
||||
DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, resetProxy) \
|
||||
DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, startProxy) \
|
||||
DECLARE_THUNK(T, ALCwasapiProxy, void, stopProxy) \
|
||||
\
|
||||
static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
|
||||
T##_ALCmmdevProxy_openProxy, \
|
||||
T##_ALCmmdevProxy_closeProxy, \
|
||||
T##_ALCmmdevProxy_resetProxy, \
|
||||
T##_ALCmmdevProxy_startProxy, \
|
||||
T##_ALCmmdevProxy_stopProxy, \
|
||||
static const struct ALCwasapiProxyVtable T##_ALCwasapiProxy_vtable = { \
|
||||
T##_ALCwasapiProxy_openProxy, \
|
||||
T##_ALCwasapiProxy_closeProxy, \
|
||||
T##_ALCwasapiProxy_resetProxy, \
|
||||
T##_ALCwasapiProxy_startProxy, \
|
||||
T##_ALCwasapiProxy_stopProxy, \
|
||||
}
|
||||
|
||||
static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
|
||||
static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
|
||||
static void ALCwasapiProxy_Construct(ALCwasapiProxy* UNUSED(self)) { }
|
||||
static void ALCwasapiProxy_Destruct(ALCwasapiProxy* UNUSED(self)) { }
|
||||
|
||||
static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
static DWORD CALLBACK ALCwasapiProxy_messageHandler(void *ptr)
|
||||
{
|
||||
ThreadRequest *req = ptr;
|
||||
IMMDeviceEnumerator *Enumerator;
|
||||
ALuint deviceCount = 0;
|
||||
ALCmmdevProxy *proxy;
|
||||
ALCwasapiProxy *proxy;
|
||||
HRESULT hr, cohr;
|
||||
MSG msg;
|
||||
|
||||
TRACE("Starting message thread\n");
|
||||
|
||||
cohr = CoInitialize(NULL);
|
||||
cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(FAILED(cohr))
|
||||
{
|
||||
WARN("Failed to initialize COM: 0x%08lx\n", cohr);
|
||||
@@ -423,11 +431,11 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
{
|
||||
case WM_USER_OpenDevice:
|
||||
req = (ThreadRequest*)msg.wParam;
|
||||
proxy = (ALCmmdevProxy*)msg.lParam;
|
||||
proxy = (ALCwasapiProxy*)msg.lParam;
|
||||
|
||||
hr = cohr = S_OK;
|
||||
if(++deviceCount == 1)
|
||||
hr = cohr = CoInitialize(NULL);
|
||||
hr = cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = V0(proxy,openProxy)();
|
||||
if(FAILED(hr))
|
||||
@@ -441,7 +449,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
|
||||
case WM_USER_ResetDevice:
|
||||
req = (ThreadRequest*)msg.wParam;
|
||||
proxy = (ALCmmdevProxy*)msg.lParam;
|
||||
proxy = (ALCwasapiProxy*)msg.lParam;
|
||||
|
||||
hr = V0(proxy,resetProxy)();
|
||||
ReturnMsgResponse(req, hr);
|
||||
@@ -449,7 +457,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
|
||||
case WM_USER_StartDevice:
|
||||
req = (ThreadRequest*)msg.wParam;
|
||||
proxy = (ALCmmdevProxy*)msg.lParam;
|
||||
proxy = (ALCwasapiProxy*)msg.lParam;
|
||||
|
||||
hr = V0(proxy,startProxy)();
|
||||
ReturnMsgResponse(req, hr);
|
||||
@@ -457,7 +465,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
|
||||
case WM_USER_StopDevice:
|
||||
req = (ThreadRequest*)msg.wParam;
|
||||
proxy = (ALCmmdevProxy*)msg.lParam;
|
||||
proxy = (ALCwasapiProxy*)msg.lParam;
|
||||
|
||||
V0(proxy,stopProxy)();
|
||||
ReturnMsgResponse(req, S_OK);
|
||||
@@ -465,7 +473,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
|
||||
case WM_USER_CloseDevice:
|
||||
req = (ThreadRequest*)msg.wParam;
|
||||
proxy = (ALCmmdevProxy*)msg.lParam;
|
||||
proxy = (ALCwasapiProxy*)msg.lParam;
|
||||
|
||||
V0(proxy,closeProxy)();
|
||||
if(--deviceCount == 0)
|
||||
@@ -479,7 +487,7 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
|
||||
hr = cohr = S_OK;
|
||||
if(++deviceCount == 1)
|
||||
hr = cohr = CoInitialize(NULL);
|
||||
hr = cohr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
|
||||
if(SUCCEEDED(hr))
|
||||
@@ -512,9 +520,9 @@ static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCmmdevPlayback {
|
||||
typedef struct ALCwasapiPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
DERIVE_FROM_TYPE(ALCmmdevProxy);
|
||||
DERIVE_FROM_TYPE(ALCwasapiProxy);
|
||||
|
||||
WCHAR *devid;
|
||||
|
||||
@@ -525,43 +533,42 @@ typedef struct ALCmmdevPlayback {
|
||||
|
||||
HANDLE MsgEvent;
|
||||
|
||||
volatile UINT32 Padding;
|
||||
ATOMIC(UINT32) Padding;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} ALCmmdevPlayback;
|
||||
} ALCwasapiPlayback;
|
||||
|
||||
static int ALCmmdevPlayback_mixerProc(void *arg);
|
||||
static int ALCwasapiPlayback_mixerProc(void *arg);
|
||||
|
||||
static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
|
||||
static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
|
||||
static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
|
||||
static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
|
||||
static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
|
||||
static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
|
||||
static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
|
||||
static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
|
||||
static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
|
||||
static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
|
||||
static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
|
||||
static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self);
|
||||
static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
|
||||
static void ALCwasapiPlayback_Construct(ALCwasapiPlayback *self, ALCdevice *device);
|
||||
static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback *self);
|
||||
static ALCenum ALCwasapiPlayback_open(ALCwasapiPlayback *self, const ALCchar *name);
|
||||
static HRESULT ALCwasapiPlayback_openProxy(ALCwasapiPlayback *self);
|
||||
static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback *self);
|
||||
static ALCboolean ALCwasapiPlayback_reset(ALCwasapiPlayback *self);
|
||||
static HRESULT ALCwasapiPlayback_resetProxy(ALCwasapiPlayback *self);
|
||||
static ALCboolean ALCwasapiPlayback_start(ALCwasapiPlayback *self);
|
||||
static HRESULT ALCwasapiPlayback_startProxy(ALCwasapiPlayback *self);
|
||||
static void ALCwasapiPlayback_stop(ALCwasapiPlayback *self);
|
||||
static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCwasapiPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static ClockLatency ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback *self);
|
||||
static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCwasapiPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCwasapiPlayback)
|
||||
|
||||
DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
|
||||
DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiPlayback);
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCwasapiPlayback);
|
||||
|
||||
|
||||
static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
|
||||
static void ALCwasapiPlayback_Construct(ALCwasapiPlayback *self, ALCdevice *device)
|
||||
{
|
||||
SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
|
||||
SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
|
||||
SET_VTABLE2(ALCwasapiPlayback, ALCbackend, self);
|
||||
SET_VTABLE2(ALCwasapiPlayback, ALCwasapiProxy, self);
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
|
||||
ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy, self));
|
||||
|
||||
self->devid = NULL;
|
||||
|
||||
@@ -572,13 +579,30 @@ static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device
|
||||
|
||||
self->MsgEvent = NULL;
|
||||
|
||||
self->Padding = 0;
|
||||
ATOMIC_INIT(&self->Padding, 0);
|
||||
|
||||
self->killNow = 0;
|
||||
ATOMIC_INIT(&self->killNow, 0);
|
||||
}
|
||||
|
||||
static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
|
||||
static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback *self)
|
||||
{
|
||||
if(self->MsgEvent)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
(void)WaitForResponse(&req);
|
||||
|
||||
CloseHandle(self->MsgEvent);
|
||||
self->MsgEvent = NULL;
|
||||
}
|
||||
|
||||
if(self->NotifyEvent)
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
|
||||
free(self->devid);
|
||||
self->devid = NULL;
|
||||
|
||||
if(self->NotifyEvent != NULL)
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
@@ -589,26 +613,26 @@ static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
|
||||
free(self->devid);
|
||||
self->devid = NULL;
|
||||
|
||||
ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
|
||||
ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy, self));
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
|
||||
FORCE_ALIGN static int ALCwasapiPlayback_mixerProc(void *arg)
|
||||
{
|
||||
ALCmmdevPlayback *self = arg;
|
||||
ALCwasapiPlayback *self = arg;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
UINT32 buffer_len, written;
|
||||
ALuint update_size, len;
|
||||
BYTE *buffer;
|
||||
HRESULT hr;
|
||||
|
||||
hr = CoInitialize(NULL);
|
||||
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
|
||||
ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "COM init failed: 0x%08lx", hr);
|
||||
V0(device->Backend,unlock)();
|
||||
return 1;
|
||||
}
|
||||
@@ -618,18 +642,18 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
|
||||
|
||||
update_size = device->UpdateSize;
|
||||
buffer_len = update_size * device->NumUpdates;
|
||||
while(!self->killNow)
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed))
|
||||
{
|
||||
hr = IAudioClient_GetCurrentPadding(self->client, &written);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to get padding: 0x%08lx\n", hr);
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to retrieve buffer padding: 0x%08lx", hr);
|
||||
V0(device->Backend,unlock)();
|
||||
break;
|
||||
}
|
||||
self->Padding = written;
|
||||
ATOMIC_STORE(&self->Padding, written, almemory_order_relaxed);
|
||||
|
||||
len = buffer_len - written;
|
||||
if(len < update_size)
|
||||
@@ -645,22 +669,22 @@ FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
|
||||
hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
ALCmmdevPlayback_lock(self);
|
||||
ALCwasapiPlayback_lock(self);
|
||||
aluMixData(device, buffer, len);
|
||||
self->Padding = written + len;
|
||||
ALCmmdevPlayback_unlock(self);
|
||||
ATOMIC_STORE(&self->Padding, written + len, almemory_order_relaxed);
|
||||
ALCwasapiPlayback_unlock(self);
|
||||
hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
|
||||
}
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("Failed to buffer data: 0x%08lx\n", hr);
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to send playback samples: 0x%08lx", hr);
|
||||
V0(device->Backend,unlock)();
|
||||
break;
|
||||
}
|
||||
}
|
||||
self->Padding = 0;
|
||||
ATOMIC_STORE(&self->Padding, 0, almemory_order_release);
|
||||
|
||||
CoUninitialize();
|
||||
return 0;
|
||||
@@ -706,7 +730,7 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
|
||||
static ALCenum ALCwasapiPlayback_open(ALCwasapiPlayback *self, const ALCchar *deviceName)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
@@ -766,7 +790,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
|
||||
hr = E_FAIL;
|
||||
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
else
|
||||
ERR("Failed to post thread message: %lu\n", GetLastError());
|
||||
@@ -791,7 +815,7 @@ static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *devi
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
|
||||
static HRESULT ALCwasapiPlayback_openProxy(ALCwasapiPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
void *ptr;
|
||||
@@ -828,24 +852,7 @@ static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
|
||||
}
|
||||
|
||||
|
||||
static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
|
||||
if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
(void)WaitForResponse(&req);
|
||||
|
||||
CloseHandle(self->MsgEvent);
|
||||
self->MsgEvent = NULL;
|
||||
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
|
||||
free(self->devid);
|
||||
self->devid = NULL;
|
||||
}
|
||||
|
||||
static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
|
||||
static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback *self)
|
||||
{
|
||||
if(self->client)
|
||||
IAudioClient_Release(self->client);
|
||||
@@ -857,18 +864,18 @@ static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
|
||||
static ALCboolean ALCwasapiPlayback_reset(ALCwasapiPlayback *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
|
||||
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
static HRESULT ALCwasapiPlayback_resetProxy(ALCwasapiPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
EndpointFormFactor formfactor = UnknownFormFactor;
|
||||
@@ -1128,18 +1135,18 @@ static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
|
||||
static ALCboolean ALCwasapiPlayback_start(ALCwasapiPlayback *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
|
||||
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
|
||||
static HRESULT ALCwasapiPlayback_startProxy(ALCwasapiPlayback *self)
|
||||
{
|
||||
HRESULT hr;
|
||||
void *ptr;
|
||||
@@ -1154,8 +1161,8 @@ static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
self->render = ptr;
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
|
||||
ATOMIC_STORE(&self->killNow, 0, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCwasapiPlayback_mixerProc, self) != althrd_success)
|
||||
{
|
||||
if(self->render)
|
||||
IAudioRenderClient_Release(self->render);
|
||||
@@ -1170,21 +1177,21 @@ static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
|
||||
}
|
||||
|
||||
|
||||
static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
|
||||
static void ALCwasapiPlayback_stop(ALCwasapiPlayback *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
(void)WaitForResponse(&req);
|
||||
}
|
||||
|
||||
static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
|
||||
static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(!self->render)
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
ATOMIC_STORE_SEQ(&self->killNow, 1);
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
IAudioRenderClient_Release(self->render);
|
||||
@@ -1193,23 +1200,24 @@ static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
|
||||
}
|
||||
|
||||
|
||||
static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self)
|
||||
static ClockLatency ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ClockLatency ret;
|
||||
|
||||
ALCmmdevPlayback_lock(self);
|
||||
ALCwasapiPlayback_lock(self);
|
||||
ret.ClockTime = GetDeviceClockTime(device);
|
||||
ret.Latency = self->Padding * DEVICE_CLOCK_RES / device->Frequency;
|
||||
ALCmmdevPlayback_unlock(self);
|
||||
ret.Latency = ATOMIC_LOAD(&self->Padding, almemory_order_relaxed) * DEVICE_CLOCK_RES /
|
||||
device->Frequency;
|
||||
ALCwasapiPlayback_unlock(self);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCmmdevCapture {
|
||||
typedef struct ALCwasapiCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
DERIVE_FROM_TYPE(ALCmmdevProxy);
|
||||
DERIVE_FROM_TYPE(ALCwasapiProxy);
|
||||
|
||||
WCHAR *devid;
|
||||
|
||||
@@ -1224,41 +1232,40 @@ typedef struct ALCmmdevCapture {
|
||||
SampleConverter *SampleConv;
|
||||
ll_ringbuffer_t *Ring;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} ALCmmdevCapture;
|
||||
} ALCwasapiCapture;
|
||||
|
||||
static int ALCmmdevCapture_recordProc(void *arg);
|
||||
static int ALCwasapiCapture_recordProc(void *arg);
|
||||
|
||||
static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
|
||||
static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
|
||||
static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
|
||||
static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
|
||||
static void ALCmmdevCapture_close(ALCmmdevCapture *self);
|
||||
static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
|
||||
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
|
||||
static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
|
||||
static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
|
||||
static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
|
||||
static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
|
||||
static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
|
||||
static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
|
||||
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
|
||||
static void ALCwasapiCapture_Construct(ALCwasapiCapture *self, ALCdevice *device);
|
||||
static void ALCwasapiCapture_Destruct(ALCwasapiCapture *self);
|
||||
static ALCenum ALCwasapiCapture_open(ALCwasapiCapture *self, const ALCchar *name);
|
||||
static HRESULT ALCwasapiCapture_openProxy(ALCwasapiCapture *self);
|
||||
static void ALCwasapiCapture_closeProxy(ALCwasapiCapture *self);
|
||||
static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, ALCboolean, reset)
|
||||
static HRESULT ALCwasapiCapture_resetProxy(ALCwasapiCapture *self);
|
||||
static ALCboolean ALCwasapiCapture_start(ALCwasapiCapture *self);
|
||||
static HRESULT ALCwasapiCapture_startProxy(ALCwasapiCapture *self);
|
||||
static void ALCwasapiCapture_stop(ALCwasapiCapture *self);
|
||||
static void ALCwasapiCapture_stopProxy(ALCwasapiCapture *self);
|
||||
static ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALuint ALCwasapiCapture_availableSamples(ALCwasapiCapture *self);
|
||||
static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCwasapiCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCwasapiCapture)
|
||||
|
||||
DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
|
||||
DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiCapture);
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCwasapiCapture);
|
||||
|
||||
|
||||
static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
|
||||
static void ALCwasapiCapture_Construct(ALCwasapiCapture *self, ALCdevice *device)
|
||||
{
|
||||
SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
|
||||
SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
|
||||
SET_VTABLE2(ALCwasapiCapture, ALCbackend, self);
|
||||
SET_VTABLE2(ALCwasapiCapture, ALCwasapiProxy, self);
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
|
||||
ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy, self));
|
||||
|
||||
self->devid = NULL;
|
||||
|
||||
@@ -1273,53 +1280,60 @@ static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
|
||||
self->SampleConv = NULL;
|
||||
self->Ring = NULL;
|
||||
|
||||
self->killNow = 0;
|
||||
ATOMIC_INIT(&self->killNow, 0);
|
||||
}
|
||||
|
||||
static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
|
||||
static void ALCwasapiCapture_Destruct(ALCwasapiCapture *self)
|
||||
{
|
||||
if(self->MsgEvent)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
(void)WaitForResponse(&req);
|
||||
|
||||
CloseHandle(self->MsgEvent);
|
||||
self->MsgEvent = NULL;
|
||||
}
|
||||
|
||||
if(self->NotifyEvent != NULL)
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
DestroySampleConverter(&self->SampleConv);
|
||||
DestroyChannelConverter(&self->ChannelConv);
|
||||
|
||||
if(self->NotifyEvent != NULL)
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
if(self->MsgEvent != NULL)
|
||||
CloseHandle(self->MsgEvent);
|
||||
self->MsgEvent = NULL;
|
||||
|
||||
free(self->devid);
|
||||
self->devid = NULL;
|
||||
|
||||
ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
|
||||
ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy, self));
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
|
||||
FORCE_ALIGN int ALCwasapiCapture_recordProc(void *arg)
|
||||
{
|
||||
ALCmmdevCapture *self = arg;
|
||||
ALCwasapiCapture *self = arg;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALfloat *samples = NULL;
|
||||
size_t samplesmax = 0;
|
||||
HRESULT hr;
|
||||
|
||||
hr = CoInitialize(NULL);
|
||||
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
|
||||
ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "COM init failed: 0x%08lx", hr);
|
||||
V0(device->Backend,unlock)();
|
||||
return 1;
|
||||
}
|
||||
|
||||
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
|
||||
|
||||
while(!self->killNow)
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed))
|
||||
{
|
||||
UINT32 avail;
|
||||
DWORD res;
|
||||
@@ -1365,7 +1379,7 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
|
||||
ALsizei srcframes = numsamples;
|
||||
|
||||
dstframes = SampleConverterInput(self->SampleConv,
|
||||
&srcdata, &srcframes, data[0].buf, data[0].len
|
||||
&srcdata, &srcframes, data[0].buf, (ALsizei)minz(data[0].len, INT_MAX)
|
||||
);
|
||||
if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0)
|
||||
{
|
||||
@@ -1374,16 +1388,16 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
|
||||
* dest block, do another run for the second block.
|
||||
*/
|
||||
dstframes += SampleConverterInput(self->SampleConv,
|
||||
&srcdata, &srcframes, data[1].buf, data[1].len
|
||||
&srcdata, &srcframes, data[1].buf, (ALsizei)minz(data[1].len, INT_MAX)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType,
|
||||
ALuint framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType,
|
||||
device->AmbiOrder);
|
||||
ALuint len1 = minu(data[0].len, numsamples);
|
||||
ALuint len2 = minu(data[1].len, numsamples-len1);
|
||||
size_t len1 = minz(data[0].len, numsamples);
|
||||
size_t len2 = minz(data[1].len, numsamples-len1);
|
||||
|
||||
memcpy(data[0].buf, rdata, len1*framesize);
|
||||
if(len2 > 0)
|
||||
@@ -1401,7 +1415,7 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
|
||||
if(FAILED(hr))
|
||||
{
|
||||
V0(device->Backend,lock)();
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to capture samples: 0x%08lx", hr);
|
||||
V0(device->Backend,unlock)();
|
||||
break;
|
||||
}
|
||||
@@ -1420,7 +1434,7 @@ FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
|
||||
static ALCenum ALCwasapiCapture_open(ALCwasapiCapture *self, const ALCchar *deviceName)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
@@ -1480,7 +1494,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
|
||||
hr = E_FAIL;
|
||||
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
else
|
||||
ERR("Failed to post thread message: %lu\n", GetLastError());
|
||||
@@ -1506,14 +1520,13 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
|
||||
hr = E_FAIL;
|
||||
if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
else
|
||||
ERR("Failed to post thread message: %lu\n", GetLastError());
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
ALCmmdevCapture_close(self);
|
||||
if(hr == E_OUTOFMEMORY)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
return ALC_INVALID_VALUE;
|
||||
@@ -1523,7 +1536,7 @@ static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *device
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
|
||||
static HRESULT ALCwasapiCapture_openProxy(ALCwasapiCapture *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
void *ptr;
|
||||
@@ -1560,27 +1573,7 @@ static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
|
||||
}
|
||||
|
||||
|
||||
static void ALCmmdevCapture_close(ALCmmdevCapture *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
|
||||
if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
(void)WaitForResponse(&req);
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
CloseHandle(self->MsgEvent);
|
||||
self->MsgEvent = NULL;
|
||||
|
||||
CloseHandle(self->NotifyEvent);
|
||||
self->NotifyEvent = NULL;
|
||||
|
||||
free(self->devid);
|
||||
self->devid = NULL;
|
||||
}
|
||||
|
||||
static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
|
||||
static void ALCwasapiCapture_closeProxy(ALCwasapiCapture *self)
|
||||
{
|
||||
if(self->client)
|
||||
IAudioClient_Release(self->client);
|
||||
@@ -1592,7 +1585,7 @@ static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
|
||||
}
|
||||
|
||||
|
||||
static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
static HRESULT ALCwasapiCapture_resetProxy(ALCwasapiCapture *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
WAVEFORMATEXTENSIBLE OutputType;
|
||||
@@ -1817,10 +1810,11 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
return hr;
|
||||
}
|
||||
|
||||
buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
|
||||
buffer_len = maxu(device->UpdateSize*device->NumUpdates, buffer_len);
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = ll_ringbuffer_create(buffer_len,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
|
||||
false
|
||||
);
|
||||
if(!self->Ring)
|
||||
{
|
||||
@@ -1839,18 +1833,18 @@ static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
|
||||
static ALCboolean ALCwasapiCapture_start(ALCwasapiCapture *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
hr = WaitForResponse(&req);
|
||||
|
||||
return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
|
||||
static HRESULT ALCwasapiCapture_startProxy(ALCwasapiCapture *self)
|
||||
{
|
||||
HRESULT hr;
|
||||
void *ptr;
|
||||
@@ -1867,8 +1861,8 @@ static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
self->capture = ptr;
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
|
||||
ATOMIC_STORE(&self->killNow, 0, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCwasapiCapture_recordProc, self) != althrd_success)
|
||||
{
|
||||
ERR("Failed to start thread\n");
|
||||
IAudioCaptureClient_Release(self->capture);
|
||||
@@ -1887,21 +1881,21 @@ static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
|
||||
}
|
||||
|
||||
|
||||
static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
|
||||
static void ALCwasapiCapture_stop(ALCwasapiCapture *self)
|
||||
{
|
||||
ThreadRequest req = { self->MsgEvent, 0 };
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
|
||||
if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCwasapiProxy, self)))
|
||||
(void)WaitForResponse(&req);
|
||||
}
|
||||
|
||||
static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
|
||||
static void ALCwasapiCapture_stopProxy(ALCwasapiCapture *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(!self->capture)
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
ATOMIC_STORE_SEQ(&self->killNow, 1);
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
IAudioCaptureClient_Release(self->capture);
|
||||
@@ -1911,14 +1905,14 @@ static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
|
||||
}
|
||||
|
||||
|
||||
ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
|
||||
ALuint ALCwasapiCapture_availableSamples(ALCwasapiCapture *self)
|
||||
{
|
||||
return (ALuint)ll_ringbuffer_read_space(self->Ring);
|
||||
}
|
||||
|
||||
ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
ALCenum ALCwasapiCapture_captureSamples(ALCwasapiCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
if(ALCmmdevCapture_availableSamples(self) < samples)
|
||||
if(ALCwasapiCapture_availableSamples(self) < samples)
|
||||
return ALC_INVALID_VALUE;
|
||||
ll_ringbuffer_read(self->Ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
@@ -1930,23 +1924,27 @@ static inline void AppendAllDevicesList2(const DevMap *entry)
|
||||
static inline void AppendCaptureDeviceList2(const DevMap *entry)
|
||||
{ AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
|
||||
|
||||
typedef struct ALCmmdevBackendFactory {
|
||||
typedef struct ALCwasapiBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCmmdevBackendFactory;
|
||||
#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
|
||||
} ALCwasapiBackendFactory;
|
||||
#define ALCWASAPIBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwasapiBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
|
||||
static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
|
||||
static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory *self);
|
||||
static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory *self);
|
||||
static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwasapiBackendFactory);
|
||||
|
||||
|
||||
static BOOL MMDevApiLoad(void)
|
||||
static ALCboolean ALCwasapiBackendFactory_init(ALCwasapiBackendFactory* UNUSED(self))
|
||||
{
|
||||
static HRESULT InitResult;
|
||||
|
||||
VECTOR_INIT(PlaybackDevices);
|
||||
VECTOR_INIT(CaptureDevices);
|
||||
|
||||
if(!ThreadHdl)
|
||||
{
|
||||
ThreadRequest req;
|
||||
@@ -1957,26 +1955,17 @@ static BOOL MMDevApiLoad(void)
|
||||
ERR("Failed to create event: %lu\n", GetLastError());
|
||||
else
|
||||
{
|
||||
ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
|
||||
ThreadHdl = CreateThread(NULL, 0, ALCwasapiProxy_messageHandler, &req, 0, &ThreadID);
|
||||
if(ThreadHdl != NULL)
|
||||
InitResult = WaitForResponse(&req);
|
||||
CloseHandle(req.FinishedEvt);
|
||||
}
|
||||
}
|
||||
return SUCCEEDED(InitResult);
|
||||
|
||||
return SUCCEEDED(InitResult) ? ALC_TRUE : ALC_FALSE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
|
||||
{
|
||||
VECTOR_INIT(PlaybackDevices);
|
||||
VECTOR_INIT(CaptureDevices);
|
||||
|
||||
if(!MMDevApiLoad())
|
||||
return ALC_FALSE;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
|
||||
static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory* UNUSED(self))
|
||||
{
|
||||
clear_devlist(&PlaybackDevices);
|
||||
VECTOR_DEINIT(PlaybackDevices);
|
||||
@@ -1993,19 +1982,14 @@ static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
|
||||
}
|
||||
}
|
||||
|
||||
static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
static ALCboolean ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
/* TODO: Disable capture with mmdevapi for now, since it doesn't do any
|
||||
* rechanneling or resampling; if the device is configured for 48000hz
|
||||
* stereo input, for example, and the app asks for 22050hz mono,
|
||||
* initialization will fail.
|
||||
*/
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
ThreadRequest req = { NULL, 0 };
|
||||
|
||||
@@ -2032,19 +2016,19 @@ static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), e
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
static ALCbackend* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCmmdevPlayback *backend;
|
||||
NEW_OBJ(backend, ALCmmdevPlayback)(device);
|
||||
ALCwasapiPlayback *backend;
|
||||
NEW_OBJ(backend, ALCwasapiPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCmmdevCapture *backend;
|
||||
NEW_OBJ(backend, ALCmmdevCapture)(device);
|
||||
ALCwasapiCapture *backend;
|
||||
NEW_OBJ(backend, ALCwasapiCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
@@ -2053,8 +2037,8 @@ static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory*
|
||||
}
|
||||
|
||||
|
||||
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
|
||||
ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
|
||||
static ALCwasapiBackendFactory factory = ALCWASAPIBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
+17
-17
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
@@ -76,16 +77,15 @@ typedef struct ALCwaveBackend {
|
||||
ALvoid *mBuffer;
|
||||
ALuint mSize;
|
||||
|
||||
volatile int killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCwaveBackend;
|
||||
|
||||
static int ALCwaveBackend_mixerProc(void *ptr);
|
||||
|
||||
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, Destruct)
|
||||
static void ALCwaveBackend_Destruct(ALCwaveBackend *self);
|
||||
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name);
|
||||
static void ALCwaveBackend_close(ALCwaveBackend *self);
|
||||
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self);
|
||||
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self);
|
||||
static void ALCwaveBackend_stop(ALCwaveBackend *self);
|
||||
@@ -110,9 +110,17 @@ static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device)
|
||||
self->mBuffer = NULL;
|
||||
self->mSize = 0;
|
||||
|
||||
self->killNow = 1;
|
||||
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)
|
||||
{
|
||||
@@ -135,7 +143,8 @@ static int ALCwaveBackend_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)
|
||||
{
|
||||
@@ -196,7 +205,7 @@ static int ALCwaveBackend_mixerProc(void *ptr)
|
||||
{
|
||||
ERR("Error writing to file\n");
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device);
|
||||
aluHandleDisconnect(device, "Failed to write playback samples");
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
@@ -233,13 +242,6 @@ static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCwaveBackend_close(ALCwaveBackend *self)
|
||||
{
|
||||
if(self->mFile)
|
||||
fclose(self->mFile);
|
||||
self->mFile = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
@@ -354,7 +356,7 @@ static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
self->killNow = 0;
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
|
||||
{
|
||||
free(self->mBuffer);
|
||||
@@ -372,10 +374,8 @@ static void ALCwaveBackend_stop(ALCwaveBackend *self)
|
||||
long size;
|
||||
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->mBuffer);
|
||||
|
||||
+39
-50
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
@@ -147,7 +148,7 @@ typedef struct ALCwinmmPlayback {
|
||||
|
||||
WAVEFORMATEX Format;
|
||||
|
||||
volatile ALboolean killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCwinmmPlayback;
|
||||
|
||||
@@ -158,7 +159,6 @@ static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWO
|
||||
static int ALCwinmmPlayback_mixerProc(void *arg);
|
||||
|
||||
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
|
||||
static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
|
||||
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
|
||||
static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
|
||||
static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
|
||||
@@ -180,7 +180,7 @@ static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device
|
||||
InitRef(&self->WaveBuffersCommitted, 0);
|
||||
self->OutHdl = NULL;
|
||||
|
||||
self->killNow = AL_TRUE;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
|
||||
@@ -224,7 +224,7 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
|
||||
if(msg.message != WOM_DONE)
|
||||
continue;
|
||||
|
||||
if(self->killNow)
|
||||
if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
{
|
||||
if(ReadRef(&self->WaveBuffersCommitted) == 0)
|
||||
break;
|
||||
@@ -311,9 +311,6 @@ failure:
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
|
||||
{ }
|
||||
|
||||
static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
@@ -374,7 +371,7 @@ static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
|
||||
ALint BufferSize;
|
||||
ALuint i;
|
||||
|
||||
self->killNow = AL_FALSE;
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
|
||||
@@ -405,11 +402,8 @@ static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
if(self->killNow)
|
||||
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
// Set flag to stop processing headers
|
||||
self->killNow = AL_TRUE;
|
||||
althrd_join(self->thread, &i);
|
||||
|
||||
// Release the wave buffers
|
||||
@@ -436,7 +430,7 @@ typedef struct ALCwinmmCapture {
|
||||
|
||||
WAVEFORMATEX Format;
|
||||
|
||||
volatile ALboolean killNow;
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCwinmmCapture;
|
||||
|
||||
@@ -447,7 +441,6 @@ static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_
|
||||
static int ALCwinmmCapture_captureProc(void *arg);
|
||||
|
||||
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
|
||||
static void ALCwinmmCapture_close(ALCwinmmCapture *self);
|
||||
static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
|
||||
static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
|
||||
@@ -469,11 +462,38 @@ static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
|
||||
InitRef(&self->WaveBuffersCommitted, 0);
|
||||
self->InHdl = NULL;
|
||||
|
||||
self->killNow = AL_TRUE;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
|
||||
{
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
/* Tell the processing thread to quit and wait for it to do so. */
|
||||
if(!ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
{
|
||||
PostThreadMessage(self->thread, WM_QUIT, 0, 0);
|
||||
|
||||
althrd_join(self->thread, &i);
|
||||
|
||||
/* Make sure capture is stopped and all pending buffers are flushed. */
|
||||
waveInReset(self->InHdl);
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0) buffer = self->WaveBuffer[i].lpData;
|
||||
self->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
// Close the Wave device
|
||||
if(self->InHdl)
|
||||
waveInClose(self->InHdl);
|
||||
self->InHdl = 0;
|
||||
@@ -512,7 +532,7 @@ static int ALCwinmmCapture_captureProc(void *arg)
|
||||
continue;
|
||||
/* Don't wait for other buffers to finish before quitting. We're
|
||||
* closing so we don't need them. */
|
||||
if(self->killNow)
|
||||
if(ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
break;
|
||||
|
||||
WaveHdr = ((WAVEHDR*)msg.lParam);
|
||||
@@ -606,7 +626,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
|
||||
if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
|
||||
CapturedDataSize = self->Format.nSamplesPerSec / 10;
|
||||
|
||||
self->Ring = ll_ringbuffer_create(CapturedDataSize+1, self->Format.nBlockAlign);
|
||||
self->Ring = ll_ringbuffer_create(CapturedDataSize, self->Format.nBlockAlign, false);
|
||||
if(!self->Ring) goto failure;
|
||||
|
||||
InitRef(&self->WaveBuffersCommitted, 0);
|
||||
@@ -632,7 +652,7 @@ static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
|
||||
IncrementRef(&self->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
self->killNow = AL_FALSE;
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
|
||||
goto failure;
|
||||
|
||||
@@ -657,37 +677,6 @@ failure:
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void ALCwinmmCapture_close(ALCwinmmCapture *self)
|
||||
{
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
/* Tell the processing thread to quit and wait for it to do so. */
|
||||
self->killNow = AL_TRUE;
|
||||
PostThreadMessage(self->thread, WM_QUIT, 0, 0);
|
||||
|
||||
althrd_join(self->thread, &i);
|
||||
|
||||
/* Make sure capture is stopped and all pending buffers are flushed. */
|
||||
waveInReset(self->InHdl);
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0) buffer = self->WaveBuffer[i].lpData;
|
||||
self->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = NULL;
|
||||
|
||||
// Close the Wave device
|
||||
waveInClose(self->InHdl);
|
||||
self->InHdl = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
|
||||
{
|
||||
waveInStart(self->InHdl);
|
||||
@@ -707,7 +696,7 @@ static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *bu
|
||||
|
||||
static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
|
||||
{
|
||||
return ll_ringbuffer_read_space(self->Ring);
|
||||
return (ALCuint)ll_ringbuffer_read_space(self->Ring);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+92
-212
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "bformatdec.h"
|
||||
#include "ambdec.h"
|
||||
#include "mixer_defs.h"
|
||||
#include "filters/splitter.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "bool.h"
|
||||
@@ -11,114 +11,14 @@
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult)
|
||||
{
|
||||
ALfloat w = freq_mult * F_TAU;
|
||||
ALfloat cw = cosf(w);
|
||||
if(cw > FLT_EPSILON)
|
||||
splitter->coeff = (sinf(w) - 1.0f) / cw;
|
||||
else
|
||||
splitter->coeff = cw * -0.5f;
|
||||
|
||||
splitter->lp_z1 = 0.0f;
|
||||
splitter->lp_z2 = 0.0f;
|
||||
splitter->hp_z1 = 0.0f;
|
||||
}
|
||||
|
||||
void bandsplit_clear(BandSplitter *splitter)
|
||||
{
|
||||
splitter->lp_z1 = 0.0f;
|
||||
splitter->lp_z2 = 0.0f;
|
||||
splitter->hp_z1 = 0.0f;
|
||||
}
|
||||
|
||||
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
|
||||
const ALfloat *input, ALsizei count)
|
||||
{
|
||||
ALfloat coeff, d, x;
|
||||
ALfloat z1, z2;
|
||||
ALsizei i;
|
||||
|
||||
coeff = splitter->coeff*0.5f + 0.5f;
|
||||
z1 = splitter->lp_z1;
|
||||
z2 = splitter->lp_z2;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
x = input[i];
|
||||
|
||||
d = (x - z1) * coeff;
|
||||
x = z1 + d;
|
||||
z1 = x + d;
|
||||
|
||||
d = (x - z2) * coeff;
|
||||
x = z2 + d;
|
||||
z2 = x + d;
|
||||
|
||||
lpout[i] = x;
|
||||
}
|
||||
splitter->lp_z1 = z1;
|
||||
splitter->lp_z2 = z2;
|
||||
|
||||
coeff = splitter->coeff;
|
||||
z1 = splitter->hp_z1;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
x = input[i];
|
||||
|
||||
d = x - coeff*z1;
|
||||
x = z1 + coeff*d;
|
||||
z1 = d;
|
||||
|
||||
hpout[i] = x - lpout[i];
|
||||
}
|
||||
splitter->hp_z1 = z1;
|
||||
}
|
||||
|
||||
|
||||
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult)
|
||||
{
|
||||
ALfloat w = freq_mult * F_TAU;
|
||||
ALfloat cw = cosf(w);
|
||||
if(cw > FLT_EPSILON)
|
||||
splitter->coeff = (sinf(w) - 1.0f) / cw;
|
||||
else
|
||||
splitter->coeff = cw * -0.5f;
|
||||
|
||||
splitter->z1 = 0.0f;
|
||||
}
|
||||
|
||||
void splitterap_clear(SplitterAllpass *splitter)
|
||||
{
|
||||
splitter->z1 = 0.0f;
|
||||
}
|
||||
|
||||
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
|
||||
{
|
||||
ALfloat coeff, d, x;
|
||||
ALfloat z1;
|
||||
ALsizei i;
|
||||
|
||||
coeff = splitter->coeff;
|
||||
z1 = splitter->z1;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
x = samples[i];
|
||||
|
||||
d = x - coeff*z1;
|
||||
x = z1 + coeff*d;
|
||||
z1 = d;
|
||||
|
||||
samples[i] = x;
|
||||
}
|
||||
splitter->z1 = z1;
|
||||
}
|
||||
|
||||
|
||||
static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
|
||||
/* 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
|
||||
};
|
||||
static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
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) */
|
||||
@@ -136,7 +36,7 @@ static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
2.645751311f, /* ACN 14 (N), sqrt(7) */
|
||||
2.645751311f, /* ACN 15 (P), sqrt(7) */
|
||||
};
|
||||
static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
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) */
|
||||
@@ -156,11 +56,9 @@ static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
};
|
||||
|
||||
|
||||
enum FreqBand {
|
||||
FB_HighFreq,
|
||||
FB_LowFreq,
|
||||
FB_Max
|
||||
};
|
||||
#define HF_BAND 0
|
||||
#define LF_BAND 1
|
||||
#define NUM_BANDS 2
|
||||
|
||||
/* These points are in AL coordinates! */
|
||||
static const ALfloat Ambi3DPoints[8][3] = {
|
||||
@@ -173,35 +71,28 @@ static const ALfloat Ambi3DPoints[8][3] = {
|
||||
{ -0.577350269f, -0.577350269f, 0.577350269f },
|
||||
{ 0.577350269f, -0.577350269f, 0.577350269f },
|
||||
};
|
||||
static const ALfloat Ambi3DDecoder[8][FB_Max][MAX_AMBI_COEFFS] = {
|
||||
{ { 0.25f, 0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, 0.125f, 0.125f } },
|
||||
{ { 0.25f, -0.1443375672f, 0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, 0.125f, 0.125f } },
|
||||
{ { 0.25f, 0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, 0.125f, -0.125f } },
|
||||
{ { 0.25f, -0.1443375672f, 0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, 0.125f, -0.125f } },
|
||||
{ { 0.25f, 0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, 0.125f, -0.125f, 0.125f } },
|
||||
{ { 0.25f, -0.1443375672f, -0.1443375672f, 0.1443375672f }, { 0.125f, -0.125f, -0.125f, 0.125f } },
|
||||
{ { 0.25f, 0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, 0.125f, -0.125f, -0.125f } },
|
||||
{ { 0.25f, -0.1443375672f, -0.1443375672f, -0.1443375672f }, { 0.125f, -0.125f, -0.125f, -0.125f } },
|
||||
static 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
|
||||
};
|
||||
|
||||
|
||||
static RowMixerFunc MixMatrixRow = MixRow_C;
|
||||
|
||||
|
||||
static alonce_flag bformatdec_inited = AL_ONCE_FLAG_INIT;
|
||||
|
||||
static void init_bformatdec(void)
|
||||
{
|
||||
MixMatrixRow = SelectRowMixer();
|
||||
}
|
||||
|
||||
|
||||
/* NOTE: BandSplitter filters are unused with single-band decoding */
|
||||
typedef struct BFormatDec {
|
||||
ALboolean Enabled[MAX_OUTPUT_CHANNELS];
|
||||
ALuint Enabled; /* Bitfield of enabled channels. */
|
||||
|
||||
union {
|
||||
alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][FB_Max][MAX_AMBI_COEFFS];
|
||||
alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][NUM_BANDS][MAX_AMBI_COEFFS];
|
||||
alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
|
||||
} Matrix;
|
||||
|
||||
@@ -216,7 +107,7 @@ typedef struct BFormatDec {
|
||||
|
||||
struct {
|
||||
BandSplitter XOver;
|
||||
ALfloat Gains[FB_Max];
|
||||
ALfloat Gains[NUM_BANDS];
|
||||
} UpSampler[4];
|
||||
|
||||
ALsizei NumChannels;
|
||||
@@ -225,21 +116,20 @@ typedef struct BFormatDec {
|
||||
|
||||
BFormatDec *bformatdec_alloc()
|
||||
{
|
||||
alcall_once(&bformatdec_inited, init_bformatdec);
|
||||
return al_calloc(16, sizeof(BFormatDec));
|
||||
}
|
||||
|
||||
void bformatdec_free(BFormatDec *dec)
|
||||
void bformatdec_free(BFormatDec **dec)
|
||||
{
|
||||
if(dec)
|
||||
if(dec && *dec)
|
||||
{
|
||||
al_free(dec->Samples);
|
||||
dec->Samples = NULL;
|
||||
dec->SamplesHF = NULL;
|
||||
dec->SamplesLF = NULL;
|
||||
al_free((*dec)->Samples);
|
||||
(*dec)->Samples = NULL;
|
||||
(*dec)->SamplesHF = NULL;
|
||||
(*dec)->SamplesLF = NULL;
|
||||
|
||||
memset(dec, 0, sizeof(*dec));
|
||||
al_free(dec);
|
||||
al_free(*dec);
|
||||
*dec = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +138,7 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount
|
||||
static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = {
|
||||
0, 1, 3, 4, 8, 9, 15
|
||||
};
|
||||
const ALfloat *coeff_scale = UnitScale;
|
||||
const ALfloat *coeff_scale = N3D2N3DScale;
|
||||
bool periphonic;
|
||||
ALfloat ratio;
|
||||
ALsizei i;
|
||||
@@ -263,10 +153,9 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount
|
||||
dec->SamplesHF = dec->Samples;
|
||||
dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
dec->Enabled[i] = AL_FALSE;
|
||||
dec->Enabled = 0;
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
dec->Enabled[chanmap[i]] = AL_TRUE;
|
||||
dec->Enabled |= 1 << chanmap[i];
|
||||
|
||||
if(conf->CoeffScale == ADS_SN3D)
|
||||
coeff_scale = SN3D2N3DScale;
|
||||
@@ -281,31 +170,31 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount
|
||||
{
|
||||
periphonic = true;
|
||||
|
||||
dec->UpSampler[0].Gains[FB_HighFreq] = (dec->NumChannels > 9) ? W_SCALE3D_THIRD :
|
||||
(dec->NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
|
||||
dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
|
||||
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[FB_HighFreq] = (dec->NumChannels > 9) ? XYZ_SCALE3D_THIRD :
|
||||
(dec->NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
|
||||
dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
|
||||
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[FB_HighFreq] = (dec->NumChannels > 5) ? W_SCALE2D_THIRD :
|
||||
(dec->NumChannels > 3) ? W_SCALE2D_SECOND : 1.0f;
|
||||
dec->UpSampler[0].Gains[FB_LowFreq] = 1.0f;
|
||||
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[FB_HighFreq] = (dec->NumChannels > 5) ? XYZ_SCALE2D_THIRD :
|
||||
(dec->NumChannels > 3) ? XYZ_SCALE2D_SECOND : 1.0f;
|
||||
dec->UpSampler[i].Gains[FB_LowFreq] = 1.0f;
|
||||
dec->UpSampler[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[FB_HighFreq] = 0.0f;
|
||||
dec->UpSampler[3].Gains[FB_LowFreq] = 0.0f;
|
||||
dec->UpSampler[3].Gains[HF_BAND] = 0.0f;
|
||||
dec->UpSampler[3].Gains[LF_BAND] = 0.0f;
|
||||
}
|
||||
|
||||
memset(&dec->Matrix, 0, sizeof(dec->Matrix));
|
||||
@@ -372,8 +261,8 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount
|
||||
else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
|
||||
else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
|
||||
if((conf->ChanMask&(1<<l)))
|
||||
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
|
||||
coeff_scale[l] * gain;
|
||||
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++)
|
||||
{
|
||||
@@ -383,8 +272,8 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount
|
||||
else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
|
||||
else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
|
||||
if((conf->ChanMask&(1<<l)))
|
||||
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
|
||||
coeff_scale[l] * gain;
|
||||
dec->Matrix.Dual[chan][LF_BAND][j] = conf->LFMatrix[i][k++] /
|
||||
coeff_scale[l] * gain;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -396,8 +285,8 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount
|
||||
else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
|
||||
else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
|
||||
if((conf->ChanMask&(1<<j)))
|
||||
dec->Matrix.Dual[chan][FB_HighFreq][j] = conf->HFMatrix[i][k++] /
|
||||
coeff_scale[j] * gain;
|
||||
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++)
|
||||
{
|
||||
@@ -406,8 +295,8 @@ void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount
|
||||
else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
|
||||
else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
|
||||
if((conf->ChanMask&(1<<j)))
|
||||
dec->Matrix.Dual[chan][FB_LowFreq][j] = conf->LFMatrix[i][k++] /
|
||||
coeff_scale[j] * gain;
|
||||
dec->Matrix.Dual[chan][LF_BAND][j] = conf->LFMatrix[i][k++] /
|
||||
coeff_scale[j] * gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,17 +317,15 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU
|
||||
|
||||
for(chan = 0;chan < OutChannels;chan++)
|
||||
{
|
||||
if(!dec->Enabled[chan])
|
||||
if(!(dec->Enabled&(1<<chan)))
|
||||
continue;
|
||||
|
||||
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
||||
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_HighFreq],
|
||||
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesHF), dec->NumChannels, 0,
|
||||
SamplesToDo
|
||||
MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][HF_BAND],
|
||||
dec->SamplesHF, dec->NumChannels, 0, SamplesToDo
|
||||
);
|
||||
MixMatrixRow(dec->ChannelMix, dec->Matrix.Dual[chan][FB_LowFreq],
|
||||
SAFE_CONST(ALfloatBUFFERSIZE*,dec->SamplesLF), 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++)
|
||||
@@ -449,12 +336,12 @@ void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BU
|
||||
{
|
||||
for(chan = 0;chan < OutChannels;chan++)
|
||||
{
|
||||
if(!dec->Enabled[chan])
|
||||
if(!(dec->Enabled&(1<<chan)))
|
||||
continue;
|
||||
|
||||
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
||||
MixMatrixRow(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
|
||||
dec->NumChannels, 0, SamplesToDo);
|
||||
MixRowSamples(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
|
||||
dec->NumChannels, 0, SamplesToDo);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[chan][i] += dec->ChannelMix[i];
|
||||
@@ -483,14 +370,13 @@ void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[B
|
||||
* bands.
|
||||
*/
|
||||
bandsplit_process(&dec->UpSampler[i].XOver,
|
||||
dec->Samples[FB_HighFreq], dec->Samples[FB_LowFreq],
|
||||
dec->Samples[HF_BAND], dec->Samples[LF_BAND],
|
||||
InSamples[i], SamplesToDo
|
||||
);
|
||||
|
||||
/* Now write each band to the output. */
|
||||
MixMatrixRow(OutBuffer[i], dec->UpSampler[i].Gains,
|
||||
SAFE_CONST(ALfloatBUFFERSIZE*,dec->Samples), FB_Max, 0,
|
||||
SamplesToDo
|
||||
MixRowSamples(OutBuffer[i], dec->UpSampler[i].Gains,
|
||||
dec->Samples, NUM_BANDS, 0, SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -511,28 +397,31 @@ static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsiz
|
||||
#define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
|
||||
|
||||
typedef struct AmbiUpsampler {
|
||||
alignas(16) ALfloat Samples[FB_Max][BUFFERSIZE];
|
||||
alignas(16) ALfloat Samples[NUM_BANDS][BUFFERSIZE];
|
||||
|
||||
BandSplitter XOver[4];
|
||||
|
||||
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][FB_Max];
|
||||
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][NUM_BANDS];
|
||||
} AmbiUpsampler;
|
||||
|
||||
AmbiUpsampler *ambiup_alloc()
|
||||
{
|
||||
alcall_once(&bformatdec_inited, init_bformatdec);
|
||||
return al_calloc(16, sizeof(AmbiUpsampler));
|
||||
}
|
||||
|
||||
void ambiup_free(struct AmbiUpsampler *ambiup)
|
||||
void ambiup_free(struct AmbiUpsampler **ambiup)
|
||||
{
|
||||
al_free(ambiup);
|
||||
if(ambiup)
|
||||
{
|
||||
al_free(*ambiup);
|
||||
*ambiup = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
|
||||
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale)
|
||||
{
|
||||
ALfloat ratio;
|
||||
size_t i;
|
||||
ALsizei i;
|
||||
|
||||
ratio = 400.0f / (ALfloat)device->Frequency;
|
||||
for(i = 0;i < 4;i++)
|
||||
@@ -545,11 +434,11 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
|
||||
ALsizei j;
|
||||
size_t k;
|
||||
|
||||
for(i = 0;i < COUNTOF(Ambi3DPoints);i++)
|
||||
for(k = 0;k < COUNTOF(Ambi3DPoints);k++)
|
||||
{
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
|
||||
CalcDirectionCoeffs(Ambi3DPoints[i], 0.0f, coeffs);
|
||||
ComputePanningGains(device->Dry, coeffs, 1.0f, encgains[i]);
|
||||
CalcDirectionCoeffs(Ambi3DPoints[k], 0.0f, coeffs);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
|
||||
}
|
||||
|
||||
/* Combine the matrices that do the in->virt and virt->out conversions
|
||||
@@ -561,32 +450,24 @@ void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device)
|
||||
{
|
||||
for(j = 0;j < device->Dry.NumChannels;j++)
|
||||
{
|
||||
ALfloat hfgain=0.0f, lfgain=0.0f;
|
||||
ALdouble gain = 0.0;
|
||||
for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
|
||||
{
|
||||
hfgain += Ambi3DDecoder[k][FB_HighFreq][i]*encgains[k][j];
|
||||
lfgain += Ambi3DDecoder[k][FB_LowFreq][i]*encgains[k][j];
|
||||
}
|
||||
ambiup->Gains[i][j][FB_HighFreq] = hfgain;
|
||||
ambiup->Gains[i][j][FB_LowFreq] = lfgain;
|
||||
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
|
||||
{
|
||||
/* Assumes full 3D/periphonic on the input and output mixes! */
|
||||
ALfloat w_scale = (device->Dry.NumChannels > 9) ? W_SCALE3D_THIRD :
|
||||
(device->Dry.NumChannels > 4) ? W_SCALE3D_SECOND : 1.0f;
|
||||
ALfloat xyz_scale = (device->Dry.NumChannels > 9) ? XYZ_SCALE3D_THIRD :
|
||||
(device->Dry.NumChannels > 4) ? XYZ_SCALE3D_SECOND : 1.0f;
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
ALsizei index = GetChannelForACN(device->Dry, i);
|
||||
if(index != INVALID_UPSAMPLE_INDEX)
|
||||
{
|
||||
ALfloat scale = device->Dry.Ambi.Map[index].Scale;
|
||||
ambiup->Gains[i][index][FB_HighFreq] = scale * ((i==0) ? w_scale : xyz_scale);
|
||||
ambiup->Gains[i][index][FB_LowFreq] = scale;
|
||||
ambiup->Gains[i][index][HF_BAND] = scale * ((i==0) ? w_scale : xyz_scale);
|
||||
ambiup->Gains[i][index][LF_BAND] = scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -599,14 +480,13 @@ void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
bandsplit_process(&ambiup->XOver[i],
|
||||
ambiup->Samples[FB_HighFreq], ambiup->Samples[FB_LowFreq],
|
||||
ambiup->Samples[HF_BAND], ambiup->Samples[LF_BAND],
|
||||
InSamples[i], SamplesToDo
|
||||
);
|
||||
|
||||
for(j = 0;j < OutChannels;j++)
|
||||
MixMatrixRow(OutBuffer[j], ambiup->Gains[i][j],
|
||||
SAFE_CONST(ALfloatBUFFERSIZE*,ambiup->Samples), FB_Max, 0,
|
||||
SamplesToDo
|
||||
MixRowSamples(OutBuffer[j], ambiup->Gains[i][j],
|
||||
ambiup->Samples, NUM_BANDS, 0, SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-38
@@ -7,18 +7,26 @@
|
||||
/* These are the necessary scales for first-order HF responses to play over
|
||||
* higher-order 2D (non-periphonic) decoders.
|
||||
*/
|
||||
#define W_SCALE2D_SECOND 1.224744871f /* sqrt(1.5) */
|
||||
#define XYZ_SCALE2D_SECOND 1.0f
|
||||
#define W_SCALE2D_THIRD 1.414213562f /* sqrt(2) */
|
||||
#define XYZ_SCALE2D_THIRD 1.082392196f
|
||||
#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_SCALE3D_SECOND 1.341640787f /* sqrt(1.8) */
|
||||
#define XYZ_SCALE3D_SECOND 1.0f
|
||||
#define W_SCALE3D_THIRD 1.695486018f
|
||||
#define XYZ_SCALE3D_THIRD 1.136697713f
|
||||
#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.
|
||||
*/
|
||||
const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS];
|
||||
const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS];
|
||||
const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS];
|
||||
|
||||
|
||||
struct AmbDecConf;
|
||||
@@ -27,7 +35,7 @@ struct AmbiUpsampler;
|
||||
|
||||
|
||||
struct BFormatDec *bformatdec_alloc();
|
||||
void bformatdec_free(struct BFormatDec *dec);
|
||||
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. */
|
||||
@@ -38,38 +46,12 @@ void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[B
|
||||
|
||||
|
||||
/* Stand-alone first-order upsampler. Kept here because it shares some stuff
|
||||
* with bformatdec.
|
||||
* 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);
|
||||
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);
|
||||
|
||||
|
||||
/* Band splitter. Splits a signal into two phase-matching frequency bands. */
|
||||
typedef struct BandSplitter {
|
||||
ALfloat coeff;
|
||||
ALfloat lp_z1;
|
||||
ALfloat lp_z2;
|
||||
ALfloat hp_z1;
|
||||
} BandSplitter;
|
||||
|
||||
void bandsplit_init(BandSplitter *splitter, ALfloat freq_mult);
|
||||
void bandsplit_clear(BandSplitter *splitter);
|
||||
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
|
||||
const ALfloat *input, ALsizei count);
|
||||
|
||||
/* The all-pass portion of the band splitter. Applies the same phase shift
|
||||
* without splitting the signal.
|
||||
*/
|
||||
typedef struct SplitterAllpass {
|
||||
ALfloat coeff;
|
||||
ALfloat z1;
|
||||
} SplitterAllpass;
|
||||
|
||||
void splitterap_init(SplitterAllpass *splitter, ALfloat freq_mult);
|
||||
void splitterap_clear(SplitterAllpass *splitter);
|
||||
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
|
||||
|
||||
#endif /* BFORMATDEC_H */
|
||||
|
||||
-5081
File diff suppressed because it is too large
Load Diff
+9
-1
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "alstring.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
@@ -38,7 +42,7 @@ struct FileMapping {
|
||||
struct FileMapping MapFileToMem(const char *fname);
|
||||
void UnmapFileMem(const struct FileMapping *mapping);
|
||||
|
||||
al_string GetProcPath(void);
|
||||
void GetProcBinary(al_string *path, al_string *fname);
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
void *LoadLib(const char *name);
|
||||
@@ -54,4 +58,8 @@ void *GetSymbol(void *handle, const char *name);
|
||||
void *Android_GetJNIEnv(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AL_COMPAT_H */
|
||||
|
||||
+18
-16
@@ -3,7 +3,8 @@
|
||||
|
||||
#include "converter.h"
|
||||
|
||||
#include "mixer_defs.h"
|
||||
#include "fpu_modes.h"
|
||||
#include "mixer/defs.h"
|
||||
|
||||
|
||||
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
|
||||
@@ -26,15 +27,16 @@ SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType
|
||||
|
||||
/* Have to set the mixer FPU mode since that's what the resampler code expects. */
|
||||
START_MIXER_MODE();
|
||||
step = fastf2i(minf((ALdouble)srcRate / dstRate, MAX_PITCH)*FRACTIONONE + 0.5f);
|
||||
step = (ALsizei)mind(((ALdouble)srcRate/dstRate*FRACTIONONE) + 0.5,
|
||||
MAX_PITCH * FRACTIONONE);
|
||||
converter->mIncrement = maxi(step, 1);
|
||||
if(converter->mIncrement == FRACTIONONE)
|
||||
converter->mResample = Resample_copy32_C;
|
||||
converter->mResample = Resample_copy_C;
|
||||
else
|
||||
{
|
||||
/* TODO: Allow other resamplers. */
|
||||
BsincPrepare(converter->mIncrement, &converter->mState.bsinc);
|
||||
converter->mResample = SelectResampler(BSincResampler);
|
||||
BsincPrepare(converter->mIncrement, &converter->mState.bsinc, &bsinc12);
|
||||
converter->mResample = SelectResampler(BSinc12Resampler);
|
||||
}
|
||||
END_MIXER_MODE();
|
||||
|
||||
@@ -205,8 +207,8 @@ ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframe
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
|
||||
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes)
|
||||
if(prepcount < MAX_RESAMPLE_PADDING*2 &&
|
||||
MAX_RESAMPLE_PADDING*2 - prepcount >= srcframes)
|
||||
{
|
||||
/* Not enough input samples to generate an output sample. */
|
||||
return 0;
|
||||
@@ -214,7 +216,7 @@ ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframe
|
||||
|
||||
DataSize64 = prepcount;
|
||||
DataSize64 += srcframes;
|
||||
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
|
||||
DataSize64 -= MAX_RESAMPLE_PADDING*2;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
@@ -256,10 +258,10 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
converter->mSrcPrepCount = 0;
|
||||
continue;
|
||||
}
|
||||
toread = mini(*srcframes, BUFFERSIZE-(MAX_POST_SAMPLES+MAX_PRE_SAMPLES));
|
||||
toread = mini(*srcframes, BUFFERSIZE - MAX_RESAMPLE_PADDING*2);
|
||||
|
||||
if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES &&
|
||||
MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= toread)
|
||||
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.
|
||||
@@ -277,7 +279,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
|
||||
DataSize64 = prepcount;
|
||||
DataSize64 += toread;
|
||||
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
|
||||
DataSize64 -= MAX_RESAMPLE_PADDING*2;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
@@ -310,7 +312,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
sizeof(converter->Chan[chan].mPrevSamples));
|
||||
else
|
||||
{
|
||||
size_t len = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, prepcount+toread-SrcDataEnd);
|
||||
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,
|
||||
@@ -319,7 +321,7 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
|
||||
/* Now resample, and store the result in the output buffer. */
|
||||
ResampledData = converter->mResample(&converter->mState,
|
||||
SrcData+MAX_PRE_SAMPLES, DataPosFrac, increment,
|
||||
SrcData+MAX_RESAMPLE_PADDING, DataPosFrac, increment,
|
||||
DstData, DstSize
|
||||
);
|
||||
|
||||
@@ -331,8 +333,8 @@ ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALs
|
||||
* fractional offset.
|
||||
*/
|
||||
DataPosFrac += increment*DstSize;
|
||||
converter->mSrcPrepCount = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES,
|
||||
prepcount+toread-(DataPosFrac>>FRACTIONBITS));
|
||||
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. */
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ typedef struct SampleConverter {
|
||||
alignas(16) ALfloat mDstSamples[BUFFERSIZE];
|
||||
|
||||
struct {
|
||||
alignas(16) ALfloat mPrevSamples[MAX_PRE_SAMPLES+MAX_POST_SAMPLES];
|
||||
alignas(16) ALfloat mPrevSamples[MAX_RESAMPLE_PADDING*2];
|
||||
} Chan[];
|
||||
} SampleConverter;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
#define MIN_FREQ 20.0f
|
||||
#define MAX_FREQ 2500.0f
|
||||
#define Q_FACTOR 5.0f
|
||||
|
||||
typedef struct ALautowahState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect parameters */
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat ResonanceGain;
|
||||
ALfloat PeakGain;
|
||||
ALfloat FreqMinNorm;
|
||||
ALfloat BandwidthNorm;
|
||||
ALfloat env_delay;
|
||||
|
||||
/* 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 *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);
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_Destruct(ALautowahState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
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++)
|
||||
ComputeFirstOrderGains(&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;
|
||||
|
||||
NEW_OBJ0(state, ALautowahState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
|
||||
|
||||
EffectStateFactory *AutowahStateFactory_getFactory(void)
|
||||
{
|
||||
static AutowahStateFactory AutowahFactory = { { GET_VTABLE2(AutowahStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &AutowahFactory);
|
||||
}
|
||||
|
||||
void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
|
||||
props->Autowah.PeakGain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
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_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{
|
||||
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)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
*val = props->Autowah.AttackTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
*val = props->Autowah.ReleaseTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
*val = props->Autowah.Resonance;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
*val = props->Autowah.PeakGain;
|
||||
break;
|
||||
|
||||
default:
|
||||
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);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALautowah);
|
||||
+261
-116
@@ -24,32 +24,40 @@
|
||||
#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];
|
||||
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][MAX_OUTPUT_CHANNELS];
|
||||
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;
|
||||
@@ -57,7 +65,7 @@ typedef struct ALchorusState {
|
||||
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state);
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALchorusState_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)
|
||||
|
||||
@@ -70,54 +78,51 @@ static void ALchorusState_Construct(ALchorusState *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[0]);
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
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;
|
||||
ALsizei it;
|
||||
|
||||
maxlen = fastf2i(AL_CHORUS_MAX_DELAY * 2.0f * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(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) * 2);
|
||||
void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
|
||||
if(!temp) return AL_FALSE;
|
||||
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = temp;
|
||||
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
|
||||
al_free(state->SampleBuffer);
|
||||
state->SampleBuffer = temp;
|
||||
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
|
||||
for(it = 0;it < state->BufferLength;it++)
|
||||
{
|
||||
state->SampleBuffer[0][it] = 0.0f;
|
||||
state->SampleBuffer[1][it] = 0.0f;
|
||||
}
|
||||
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 ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
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;
|
||||
@@ -125,50 +130,61 @@ static ALvoid ALchorusState_update(ALchorusState *state, const ALCdevice *Device
|
||||
switch(props->Chorus.Waveform)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM_TRIANGLE:
|
||||
state->waveform = CWF_Triangle;
|
||||
state->waveform = WF_Triangle;
|
||||
break;
|
||||
case AL_CHORUS_WAVEFORM_SINUSOID:
|
||||
state->waveform = CWF_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;
|
||||
state->delay = fastf2i(props->Chorus.Delay * frequency);
|
||||
/* The LFO depth is scaled to be relative to the sample delay. */
|
||||
state->depth = props->Chorus.Depth * state->delay;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
|
||||
phase = props->Chorus.Phase;
|
||||
rate = props->Chorus.Rate;
|
||||
if(!(rate > 0.0f))
|
||||
{
|
||||
state->lfo_scale = 0.0f;
|
||||
state->lfo_offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->lfo_scale = 0.0f;
|
||||
state->lfo_disp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Calculate LFO coefficient */
|
||||
state->lfo_range = fastf2i(frequency/rate + 0.5f);
|
||||
/* 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 CWF_Triangle:
|
||||
case WF_Triangle:
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
case WF_Sinusoid:
|
||||
state->lfo_scale = F_TAU / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate lfo phase displacement */
|
||||
if(phase >= 0)
|
||||
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
|
||||
else
|
||||
state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
|
||||
if(phase < 0) phase = 360 + phase;
|
||||
state->lfo_disp = (state->lfo_range*phase + 180) / 360;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,67 +215,68 @@ static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsi
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0];
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1];
|
||||
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(128, SamplesToDo-base);
|
||||
ALfloat temps[128][2];
|
||||
ALint moddelays[2][128];
|
||||
const ALsizei todo = mini(256, SamplesToDo-base);
|
||||
ALint moddelays[2][256];
|
||||
alignas(16) ALfloat temps[2][256];
|
||||
|
||||
switch(state->waveform)
|
||||
if(state->waveform == WF_Sinusoid)
|
||||
{
|
||||
case CWF_Triangle:
|
||||
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
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++)
|
||||
{
|
||||
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
|
||||
leftbuf[offset&bufmask] += temps[i][0];
|
||||
ALint delay;
|
||||
ALfloat mu;
|
||||
|
||||
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
|
||||
rightbuf[offset&bufmask] += temps[i][1];
|
||||
// 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 < NumChannels;c++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -268,11 +285,11 @@ static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, c
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALchorusStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALchorusStateFactory;
|
||||
typedef struct ChorusStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} ChorusStateFactory;
|
||||
|
||||
static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
|
||||
static ALeffectState *ChorusStateFactory_create(ChorusStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALchorusState *state;
|
||||
|
||||
@@ -282,14 +299,14 @@ static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(f
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -300,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;
|
||||
@@ -325,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)
|
||||
{
|
||||
@@ -370,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;
|
||||
@@ -399,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);
|
||||
|
||||
+84
-94
@@ -27,6 +27,13 @@
|
||||
#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);
|
||||
|
||||
@@ -35,14 +42,14 @@ typedef struct ALcompressorState {
|
||||
|
||||
/* Effect parameters */
|
||||
ALboolean Enabled;
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat GainCtrl;
|
||||
ALfloat AttackMult;
|
||||
ALfloat ReleaseMult;
|
||||
ALfloat EnvFollower;
|
||||
} ALcompressorState;
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
|
||||
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALcompressorState_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)
|
||||
|
||||
@@ -55,9 +62,9 @@ static void ALcompressorState_Construct(ALcompressorState *state)
|
||||
SET_VTABLE2(ALcompressorState, ALeffectState, state);
|
||||
|
||||
state->Enabled = AL_TRUE;
|
||||
state->AttackRate = 0.0f;
|
||||
state->ReleaseRate = 0.0f;
|
||||
state->GainCtrl = 1.0f;
|
||||
state->AttackMult = 1.0f;
|
||||
state->ReleaseMult = 1.0f;
|
||||
state->EnvFollower = 1.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
|
||||
@@ -67,17 +74,24 @@ static ALvoid ALcompressorState_Destruct(ALcompressorState *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, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALuint i;
|
||||
|
||||
state->Enabled = props->Compressor.OnOff;
|
||||
@@ -85,7 +99,7 @@ static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCdevice
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < 4;i++)
|
||||
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
}
|
||||
|
||||
@@ -96,71 +110,52 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][4];
|
||||
ALsizei td = mini(64, SamplesToDo-base);
|
||||
|
||||
/* Load samples into the temp buffer first. */
|
||||
for(j = 0;j < 4;j++)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
temps[i][j] = SamplesIn[j][i+base];
|
||||
}
|
||||
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 gain = state->GainCtrl;
|
||||
ALfloat output, amplitude;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
for(i = 0;i < td;++i)
|
||||
{
|
||||
/* Roughly calculate the maximum amplitude from the 4-channel
|
||||
* signal, and attack or release the gain control to reach it.
|
||||
/* Clamp the absolute amplitude to the defined envelope limits,
|
||||
* then attack or release the envelope to reach it.
|
||||
*/
|
||||
amplitude = fabsf(temps[i][0]);
|
||||
amplitude = maxf(amplitude + fabsf(temps[i][1]),
|
||||
maxf(amplitude + fabsf(temps[i][2]),
|
||||
amplitude + fabsf(temps[i][3])));
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
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);
|
||||
|
||||
/* Apply the inverse of the gain control to normalize/compress
|
||||
* the volume. */
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
for(j = 0;j < 4;j++)
|
||||
temps[i][j] *= output;
|
||||
/* Apply the reciprocal of the envelope to normalize the volume
|
||||
* (compress the dynamic range).
|
||||
*/
|
||||
gains[i] = 1.0f / env;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALfloat gain = state->GainCtrl;
|
||||
ALfloat output, amplitude;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
/* 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)
|
||||
{
|
||||
/* Same as above, except the amplitude is forced to 1. This
|
||||
* helps ensure smooth gain changes when the compressor is
|
||||
* turned on and off.
|
||||
*/
|
||||
amplitude = 1.0f;
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
ALfloat amplitude = 1.0f;
|
||||
if(amplitude > env)
|
||||
env = minf(env*state->AttackMult, amplitude);
|
||||
else if(amplitude < env)
|
||||
env = maxf(env*state->ReleaseMult, amplitude);
|
||||
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
for(j = 0;j < 4;j++)
|
||||
temps[i][j] *= output;
|
||||
gains[i] = 1.0f / env;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
state->EnvFollower = env;
|
||||
|
||||
/* Now mix to the output. */
|
||||
for(j = 0;j < 4;j++)
|
||||
/* Now compress the signal amplitude to output. */
|
||||
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
|
||||
{
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
@@ -169,7 +164,7 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
|
||||
continue;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][base+i] += gain * temps[i][j];
|
||||
SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,11 +173,11 @@ static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei Sample
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALcompressorStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALcompressorStateFactory;
|
||||
typedef struct CompressorStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} CompressorStateFactory;
|
||||
|
||||
static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
|
||||
static ALeffectState *CompressorStateFactory_create(CompressorStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALcompressorState *state;
|
||||
|
||||
@@ -192,13 +187,13 @@ static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,24 +204,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)
|
||||
{
|
||||
@@ -236,19 +228,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);
|
||||
|
||||
+43
-62
@@ -23,21 +23,22 @@
|
||||
#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[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} ALdedicatedState;
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALdedicatedState_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)
|
||||
|
||||
@@ -46,13 +47,8 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
|
||||
|
||||
static void ALdedicatedState_Construct(ALdedicatedState *state)
|
||||
{
|
||||
ALsizei s;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
|
||||
|
||||
for(s = 0;s < MAX_OUTPUT_CHANNELS;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
|
||||
@@ -60,40 +56,44 @@ static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
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 ALCdevice *device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat Gain;
|
||||
ALuint i;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
state->gains[i] = 0.0f;
|
||||
state->TargetGains[i] = 0.0f;
|
||||
|
||||
Gain = Slot->Params.Gain * props->Dedicated.Gain;
|
||||
if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
Gain = slot->Params.Gain * props->Dedicated.Gain;
|
||||
if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
{
|
||||
int idx;
|
||||
if((idx=GetChannelIdxByName(device->RealOut, LFE)) != -1)
|
||||
if((idx=GetChannelIdxByName(&device->RealOut, LFE)) != -1)
|
||||
{
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
|
||||
state->gains[idx] = Gain;
|
||||
state->TargetGains[idx] = Gain;
|
||||
}
|
||||
}
|
||||
else if(Slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
{
|
||||
int idx;
|
||||
/* Dialog goes to the front-center speaker if it exists, otherwise it
|
||||
* plays from the front-center location. */
|
||||
if((idx=GetChannelIdxByName(device->RealOut, FrontCenter)) != -1)
|
||||
if((idx=GetChannelIdxByName(&device->RealOut, FrontCenter)) != -1)
|
||||
{
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
|
||||
state->gains[idx] = Gain;
|
||||
state->TargetGains[idx] = Gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -102,34 +102,23 @@ static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCdevice *
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
|
||||
ComputePanningGains(device->Dry, coeffs, Gain, state->gains);
|
||||
ComputeDryPanGains(&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)
|
||||
{
|
||||
ALsizei i, c;
|
||||
|
||||
SamplesIn = ASSUME_ALIGNED(SamplesIn, 16);
|
||||
SamplesOut = ASSUME_ALIGNED(SamplesOut, 16);
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
const ALfloat gain = state->gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
SamplesOut[c][i] += SamplesIn[0][i] * gain;
|
||||
}
|
||||
MixSamples(SamplesIn[0], NumChannels, SamplesOut, state->CurrentGains,
|
||||
state->TargetGains, SamplesToDo, 0, SamplesToDo);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALdedicatedStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALdedicatedStateFactory;
|
||||
typedef struct DedicatedStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} DedicatedStateFactory;
|
||||
|
||||
ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
|
||||
ALeffectState *DedicatedStateFactory_create(DedicatedStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdedicatedState *state;
|
||||
|
||||
@@ -139,23 +128,21 @@ ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(fa
|
||||
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;
|
||||
@@ -163,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;
|
||||
@@ -192,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);
|
||||
|
||||
+65
-75
@@ -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"
|
||||
|
||||
|
||||
typedef struct ALdistortionState {
|
||||
@@ -37,15 +37,17 @@ typedef struct ALdistortionState {
|
||||
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 *state);
|
||||
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALdistortionState_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)
|
||||
|
||||
@@ -56,9 +58,6 @@ static void ALdistortionState_Construct(ALdistortionState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdistortionState, ALeffectState, state);
|
||||
|
||||
ALfilterState_clear(&state->lowpass);
|
||||
ALfilterState_clear(&state->bandpass);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
|
||||
@@ -66,21 +65,22 @@ static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
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 ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat bandwidth;
|
||||
ALfloat cutoff;
|
||||
ALfloat edge;
|
||||
|
||||
/* Store distorted signal attenuation settings. */
|
||||
state->attenuation = props->Distortion.Gain;
|
||||
|
||||
/* Store waveshaper edge settings. */
|
||||
edge = sinf(props->Distortion.Edge * (F_PI_2));
|
||||
edge = minf(edge, 0.99f);
|
||||
@@ -92,98 +92,94 @@ static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCdevice
|
||||
/* Multiply sampling frequency by the amount of oversampling done during
|
||||
* processing.
|
||||
*/
|
||||
ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
|
||||
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);
|
||||
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
|
||||
BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
ComputeAmbientGains(Device->Dry, Slot->Params.Gain, state->Gain);
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputeDryPanGains(&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 it, kt;
|
||||
ALsizei base;
|
||||
ALsizei i, k;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
float buffer[2][64 * 4];
|
||||
ALsizei td = mini(64, SamplesToDo-base);
|
||||
|
||||
/* Perform 4x oversampling to avoid aliasing. Oversampling greatly
|
||||
* improves distortion quality and allows to implement lowpass and
|
||||
* bandpass filters using high frequencies, at which classic IIR
|
||||
* filters became unstable.
|
||||
*/
|
||||
ALsizei todo = mini(BUFFERSIZE, (SamplesToDo-base) * 4);
|
||||
|
||||
/* Fill oversample buffer using zero stuffing. */
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
/* Multiply the sample by the amount of oversampling to maintain
|
||||
* the signal's power.
|
||||
*/
|
||||
buffer[0][it*4 + 0] = SamplesIn[0][it+base] * 4.0f;
|
||||
buffer[0][it*4 + 1] = 0.0f;
|
||||
buffer[0][it*4 + 2] = 0.0f;
|
||||
buffer[0][it*4 + 3] = 0.0f;
|
||||
}
|
||||
/* 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.
|
||||
*/
|
||||
ALfilterState_process(&state->lowpass, buffer[1], buffer[0], td*4);
|
||||
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(it = 0;it < td*4;it++)
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat smp = buffer[1][it];
|
||||
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][it] = smp;
|
||||
buffer[0][i] = smp;
|
||||
}
|
||||
|
||||
/* Third step, do bandpass filtering of distorted signal. */
|
||||
ALfilterState_process(&state->bandpass, buffer[1], buffer[0], td*4);
|
||||
BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
|
||||
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
todo >>= 2;
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
/* Fourth step, final, do attenuation and perform decimation,
|
||||
* store only one sample out of 4.
|
||||
* storing only one sample out of four.
|
||||
*/
|
||||
ALfloat gain = state->Gain[kt] * state->attenuation;
|
||||
ALfloat gain = state->Gain[k];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * buffer[1][it*4];
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[k][base+i] += gain * buffer[1][i*4];
|
||||
}
|
||||
|
||||
base += td;
|
||||
base += todo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALdistortionStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALdistortionStateFactory;
|
||||
typedef struct DistortionStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} DistortionStateFactory;
|
||||
|
||||
static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
|
||||
static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdistortionState *state;
|
||||
|
||||
@@ -193,23 +189,21 @@ static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *
|
||||
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 +211,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 +277,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);
|
||||
|
||||
+79
-95
@@ -28,6 +28,7 @@
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
typedef struct ALechoState {
|
||||
@@ -42,17 +43,21 @@ typedef struct ALechoState {
|
||||
ALsizei delay;
|
||||
} Tap[2];
|
||||
ALsizei Offset;
|
||||
|
||||
/* The panning gains for the two taps */
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
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);
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALechoState_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)
|
||||
|
||||
@@ -71,7 +76,7 @@ static void ALechoState_Construct(ALechoState *state)
|
||||
state->Tap[1].delay = 0;
|
||||
state->Offset = 0;
|
||||
|
||||
ALfilterState_clear(&state->Filter);
|
||||
BiquadFilter_clear(&state->Filter);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
@@ -83,13 +88,14 @@ static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
{
|
||||
ALsizei maxlen, i;
|
||||
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 = fastf2i(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
|
||||
maxlen += fastf2i(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
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)
|
||||
{
|
||||
@@ -100,20 +106,22 @@ static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
state->SampleBuffer = temp;
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
for(i = 0;i < state->BufferLength;i++)
|
||||
state->SampleBuffer[i] = 0.0f;
|
||||
|
||||
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 ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
ALuint frequency = Device->Frequency;
|
||||
const ALCdevice *device = context->Device;
|
||||
ALuint frequency = device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat gain, lrpan, spread;
|
||||
ALfloat gainhf, lrpan, spread;
|
||||
|
||||
state->Tap[0].delay = fastf2i(props->Echo.Delay * frequency) + 1;
|
||||
state->Tap[1].delay = fastf2i(props->Echo.LRDelay * frequency);
|
||||
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;
|
||||
@@ -126,20 +134,18 @@ static ALvoid ALechoState_update(ALechoState *state, const ALCdevice *Device, co
|
||||
|
||||
state->FeedGain = props->Echo.Feedback;
|
||||
|
||||
gain = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
|
||||
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
|
||||
gain, LOWPASSFREQREF/frequency,
|
||||
calc_rcpQ_from_slope(gain, 1.0f));
|
||||
|
||||
gain = Slot->Params.Gain;
|
||||
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);
|
||||
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[0]);
|
||||
ComputeDryPanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
|
||||
/* Second tap panning */
|
||||
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, gain, state->Gain[1]);
|
||||
ComputeDryPanGains(&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)
|
||||
@@ -147,73 +153,59 @@ static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const
|
||||
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 x[2], y[2], in, out;
|
||||
ALsizei base, k;
|
||||
ALsizei i;
|
||||
ALfloat z1, z2, in, out;
|
||||
ALsizei base;
|
||||
ALsizei c, i;
|
||||
|
||||
x[0] = state->Filter.x[0];
|
||||
x[1] = state->Filter.x[1];
|
||||
y[0] = state->Filter.y[0];
|
||||
y[1] = state->Filter.y[1];
|
||||
z1 = state->Filter.z1;
|
||||
z2 = state->Filter.z2;
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[128][2];
|
||||
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[i][0] = state->SampleBuffer[(offset-tap1) & mask];
|
||||
temps[0][i] = delaybuf[(offset-tap1) & mask];
|
||||
/* Second tap */
|
||||
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
|
||||
temps[1][i] = delaybuf[(offset-tap2) & mask];
|
||||
|
||||
// Apply damping and feedback gain to the second tap, and mix in the
|
||||
// new sample
|
||||
in = temps[i][1] + SamplesIn[0][i+base];
|
||||
out = in*state->Filter.b0 +
|
||||
x[0]*state->Filter.b1 + x[1]*state->Filter.b2 -
|
||||
y[0]*state->Filter.a1 - y[1]*state->Filter.a2;
|
||||
x[1] = x[0]; x[0] = in;
|
||||
y[1] = y[0]; y[0] = out;
|
||||
/* 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;
|
||||
|
||||
state->SampleBuffer[offset&mask] = out * state->FeedGain;
|
||||
delaybuf[offset&mask] += out * state->FeedGain;
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][k];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][k];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
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.x[0] = x[0];
|
||||
state->Filter.x[1] = x[1];
|
||||
state->Filter.y[0] = y[0];
|
||||
state->Filter.y[1] = y[1];
|
||||
state->Filter.z1 = z1;
|
||||
state->Filter.z2 = z2;
|
||||
|
||||
state->Offset = offset;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALechoStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALechoStateFactory;
|
||||
typedef struct EchoStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} EchoStateFactory;
|
||||
|
||||
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
|
||||
ALeffectState *EchoStateFactory_create(EchoStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALechoState *state;
|
||||
|
||||
@@ -223,22 +215,20 @@ ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
|
||||
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;
|
||||
@@ -246,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;
|
||||
@@ -315,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);
|
||||
|
||||
+88
-113
@@ -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 *
|
||||
@@ -72,24 +72,24 @@
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
|
||||
|
||||
|
||||
/* The maximum number of sample frames per update. */
|
||||
#define MAX_UPDATE_SAMPLES 256
|
||||
|
||||
typedef struct ALequalizerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
|
||||
struct {
|
||||
/* Effect gains for each channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState filter[4][MAX_EFFECT_CHANNELS];
|
||||
/* Effect parameters */
|
||||
BiquadFilter filter[4];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
|
||||
ALfloat SampleBuffer[4][MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES];
|
||||
ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
} ALequalizerState;
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALequalizerState_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)
|
||||
|
||||
@@ -98,18 +98,8 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
|
||||
|
||||
static void ALequalizerState_Construct(ALequalizerState *state)
|
||||
{
|
||||
int it, ft;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALequalizerState, ALeffectState, state);
|
||||
|
||||
/* Initialize sample history only on filter creation to avoid */
|
||||
/* sound clicks if filter settings were changed in runtime. */
|
||||
for(it = 0; it < 4; it++)
|
||||
{
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_clear(&state->filter[it][ft]);
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
|
||||
@@ -117,107 +107,100 @@ static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
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 ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
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, freq_mult;
|
||||
ALfloat gain, f0norm;
|
||||
ALuint i;
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Gain[i]);
|
||||
ComputeFirstOrderGains(&device->FOAOut, IdentityMatrixf.m[i],
|
||||
slot->Params.Gain, state->Chans[i].TargetGains);
|
||||
|
||||
/* Calculate coefficients for the each type of filter. Note that the shelf
|
||||
* filters' gain is for the reference frequency, which is the centerpoint
|
||||
* of the transition band.
|
||||
*/
|
||||
gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
|
||||
freq_mult = props->Equalizer.LowCutoff/frequency;
|
||||
ALfilterState_setParams(&state->filter[0][0], ALfilterType_LowShelf,
|
||||
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
f0norm = props->Equalizer.LowCutoff/frequency;
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
|
||||
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++)
|
||||
ALfilterState_copyParams(&state->filter[0][i], &state->filter[0][0]);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
|
||||
freq_mult = props->Equalizer.Mid1Center/frequency;
|
||||
ALfilterState_setParams(&state->filter[1][0], ALfilterType_Peaking,
|
||||
gain, freq_mult, calc_rcpQ_from_bandwidth(
|
||||
freq_mult, props->Equalizer.Mid1Width
|
||||
f0norm = props->Equalizer.Mid1Center/frequency;
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(
|
||||
f0norm, props->Equalizer.Mid1Width
|
||||
)
|
||||
);
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[1][i], &state->filter[1][0]);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
|
||||
freq_mult = props->Equalizer.Mid2Center/frequency;
|
||||
ALfilterState_setParams(&state->filter[2][0], ALfilterType_Peaking,
|
||||
gain, freq_mult, calc_rcpQ_from_bandwidth(
|
||||
freq_mult, props->Equalizer.Mid2Width
|
||||
f0norm = props->Equalizer.Mid2Center/frequency;
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(
|
||||
f0norm, props->Equalizer.Mid2Width
|
||||
)
|
||||
);
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_copyParams(&state->filter[2][i], &state->filter[2][0]);
|
||||
|
||||
gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
|
||||
freq_mult = props->Equalizer.HighCutoff/frequency;
|
||||
ALfilterState_setParams(&state->filter[3][0], ALfilterType_HighShelf,
|
||||
gain, freq_mult, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
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++)
|
||||
ALfilterState_copyParams(&state->filter[3][i], &state->filter[3][0]);
|
||||
{
|
||||
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 ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat (*Samples)[MAX_EFFECT_CHANNELS][MAX_UPDATE_SAMPLES] = state->SampleBuffer;
|
||||
ALsizei it, kt, ft;
|
||||
ALsizei base;
|
||||
ALfloat (*restrict temps)[BUFFERSIZE] = state->SampleBuffer;
|
||||
ALsizei c;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
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);
|
||||
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[0][ft], Samples[0][ft], &SamplesIn[ft][base], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[1][ft], Samples[1][ft], Samples[0][ft], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[2][ft], Samples[2][ft], Samples[1][ft], td);
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
ALfilterState_process(&state->filter[3][ft], Samples[3][ft], Samples[2][ft], td);
|
||||
|
||||
for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++)
|
||||
{
|
||||
for(kt = 0;kt < NumChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[ft][kt];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * Samples[3][ft][it];
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
MixSamples(temps[3], NumChannels, SamplesOut,
|
||||
state->Chans[c].CurrentGains, state->Chans[c].TargetGains,
|
||||
SamplesToDo, 0, SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALequalizerStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALequalizerStateFactory;
|
||||
typedef struct EqualizerStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} EqualizerStateFactory;
|
||||
|
||||
ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
|
||||
ALeffectState *EqualizerStateFactory_create(EqualizerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALequalizerState *state;
|
||||
|
||||
@@ -227,22 +210,20 @@ ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(fa
|
||||
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;
|
||||
@@ -250,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;
|
||||
@@ -369,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);
|
||||
|
||||
@@ -1,408 +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.,
|
||||
* 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 "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];
|
||||
ALsizei BufferLength;
|
||||
ALsizei offset;
|
||||
ALsizei lfo_range;
|
||||
ALfloat lfo_scale;
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ALfloat Gain[2][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* effect parameters */
|
||||
enum FlangerWaveForm waveform;
|
||||
ALint delay;
|
||||
ALfloat depth;
|
||||
ALfloat feedback;
|
||||
} ALflangerState;
|
||||
|
||||
static ALvoid ALflangerState_Destruct(ALflangerState *state);
|
||||
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device);
|
||||
static ALvoid ALflangerState_update(ALflangerState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
|
||||
|
||||
|
||||
static void ALflangerState_Construct(ALflangerState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALflangerState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = FWF_Triangle;
|
||||
}
|
||||
|
||||
static ALvoid ALflangerState_Destruct(ALflangerState *state)
|
||||
{
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
|
||||
{
|
||||
ALsizei maxlen;
|
||||
ALsizei it;
|
||||
|
||||
maxlen = fastf2i(AL_FLANGER_MAX_DELAY * 2.0f * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp = al_calloc(16, maxlen * sizeof(ALfloat) * 2);
|
||||
if(!temp) return AL_FALSE;
|
||||
|
||||
al_free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = temp;
|
||||
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
|
||||
|
||||
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, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
|
||||
switch(props->Flanger.Waveform)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM_TRIANGLE:
|
||||
state->waveform = FWF_Triangle;
|
||||
break;
|
||||
case AL_FLANGER_WAVEFORM_SINUSOID:
|
||||
state->waveform = FWF_Sinusoid;
|
||||
break;
|
||||
}
|
||||
state->feedback = props->Flanger.Feedback;
|
||||
state->delay = fastf2i(props->Flanger.Delay * frequency);
|
||||
/* The LFO depth is scaled to be relative to the sample delay. */
|
||||
state->depth = props->Flanger.Depth * state->delay;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[0]);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanningGains(Device->Dry, coeffs, Slot->Params.Gain, state->Gain[1]);
|
||||
|
||||
phase = props->Flanger.Phase;
|
||||
rate = props->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 = fastf2i(frequency/rate + 0.5f);
|
||||
switch(state->waveform)
|
||||
{
|
||||
case FWF_Triangle:
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case FWF_Sinusoid:
|
||||
state->lfo_scale = F_TAU / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate lfo phase displacement */
|
||||
if(phase >= 0)
|
||||
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
|
||||
else
|
||||
state->lfo_disp = fastf2i(state->lfo_range * ((360+phase)/360.0f));
|
||||
}
|
||||
}
|
||||
|
||||
static 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 ALflangerState_process(ALflangerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0];
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1];
|
||||
const ALsizei bufmask = state->BufferLength-1;
|
||||
const ALfloat feedback = state->feedback;
|
||||
ALsizei offset = state->offset;
|
||||
ALsizei i, c;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
const ALsizei todo = mini(128, SamplesToDo-base);
|
||||
ALfloat temps[128][2];
|
||||
ALint moddelays[2][128];
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
case FWF_Triangle:
|
||||
GetTriangleDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetTriangleDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
case FWF_Sinusoid:
|
||||
GetSinusoidDelays(moddelays[0], offset%state->lfo_range, state->lfo_range,
|
||||
state->lfo_scale, state->depth, state->delay, todo);
|
||||
GetSinusoidDelays(moddelays[1], (offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
break;
|
||||
}
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
leftbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][0] = leftbuf[(offset-moddelays[0][i])&bufmask] * feedback;
|
||||
leftbuf[offset&bufmask] += temps[i][0];
|
||||
|
||||
rightbuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
temps[i][1] = rightbuf[(offset-moddelays[1][i])&bufmask] * feedback;
|
||||
rightbuf[offset&bufmask] += temps[i][1];
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(c = 0;c < NumChannels;c++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][c];
|
||||
if(fabsf(gain) > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[c][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += todo;
|
||||
}
|
||||
|
||||
state->offset = offset;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALflangerStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALflangerStateFactory;
|
||||
|
||||
ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALflangerState *state;
|
||||
|
||||
NEW_OBJ0(state, ALflangerState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
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);
|
||||
@@ -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);
|
||||
ComputeDryPanGains(&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);
|
||||
+87
-91
@@ -24,28 +24,33 @@
|
||||
#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);
|
||||
|
||||
void (*Process)(ALfloat*, const ALfloat*, ALsizei, const ALsizei, ALsizei);
|
||||
void (*GetSamples)(ALfloat*, ALsizei, const ALsizei, ALsizei);
|
||||
|
||||
ALsizei index;
|
||||
ALsizei step;
|
||||
|
||||
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
|
||||
struct {
|
||||
BiquadFilter Filter;
|
||||
|
||||
ALfilterState Filter[MAX_EFFECT_CHANNELS];
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
} ALmodulatorState;
|
||||
|
||||
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
|
||||
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALmodulatorState_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)
|
||||
|
||||
@@ -58,51 +63,52 @@ DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
|
||||
|
||||
static inline ALfloat Sin(ALsizei index)
|
||||
{
|
||||
return sinf(index*(F_TAU/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
|
||||
return sinf((ALfloat)index * (F_TAU / WAVEFORM_FRACONE));
|
||||
}
|
||||
|
||||
static inline ALfloat Saw(ALsizei index)
|
||||
{
|
||||
return (ALfloat)index / WAVEFORM_FRACONE;
|
||||
return (ALfloat)index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
|
||||
}
|
||||
|
||||
static inline ALfloat Square(ALsizei index)
|
||||
{
|
||||
return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
|
||||
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, const ALfloat *restrict src,\
|
||||
ALsizei index, const ALsizei step, ALsizei todo) \
|
||||
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] = src[i] * func(index); \
|
||||
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)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
|
||||
|
||||
state->index = 0;
|
||||
state->step = 1;
|
||||
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ALfilterState_clear(&state->Filter[i]);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
|
||||
@@ -110,91 +116,89 @@ static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
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 ALCdevice *Device, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
ALfloat cw, a;
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat f0norm;
|
||||
ALsizei i;
|
||||
|
||||
if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
state->Process = ModulateSin;
|
||||
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->Process = ModulateSaw;
|
||||
state->GetSamples = ModulateSaw;
|
||||
else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
|
||||
state->Process = ModulateSquare;
|
||||
state->GetSamples = ModulateSquare;
|
||||
|
||||
state->step = fastf2i(props->Modulator.Frequency*WAVEFORM_FRACONE /
|
||||
Device->Frequency);
|
||||
if(state->step == 0) state->step = 1;
|
||||
|
||||
/* Custom filter coeffs, which match the old version instead of a low-shelf. */
|
||||
cw = cosf(F_TAU * props->Modulator.HighPassCutoff / Device->Frequency);
|
||||
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
|
||||
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++)
|
||||
{
|
||||
state->Filter[i].b0 = a;
|
||||
state->Filter[i].b1 = -a;
|
||||
state->Filter[i].b2 = 0.0f;
|
||||
state->Filter[i].a1 = -a;
|
||||
state->Filter[i].a2 = 0.0f;
|
||||
}
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = Device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = Device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputeFirstOrderGains(Device->FOAOut, IdentityMatrixf.m[i],
|
||||
Slot->Params.Gain, state->Gain[i]);
|
||||
ComputeFirstOrderGains(&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 index = state->index;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[2][128];
|
||||
ALsizei td = mini(128, SamplesToDo-base);
|
||||
ALsizei i, j, k;
|
||||
alignas(16) ALfloat modsamples[MAX_UPDATE_SAMPLES];
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
ALsizei c, i;
|
||||
|
||||
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
|
||||
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++)
|
||||
{
|
||||
ALfilterState_process(&state->Filter[j], temps[0], &SamplesIn[j][base], td);
|
||||
state->Process(temps[1], temps[0], index, step, td);
|
||||
alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
|
||||
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[j][k];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
BiquadFilter_process(&state->Chans[c].Filter, temps, &SamplesIn[c][base], td);
|
||||
for(i = 0;i < td;i++)
|
||||
temps[i] *= modsamples[i];
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][base+i] += gain * temps[1][i];
|
||||
}
|
||||
MixSamples(temps, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
|
||||
state->Chans[c].TargetGains, SamplesToDo-base, base, td);
|
||||
}
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
index += step;
|
||||
index &= WAVEFORM_FRACMASK;
|
||||
}
|
||||
base += td;
|
||||
}
|
||||
state->index = index;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALmodulatorStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALmodulatorStateFactory;
|
||||
typedef struct ModulatorStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} ModulatorStateFactory;
|
||||
|
||||
static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
|
||||
static ALeffectState *ModulatorStateFactory_create(ModulatorStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALmodulatorState *state;
|
||||
|
||||
@@ -204,13 +208,13 @@ static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UN
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -221,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;
|
||||
@@ -251,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)
|
||||
{
|
||||
@@ -280,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;
|
||||
@@ -300,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);
|
||||
|
||||
+37
-37
@@ -16,8 +16,8 @@ typedef struct ALnullState {
|
||||
/* Forward-declare "virtual" functions to define the vtable with. */
|
||||
static ALvoid ALnullState_Destruct(ALnullState *state);
|
||||
static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device);
|
||||
static ALvoid ALnullState_update(ALnullState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloatBUFFERSIZE*restrict samplesIn, ALfloatBUFFERSIZE*restrict samplesOut, ALsizei NumChannels);
|
||||
static 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);
|
||||
|
||||
@@ -56,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), const ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
|
||||
static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCcontext* UNUSED(context), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCdevice* UN
|
||||
* input to the output buffer. The result should be added to the output buffer,
|
||||
* not replace it.
|
||||
*/
|
||||
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(NumChannels))
|
||||
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(numChannels))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -85,12 +85,12 @@ static void ALnullState_Delete(void *ptr)
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -100,79 +100,79 @@ ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
ComputeDryPanGains(&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);
|
||||
+886
-1235
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
+189
-181
@@ -1,9 +1,10 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "nfcfilter.h"
|
||||
#include "nfc.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#include "alu.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Near-field control filters are the basis for handling the near-field effect.
|
||||
@@ -52,35 +53,33 @@ static const float B[4][3] = {
|
||||
/*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
|
||||
};
|
||||
|
||||
void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1)
|
||||
static void NfcFilterCreate1(struct NfcFilter1 *nfc, const float w0, const float w1)
|
||||
{
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
memset(nfc, 0, sizeof(*nfc));
|
||||
|
||||
nfc->g = 1.0f;
|
||||
nfc->coeffs[0] = 1.0f;
|
||||
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->coeffs[0] *= g_0;
|
||||
nfc->coeffs[1] = (2.0f * b_00) / g_0;
|
||||
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->g /= g_0;
|
||||
nfc->coeffs[0] /= g_0;
|
||||
nfc->coeffs[1+1] = (2.0f * b_00) / g_0;
|
||||
nfc->base_gain /= g_0;
|
||||
nfc->gain /= g_0;
|
||||
nfc->a1 = 2.0f * b_00 / g_0;
|
||||
}
|
||||
|
||||
void NfcFilterAdjust1(NfcFilter *nfc, const float w0)
|
||||
static void NfcFilterAdjust1(struct NfcFilter1 *nfc, const float w0)
|
||||
{
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
@@ -89,212 +88,221 @@ void NfcFilterAdjust1(NfcFilter *nfc, const float w0)
|
||||
b_00 = B[1][0] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->coeffs[0] = nfc->g * g_0;
|
||||
nfc->coeffs[1] = (2.0f * b_00) / g_0;
|
||||
nfc->gain = nfc->base_gain * g_0;
|
||||
nfc->b1 = 2.0f * b_00 / g_0;
|
||||
}
|
||||
|
||||
void NfcFilterUpdate1(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
|
||||
|
||||
static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float w1)
|
||||
{
|
||||
const float b0 = nfc->coeffs[0];
|
||||
const float a0 = nfc->coeffs[1];
|
||||
const float a1 = nfc->coeffs[2];
|
||||
float z1 = nfc->history[0];
|
||||
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 out = src[i] * b0;
|
||||
float y;
|
||||
|
||||
y = out - (a1*z1);
|
||||
out = y + (a0*z1);
|
||||
float y = src[i]*gain - a1*z1;
|
||||
float out = y + b1*z1;
|
||||
z1 += y;
|
||||
|
||||
dst[i] = out;
|
||||
}
|
||||
nfc->history[0] = z1;
|
||||
nfc->first.z[0] = z1;
|
||||
}
|
||||
|
||||
|
||||
void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1)
|
||||
void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
float b_10, b_11, g_1;
|
||||
float r;
|
||||
|
||||
memset(nfc, 0, sizeof(*nfc));
|
||||
|
||||
nfc->g = 1.0f;
|
||||
nfc->coeffs[0] = 1.0f;
|
||||
|
||||
/* Calculate bass-boost coefficients. */
|
||||
r = 0.5f * w0;
|
||||
b_10 = B[2][0] * r;
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] *= g_1;
|
||||
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2] = (4.0f * b_11) / g_1;
|
||||
|
||||
/* Calculate bass-cut coefficients. */
|
||||
r = 0.5f * w1;
|
||||
b_10 = B[2][0] * r;
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->g /= g_1;
|
||||
nfc->coeffs[0] /= g_1;
|
||||
nfc->coeffs[2+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2+2] = (4.0f * b_11) / g_1;
|
||||
}
|
||||
|
||||
void NfcFilterAdjust2(NfcFilter *nfc, const float w0)
|
||||
{
|
||||
float b_10, b_11, g_1;
|
||||
float r;
|
||||
|
||||
r = 0.5f * w0;
|
||||
b_10 = B[2][0] * r;
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] = nfc->g * g_1;
|
||||
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2] = (4.0f * b_11) / g_1;
|
||||
}
|
||||
|
||||
void NfcFilterUpdate2(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
const float b0 = nfc->coeffs[0];
|
||||
const float a00 = nfc->coeffs[1];
|
||||
const float a01 = nfc->coeffs[2];
|
||||
const float a10 = nfc->coeffs[3];
|
||||
const float a11 = nfc->coeffs[4];
|
||||
float z1 = nfc->history[0];
|
||||
float z2 = nfc->history[1];
|
||||
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 out = src[i] * b0;
|
||||
float y;
|
||||
|
||||
y = out - (a10*z1) - (a11*z2);
|
||||
out = y + (a00*z1) + (a01*z2);
|
||||
float y = src[i]*gain - a1*z1 - a2*z2;
|
||||
float out = y + b1*z1 + b2*z2;
|
||||
z2 += z1;
|
||||
z1 += y;
|
||||
|
||||
dst[i] = out;
|
||||
}
|
||||
nfc->history[0] = z1;
|
||||
nfc->history[1] = z2;
|
||||
nfc->second.z[0] = z1;
|
||||
nfc->second.z[1] = z2;
|
||||
}
|
||||
|
||||
|
||||
void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1)
|
||||
void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
float b_10, b_11, g_1;
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
memset(nfc, 0, sizeof(*nfc));
|
||||
|
||||
nfc->g = 1.0f;
|
||||
nfc->coeffs[0] = 1.0f;
|
||||
|
||||
/* Calculate bass-boost coefficients. */
|
||||
r = 0.5f * w0;
|
||||
b_10 = B[3][0] * r;
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] *= g_1;
|
||||
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2] = (4.0f * b_11) / g_1;
|
||||
|
||||
b_00 = B[3][2] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->coeffs[0] *= g_0;
|
||||
nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
|
||||
|
||||
/* Calculate bass-cut coefficients. */
|
||||
r = 0.5f * w1;
|
||||
b_10 = B[3][0] * r;
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->g /= g_1;
|
||||
nfc->coeffs[0] /= g_1;
|
||||
nfc->coeffs[3+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[3+2] = (4.0f * b_11) / g_1;
|
||||
|
||||
b_00 = B[3][2] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->g /= g_0;
|
||||
nfc->coeffs[0] /= g_0;
|
||||
nfc->coeffs[3+2+1] = (2.0f * b_00) / g_0;
|
||||
}
|
||||
|
||||
void NfcFilterAdjust3(NfcFilter *nfc, const float w0)
|
||||
{
|
||||
float b_10, b_11, g_1;
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
r = 0.5f * w0;
|
||||
b_10 = B[3][0] * r;
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] = nfc->g * g_1;
|
||||
nfc->coeffs[1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[2] = (4.0f * b_11) / g_1;
|
||||
|
||||
b_00 = B[3][2] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->coeffs[0] *= g_0;
|
||||
nfc->coeffs[2+1] = (2.0f * b_00) / g_0;
|
||||
}
|
||||
|
||||
void NfcFilterUpdate3(NfcFilter *nfc, ALfloat *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
const float b0 = nfc->coeffs[0];
|
||||
const float a00 = nfc->coeffs[1];
|
||||
const float a01 = nfc->coeffs[2];
|
||||
const float a02 = nfc->coeffs[3];
|
||||
const float a10 = nfc->coeffs[4];
|
||||
const float a11 = nfc->coeffs[5];
|
||||
const float a12 = nfc->coeffs[6];
|
||||
float z1 = nfc->history[0];
|
||||
float z2 = nfc->history[1];
|
||||
float z3 = nfc->history[2];
|
||||
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 out = src[i] * b0;
|
||||
float y;
|
||||
|
||||
y = out - (a10*z1) - (a11*z2);
|
||||
out = y + (a00*z1) + (a01*z2);
|
||||
float y = src[i]*gain - a1*z1 - a2*z2;
|
||||
float out = y + b1*z1 + b2*z2;
|
||||
z2 += z1;
|
||||
z1 += y;
|
||||
|
||||
y = out - (a12*z3);
|
||||
out = y + (a02*z3);
|
||||
y = out - a3*z3;
|
||||
out = y + b3*z3;
|
||||
z3 += y;
|
||||
|
||||
dst[i] = out;
|
||||
}
|
||||
nfc->history[0] = z1;
|
||||
nfc->history[1] = z2;
|
||||
nfc->history[2] = z3;
|
||||
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)
|
||||
{
|
||||
@@ -391,7 +399,7 @@ static void NfcFilterAdjust(NfcFilter *nfc, const float distance)
|
||||
}
|
||||
}
|
||||
|
||||
static float NfcFilterUpdate(const float in, NfcFilter *nfc)
|
||||
static float NfcFilterProcess(const float in, NfcFilter *nfc)
|
||||
{
|
||||
int i;
|
||||
float out = in * nfc->coeffs[0];
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
+225
-201
@@ -39,6 +39,9 @@
|
||||
#ifdef HAVE_DIRENT_H
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
#ifdef HAVE_PROC_PIDPATH
|
||||
#include <libproc.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
@@ -66,7 +69,7 @@ DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc
|
||||
DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
|
||||
DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
|
||||
|
||||
#ifdef HAVE_MMDEVAPI
|
||||
#ifdef HAVE_WASAPI
|
||||
#include <wtypes.h>
|
||||
#include <devpropdef.h>
|
||||
#include <propkeydef.h>
|
||||
@@ -108,6 +111,8 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "cpu_caps.h"
|
||||
#include "fpu_modes.h"
|
||||
#include "atomic.h"
|
||||
#include "uintmap.h"
|
||||
#include "vector.h"
|
||||
@@ -118,73 +123,50 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x
|
||||
|
||||
extern inline ALuint NextPowerOf2(ALuint value);
|
||||
extern inline size_t RoundUp(size_t value, size_t r);
|
||||
extern inline ALuint64 ScaleRound(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
|
||||
extern inline ALuint64 ScaleFloor(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
|
||||
extern inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale);
|
||||
extern inline ALint fastf2i(ALfloat f);
|
||||
extern inline int float2int(float f);
|
||||
extern inline float fast_roundf(float f);
|
||||
#ifndef __GNUC__
|
||||
#if defined(HAVE_BITSCANFORWARD64_INTRINSIC)
|
||||
extern inline int msvc64_ctz64(ALuint64 v);
|
||||
#elif defined(HAVE_BITSCANFORWARD_INTRINSIC)
|
||||
extern inline int msvc_ctz64(ALuint64 v);
|
||||
#else
|
||||
extern inline int fallback_popcnt64(ALuint64 v);
|
||||
extern inline int fallback_ctz64(ALuint64 value);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
ALuint CPUCapFlags = 0;
|
||||
#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
|
||||
defined(_M_IX86) || defined(_M_X64))
|
||||
typedef unsigned int reg_type;
|
||||
static inline void get_cpuid(int f, reg_type *regs)
|
||||
{ __get_cpuid(f, ®s[0], ®s[1], ®s[2], ®s[3]); }
|
||||
#define CAN_GET_CPUID
|
||||
#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
|
||||
defined(_M_IX86) || defined(_M_X64))
|
||||
typedef int reg_type;
|
||||
static inline void get_cpuid(int f, reg_type *regs)
|
||||
{ (__cpuid)(regs, f); }
|
||||
#define CAN_GET_CPUID
|
||||
#endif
|
||||
|
||||
int CPUCapFlags = 0;
|
||||
|
||||
void FillCPUCaps(ALuint capfilter)
|
||||
void FillCPUCaps(int capfilter)
|
||||
{
|
||||
ALuint caps = 0;
|
||||
int caps = 0;
|
||||
|
||||
/* FIXME: We really should get this for all available CPUs in case different
|
||||
* CPUs have different caps (is that possible on one machine?). */
|
||||
#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
|
||||
defined(_M_IX86) || defined(_M_X64))
|
||||
#ifdef CAN_GET_CPUID
|
||||
union {
|
||||
unsigned int regs[4];
|
||||
char str[sizeof(unsigned int[4])];
|
||||
} cpuinf[3];
|
||||
reg_type regs[4];
|
||||
char str[sizeof(reg_type[4])];
|
||||
} cpuinf[3] = {{ { 0, 0, 0, 0 } }};
|
||||
|
||||
if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
ERR("Failed to get CPUID\n");
|
||||
else
|
||||
{
|
||||
unsigned int maxfunc = cpuinf[0].regs[0];
|
||||
unsigned int maxextfunc = 0;
|
||||
|
||||
if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
maxextfunc = cpuinf[0].regs[0];
|
||||
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
|
||||
|
||||
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
|
||||
if(maxextfunc >= 0x80000004 &&
|
||||
__get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) &&
|
||||
__get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) &&
|
||||
__get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3]))
|
||||
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
|
||||
|
||||
if(maxfunc >= 1 &&
|
||||
__get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
{
|
||||
if((cpuinf[0].regs[3]&(1<<25)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE;
|
||||
if((cpuinf[0].regs[3]&(1<<26)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((cpuinf[0].regs[2]&(1<<0)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE3;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
|
||||
defined(_M_IX86) || defined(_M_X64))
|
||||
union {
|
||||
int regs[4];
|
||||
char str[sizeof(int[4])];
|
||||
} cpuinf[3];
|
||||
|
||||
(__cpuid)(cpuinf[0].regs, 0);
|
||||
get_cpuid(0, cpuinf[0].regs);
|
||||
if(cpuinf[0].regs[0] == 0)
|
||||
ERR("Failed to get CPUID\n");
|
||||
else
|
||||
@@ -192,7 +174,7 @@ void FillCPUCaps(ALuint capfilter)
|
||||
unsigned int maxfunc = cpuinf[0].regs[0];
|
||||
unsigned int maxextfunc;
|
||||
|
||||
(__cpuid)(cpuinf[0].regs, 0x80000000);
|
||||
get_cpuid(0x80000000, cpuinf[0].regs);
|
||||
maxextfunc = cpuinf[0].regs[0];
|
||||
|
||||
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
|
||||
@@ -200,29 +182,23 @@ void FillCPUCaps(ALuint capfilter)
|
||||
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
|
||||
if(maxextfunc >= 0x80000004)
|
||||
{
|
||||
(__cpuid)(cpuinf[0].regs, 0x80000002);
|
||||
(__cpuid)(cpuinf[1].regs, 0x80000003);
|
||||
(__cpuid)(cpuinf[2].regs, 0x80000004);
|
||||
get_cpuid(0x80000002, cpuinf[0].regs);
|
||||
get_cpuid(0x80000003, cpuinf[1].regs);
|
||||
get_cpuid(0x80000004, cpuinf[2].regs);
|
||||
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
|
||||
}
|
||||
|
||||
if(maxfunc >= 1)
|
||||
{
|
||||
(__cpuid)(cpuinf[0].regs, 1);
|
||||
get_cpuid(1, cpuinf[0].regs);
|
||||
if((cpuinf[0].regs[3]&(1<<25)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE;
|
||||
if((cpuinf[0].regs[3]&(1<<26)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((cpuinf[0].regs[2]&(1<<0)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE3;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if((caps&CPU_CAP_SSE) && (cpuinf[0].regs[3]&(1<<26)))
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((caps&CPU_CAP_SSE2) && (cpuinf[0].regs[2]&(1<<0)))
|
||||
caps |= CPU_CAP_SSE3;
|
||||
if((caps&CPU_CAP_SSE3) && (cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -247,22 +223,32 @@ void FillCPUCaps(ALuint capfilter)
|
||||
ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
|
||||
else
|
||||
{
|
||||
al_string features = AL_STRING_INIT_STATIC();
|
||||
char buf[256];
|
||||
|
||||
while(fgets(buf, sizeof(buf), file) != NULL)
|
||||
{
|
||||
size_t len;
|
||||
char *str;
|
||||
|
||||
if(strncmp(buf, "Features\t:", 10) != 0)
|
||||
continue;
|
||||
|
||||
len = strlen(buf);
|
||||
while(len > 0 && isspace(buf[len-1]))
|
||||
buf[--len] = 0;
|
||||
alstr_copy_cstr(&features, buf+10);
|
||||
while(VECTOR_BACK(features) != '\n')
|
||||
{
|
||||
if(fgets(buf, sizeof(buf), file) == NULL)
|
||||
break;
|
||||
alstr_append_cstr(&features, buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
|
||||
TRACE("Got features string:%s\n", buf+10);
|
||||
if(!alstr_empty(features))
|
||||
{
|
||||
const char *str = alstr_get_cstr(features);
|
||||
while(isspace(str[0])) ++str;
|
||||
|
||||
str = buf;
|
||||
TRACE("Got features string:%s\n", str);
|
||||
while((str=strstr(str, "neon")) != NULL)
|
||||
{
|
||||
if(isspace(*(str-1)) && (str[4] == 0 || isspace(str[4])))
|
||||
@@ -270,13 +256,11 @@ void FillCPUCaps(ALuint capfilter)
|
||||
caps |= CPU_CAP_NEON;
|
||||
break;
|
||||
}
|
||||
str++;
|
||||
++str;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
alstr_reset(&features);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -294,81 +278,44 @@ void FillCPUCaps(ALuint capfilter)
|
||||
|
||||
void SetMixerFPUMode(FPUCtl *ctl)
|
||||
{
|
||||
#ifdef HAVE_FENV_H
|
||||
fegetenv(STATIC_CAST(fenv_t, ctl));
|
||||
#ifdef _WIN32
|
||||
/* HACK: A nasty bug in MinGW-W64 causes fegetenv and fesetenv to not save
|
||||
* and restore the FPU rounding mode, so we have to do it manually. Don't
|
||||
* know if this also applies to MSVC.
|
||||
*/
|
||||
ctl->round_mode = fegetround();
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
/* FIXME: Some fegetenv implementations can get the SSE environment too?
|
||||
* How to tell when it does? */
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
|
||||
#endif
|
||||
|
||||
#ifdef FE_TOWARDZERO
|
||||
fesetround(FE_TOWARDZERO);
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
{
|
||||
int sseState = ctl->sse_state;
|
||||
sseState |= 0x6000; /* set round-to-zero */
|
||||
__asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
|
||||
unsigned int sseState = ctl->sse_state;
|
||||
sseState |= 0x8000; /* set flush-to-zero */
|
||||
if((CPUCapFlags&CPU_CAP_SSE2))
|
||||
sseState |= 0x0040; /* set denormals-are-zero */
|
||||
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE___CONTROL87_2)
|
||||
|
||||
int mode;
|
||||
__control87_2(0, 0, &ctl->state, NULL);
|
||||
__control87_2(_RC_CHOP, _MCW_RC, &mode, NULL);
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
{
|
||||
__control87_2(0, 0, NULL, &ctl->sse_state);
|
||||
__control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
|
||||
}
|
||||
#endif
|
||||
__control87_2(0, 0, &ctl->state, &ctl->sse_state);
|
||||
_control87(_DN_FLUSH, _MCW_DN);
|
||||
|
||||
#elif defined(HAVE__CONTROLFP)
|
||||
|
||||
ctl->state = _controlfp(0, 0);
|
||||
(void)_controlfp(_RC_CHOP, _MCW_RC);
|
||||
_controlfp(_DN_FLUSH, _MCW_DN);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RestoreFPUMode(const FPUCtl *ctl)
|
||||
{
|
||||
#ifdef HAVE_FENV_H
|
||||
fesetenv(STATIC_CAST(fenv_t, ctl));
|
||||
#ifdef _WIN32
|
||||
fesetround(ctl->round_mode);
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE___CONTROL87_2)
|
||||
|
||||
int mode;
|
||||
__control87_2(ctl->state, _MCW_RC, &mode, NULL);
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
|
||||
#endif
|
||||
__control87_2(ctl->state, _MCW_DN, &mode, NULL);
|
||||
__control87_2(ctl->sse_state, _MCW_DN, NULL, &mode);
|
||||
|
||||
#elif defined(HAVE__CONTROLFP)
|
||||
|
||||
_controlfp(ctl->state, _MCW_RC);
|
||||
_controlfp(ctl->state, _MCW_DN);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -392,9 +339,8 @@ static WCHAR *strrchrW(WCHAR *str, WCHAR ch)
|
||||
return ret;
|
||||
}
|
||||
|
||||
al_string GetProcPath(void)
|
||||
void GetProcBinary(al_string *path, al_string *fname)
|
||||
{
|
||||
al_string ret = AL_STRING_INIT_STATIC();
|
||||
WCHAR *pathname, *sep;
|
||||
DWORD pathlen;
|
||||
DWORD len;
|
||||
@@ -411,23 +357,34 @@ al_string GetProcPath(void)
|
||||
{
|
||||
free(pathname);
|
||||
ERR("Failed to get process name: error %lu\n", GetLastError());
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
pathname[len] = 0;
|
||||
if((sep = strrchrW(pathname, '\\')))
|
||||
if((sep=strrchrW(pathname, '\\')) != NULL)
|
||||
{
|
||||
WCHAR *sep2 = strrchrW(pathname, '/');
|
||||
if(sep2) *sep2 = 0;
|
||||
else *sep = 0;
|
||||
WCHAR *sep2 = strrchrW(sep+1, '/');
|
||||
if(sep2) sep = sep2;
|
||||
}
|
||||
else
|
||||
sep = strrchrW(pathname, '/');
|
||||
|
||||
if(sep)
|
||||
{
|
||||
if(path) alstr_copy_wrange(path, pathname, sep);
|
||||
if(fname) alstr_copy_wcstr(fname, sep+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(path) alstr_clear(path);
|
||||
if(fname) alstr_copy_wcstr(fname, pathname);
|
||||
}
|
||||
else if((sep = strrchrW(pathname, '/')))
|
||||
*sep = 0;
|
||||
alstr_copy_wcstr(&ret, pathname);
|
||||
free(pathname);
|
||||
|
||||
TRACE("Got: %s\n", alstr_get_cstr(ret));
|
||||
return ret;
|
||||
if(path && fname)
|
||||
TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname));
|
||||
else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path));
|
||||
else if(fname) TRACE("Got filename: %s\n", alstr_get_cstr(*fname));
|
||||
}
|
||||
|
||||
|
||||
@@ -634,7 +591,7 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir)
|
||||
/* Search the local and global data dirs. */
|
||||
for(i = 0;i < COUNTOF(ids);i++)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
WCHAR buffer[MAX_PATH];
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE)
|
||||
{
|
||||
alstr_copy_wcstr(&path, buffer);
|
||||
@@ -721,64 +678,103 @@ void UnmapFileMem(const struct FileMapping *mapping)
|
||||
|
||||
#else
|
||||
|
||||
al_string GetProcPath(void)
|
||||
void GetProcBinary(al_string *path, al_string *fname)
|
||||
{
|
||||
al_string ret = AL_STRING_INIT_STATIC();
|
||||
char *pathname, *sep;
|
||||
char *pathname = NULL;
|
||||
size_t pathlen;
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
|
||||
mib[3] = getpid();
|
||||
if (sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1) {
|
||||
WARN("Failed to sysctl kern.proc.pathname.%d: %s\n", mib[3], strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
pathname = malloc(pathlen + 1);
|
||||
sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
|
||||
pathname[pathlen] = 0;
|
||||
#else
|
||||
const char *fname;
|
||||
ssize_t len;
|
||||
|
||||
pathlen = 256;
|
||||
pathname = malloc(pathlen);
|
||||
|
||||
fname = "/proc/self/exe";
|
||||
len = readlink(fname, pathname, pathlen);
|
||||
if(len == -1 && errno == ENOENT)
|
||||
{
|
||||
fname = "/proc/self/file";
|
||||
len = readlink(fname, pathname, pathlen);
|
||||
}
|
||||
|
||||
while(len > 0 && (size_t)len == pathlen)
|
||||
{
|
||||
free(pathname);
|
||||
pathlen <<= 1;
|
||||
pathname = malloc(pathlen);
|
||||
len = readlink(fname, pathname, pathlen);
|
||||
}
|
||||
if(len <= 0)
|
||||
{
|
||||
free(pathname);
|
||||
WARN("Failed to readlink %s: %s\n", fname, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
pathname[len] = 0;
|
||||
#endif
|
||||
|
||||
sep = strrchr(pathname, '/');
|
||||
if(sep)
|
||||
alstr_copy_range(&ret, pathname, sep);
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
||||
if(sysctl(mib, 4, NULL, &pathlen, NULL, 0) == -1)
|
||||
WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno));
|
||||
else
|
||||
alstr_copy_cstr(&ret, pathname);
|
||||
{
|
||||
pathname = malloc(pathlen + 1);
|
||||
sysctl(mib, 4, (void*)pathname, &pathlen, NULL, 0);
|
||||
pathname[pathlen] = 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_PROC_PIDPATH
|
||||
if(!pathname)
|
||||
{
|
||||
const pid_t pid = getpid();
|
||||
char procpath[PROC_PIDPATHINFO_MAXSIZE];
|
||||
int ret;
|
||||
|
||||
ret = proc_pidpath(pid, procpath, sizeof(procpath));
|
||||
if(ret < 1)
|
||||
{
|
||||
WARN("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno));
|
||||
free(pathname);
|
||||
pathname = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathlen = strlen(procpath);
|
||||
pathname = strdup(procpath);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(!pathname)
|
||||
{
|
||||
const char *selfname;
|
||||
ssize_t len;
|
||||
|
||||
pathlen = 256;
|
||||
pathname = malloc(pathlen);
|
||||
|
||||
selfname = "/proc/self/exe";
|
||||
len = readlink(selfname, pathname, pathlen);
|
||||
if(len == -1 && errno == ENOENT)
|
||||
{
|
||||
selfname = "/proc/self/file";
|
||||
len = readlink(selfname, pathname, pathlen);
|
||||
}
|
||||
if(len == -1 && errno == ENOENT)
|
||||
{
|
||||
selfname = "/proc/curproc/exe";
|
||||
len = readlink(selfname, pathname, pathlen);
|
||||
}
|
||||
if(len == -1 && errno == ENOENT)
|
||||
{
|
||||
selfname = "/proc/curproc/file";
|
||||
len = readlink(selfname, pathname, pathlen);
|
||||
}
|
||||
|
||||
while(len > 0 && (size_t)len == pathlen)
|
||||
{
|
||||
free(pathname);
|
||||
pathlen <<= 1;
|
||||
pathname = malloc(pathlen);
|
||||
len = readlink(selfname, pathname, pathlen);
|
||||
}
|
||||
if(len <= 0)
|
||||
{
|
||||
free(pathname);
|
||||
WARN("Failed to readlink %s: %s\n", selfname, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
pathname[len] = 0;
|
||||
}
|
||||
|
||||
char *sep = strrchr(pathname, '/');
|
||||
if(sep)
|
||||
{
|
||||
if(path) alstr_copy_range(path, pathname, sep);
|
||||
if(fname) alstr_copy_cstr(fname, sep+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(path) alstr_clear(path);
|
||||
if(fname) alstr_copy_cstr(fname, pathname);
|
||||
}
|
||||
free(pathname);
|
||||
|
||||
TRACE("Got: %s\n", alstr_get_cstr(ret));
|
||||
return ret;
|
||||
if(path && fname)
|
||||
TRACE("Got: %s, %s\n", alstr_get_cstr(*path), alstr_get_cstr(*fname));
|
||||
else if(path) TRACE("Got path: %s\n", alstr_get_cstr(*path));
|
||||
else if(fname) TRACE("Got filename: %s\n", alstr_get_cstr(*fname));
|
||||
}
|
||||
|
||||
|
||||
@@ -881,15 +877,32 @@ vector_al_string SearchDataFiles(const char *ext, const char *subdir)
|
||||
{
|
||||
al_string path = AL_STRING_INIT_STATIC();
|
||||
const char *str, *next;
|
||||
char cwdbuf[PATH_MAX];
|
||||
|
||||
/* Search the app-local directory. */
|
||||
if((str=getenv("ALSOFT_LOCAL_PATH")) && *str != '\0')
|
||||
DirectorySearch(str, ext, &results);
|
||||
else if(getcwd(cwdbuf, sizeof(cwdbuf)))
|
||||
DirectorySearch(cwdbuf, ext, &results);
|
||||
else
|
||||
DirectorySearch(".", ext, &results);
|
||||
{
|
||||
size_t cwdlen = 256;
|
||||
char *cwdbuf = malloc(cwdlen);
|
||||
while(!getcwd(cwdbuf, cwdlen))
|
||||
{
|
||||
free(cwdbuf);
|
||||
cwdbuf = NULL;
|
||||
if(errno != ERANGE)
|
||||
break;
|
||||
cwdlen <<= 1;
|
||||
cwdbuf = malloc(cwdlen);
|
||||
}
|
||||
if(!cwdbuf)
|
||||
DirectorySearch(".", ext, &results);
|
||||
else
|
||||
{
|
||||
DirectorySearch(cwdbuf, ext, &results);
|
||||
free(cwdbuf);
|
||||
cwdbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Search local data dir
|
||||
if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
|
||||
@@ -1092,8 +1105,8 @@ void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_
|
||||
void alstr_append_char(al_string *str, const al_string_char_type c)
|
||||
{
|
||||
size_t len = alstr_length(*str);
|
||||
VECTOR_RESIZE(*str, len, len+2);
|
||||
VECTOR_PUSH_BACK(*str, c);
|
||||
VECTOR_RESIZE(*str, len+1, len+2);
|
||||
VECTOR_BACK(*str) = c;
|
||||
VECTOR_ELEM(*str, len+1) = 0;
|
||||
}
|
||||
|
||||
@@ -1151,6 +1164,17 @@ 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)
|
||||
{
|
||||
int len;
|
||||
if((len=WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), NULL, 0, NULL, NULL)) > 0)
|
||||
{
|
||||
VECTOR_RESIZE(*str, len, len+1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, from, (int)(to-from), &VECTOR_FRONT(*str), len+1, NULL, NULL);
|
||||
VECTOR_ELEM(*str, len) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to)
|
||||
{
|
||||
int len;
|
||||
|
||||
+373
-62
@@ -28,8 +28,9 @@
|
||||
#include "alMain.h"
|
||||
#include "alSource.h"
|
||||
#include "alu.h"
|
||||
#include "bformatdec.h"
|
||||
#include "hrtf.h"
|
||||
#include "alconfig.h"
|
||||
#include "filters/splitter.h"
|
||||
|
||||
#include "compat.h"
|
||||
#include "almalloc.h"
|
||||
@@ -40,12 +41,20 @@
|
||||
#define MAX_IR_SIZE (512)
|
||||
#define MOD_IR_SIZE (8)
|
||||
|
||||
#define MIN_FD_COUNT (1)
|
||||
#define MAX_FD_COUNT (16)
|
||||
|
||||
#define MIN_FD_DISTANCE (50)
|
||||
#define MAX_FD_DISTANCE (2500)
|
||||
|
||||
#define MIN_EV_COUNT (5)
|
||||
#define MAX_EV_COUNT (128)
|
||||
|
||||
#define MIN_AZ_COUNT (1)
|
||||
#define MAX_AZ_COUNT (128)
|
||||
|
||||
#define MAX_HRIR_DELAY (HRTF_HISTORY_LENGTH-1)
|
||||
|
||||
struct HrtfEntry {
|
||||
struct HrtfEntry *next;
|
||||
struct Hrtf *handle;
|
||||
@@ -54,6 +63,7 @@ struct HrtfEntry {
|
||||
|
||||
static const ALchar magicMarker00[8] = "MinPHR00";
|
||||
static const ALchar magicMarker01[8] = "MinPHR01";
|
||||
static const ALchar magicMarker02[8] = "MinPHR02";
|
||||
|
||||
/* First value for pass-through coefficients (remaining are 0), used for omni-
|
||||
* directional sounds. */
|
||||
@@ -64,37 +74,36 @@ static struct HrtfEntry *LoadedHrtfs = NULL;
|
||||
|
||||
|
||||
/* Calculate the elevation index given the polar elevation in radians. This
|
||||
* will return an index between 0 and (evcount - 1). Assumes the FPU is in
|
||||
* round-to-zero mode.
|
||||
* will return an index between 0 and (evcount - 1).
|
||||
*/
|
||||
static ALsizei CalcEvIndex(ALsizei evcount, ALfloat ev, ALfloat *mu)
|
||||
{
|
||||
ALsizei idx;
|
||||
ev = (F_PI_2+ev) * (evcount-1) / F_PI;
|
||||
idx = mini(fastf2i(ev), evcount-1);
|
||||
idx = float2int(ev);
|
||||
|
||||
*mu = ev - idx;
|
||||
return idx;
|
||||
return mini(idx, evcount-1);
|
||||
}
|
||||
|
||||
/* Calculate the azimuth index given the polar azimuth in radians. This will
|
||||
* return an index between 0 and (azcount - 1). Assumes the FPU is in round-to-
|
||||
* zero mode.
|
||||
* return an index between 0 and (azcount - 1).
|
||||
*/
|
||||
static ALsizei CalcAzIndex(ALsizei azcount, ALfloat az, ALfloat *mu)
|
||||
{
|
||||
ALsizei idx;
|
||||
az = (F_TAU+az) * azcount / F_TAU;
|
||||
|
||||
idx = fastf2i(az) % azcount;
|
||||
*mu = az - floorf(az);
|
||||
return idx;
|
||||
idx = float2int(az);
|
||||
*mu = az - idx;
|
||||
return idx % azcount;
|
||||
}
|
||||
|
||||
/* Calculates static HRIR coefficients and delays for the given polar elevation
|
||||
* and azimuth in radians. The coefficients are normalized.
|
||||
*/
|
||||
void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays)
|
||||
void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread,
|
||||
ALfloat (*restrict coeffs)[2], ALsizei *delays)
|
||||
{
|
||||
ALsizei evidx, azidx, idx[4];
|
||||
ALsizei evoffset;
|
||||
@@ -149,11 +158,11 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
|
||||
/* Calculate the blended HRIR delays. */
|
||||
delays[0] = fastf2i(
|
||||
Hrtf->delays[idx[0]][0]*blend[0] + Hrtf->delays[idx[1]][0]*blend[1] +
|
||||
Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3] + 0.5f
|
||||
Hrtf->delays[idx[2]][0]*blend[2] + Hrtf->delays[idx[3]][0]*blend[3]
|
||||
);
|
||||
delays[1] = fastf2i(
|
||||
Hrtf->delays[idx[0]][1]*blend[0] + Hrtf->delays[idx[1]][1]*blend[1] +
|
||||
Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3] + 0.5f
|
||||
Hrtf->delays[idx[2]][1]*blend[2] + Hrtf->delays[idx[3]][1]*blend[3]
|
||||
);
|
||||
|
||||
/* Calculate the sample offsets for the HRIR indices. */
|
||||
@@ -162,6 +171,8 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
|
||||
idx[2] *= Hrtf->irSize;
|
||||
idx[3] *= Hrtf->irSize;
|
||||
|
||||
ASSUME(Hrtf->irSize >= MIN_IR_SIZE && (Hrtf->irSize%MOD_IR_SIZE) == 0);
|
||||
coeffs = ASSUME_ALIGNED(coeffs, 16);
|
||||
/* Calculate the blended HRIR coefficients. */
|
||||
coeffs[0][0] = PassthruCoeff * (1.0f-dirfact);
|
||||
coeffs[0][1] = PassthruCoeff * (1.0f-dirfact);
|
||||
@@ -172,16 +183,17 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
|
||||
}
|
||||
for(c = 0;c < 4;c++)
|
||||
{
|
||||
const ALfloat (*restrict srccoeffs)[2] = ASSUME_ALIGNED(Hrtf->coeffs+idx[c], 16);
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] += Hrtf->coeffs[idx[c]+i][0] * blend[c];
|
||||
coeffs[i][1] += Hrtf->coeffs[idx[c]+i][1] * blend[c];
|
||||
coeffs[i][0] += srccoeffs[i][0] * blend[c];
|
||||
coeffs[i][1] += srccoeffs[i][1] * blend[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const ALfloat (*restrict AmbiPoints)[2], const ALfloat (*restrict AmbiMatrix)[2][MAX_AMBI_COEFFS], ALsizei AmbiCount)
|
||||
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)
|
||||
{
|
||||
/* Set this to 2 for dual-band HRTF processing. May require a higher quality
|
||||
* band-splitter, or better calculation of the new IR length to deal with the
|
||||
@@ -189,12 +201,16 @@ ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsize
|
||||
*/
|
||||
#define NUM_BANDS 2
|
||||
BandSplitter splitter;
|
||||
ALsizei idx[HRTF_AMBI_MAX_CHANNELS];
|
||||
ALdouble (*tmpres)[HRIR_LENGTH][2];
|
||||
ALsizei *restrict idx;
|
||||
ALsizei min_delay = HRTF_HISTORY_LENGTH;
|
||||
ALsizei max_delay = 0;
|
||||
ALfloat temps[3][HRIR_LENGTH];
|
||||
ALsizei max_length = 0;
|
||||
ALsizei max_length;
|
||||
ALsizei i, c, b;
|
||||
|
||||
idx = al_calloc(DEF_ALIGN, AmbiCount*sizeof(*idx));
|
||||
|
||||
for(c = 0;c < AmbiCount;c++)
|
||||
{
|
||||
ALuint evidx, azidx;
|
||||
@@ -202,23 +218,24 @@ ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsize
|
||||
ALuint azcount;
|
||||
|
||||
/* Calculate elevation index. */
|
||||
evidx = (ALsizei)floorf((F_PI_2 + AmbiPoints[c][0]) *
|
||||
(Hrtf->evCount-1)/F_PI + 0.5f);
|
||||
evidx = mini(evidx, Hrtf->evCount-1);
|
||||
evidx = (ALsizei)((F_PI_2+AmbiPoints[c].Elev) * (Hrtf->evCount-1) / F_PI + 0.5f);
|
||||
evidx = clampi(evidx, 0, Hrtf->evCount-1);
|
||||
|
||||
azcount = Hrtf->azCount[evidx];
|
||||
evoffset = Hrtf->evOffset[evidx];
|
||||
|
||||
/* Calculate azimuth index for this elevation. */
|
||||
azidx = (ALsizei)floorf((F_TAU+AmbiPoints[c][1]) *
|
||||
azcount/F_TAU + 0.5f) % azcount;
|
||||
azidx = (ALsizei)((F_TAU+AmbiPoints[c].Azim) * azcount / F_TAU + 0.5f) % azcount;
|
||||
|
||||
/* Calculate indices for left and right channels. */
|
||||
idx[c] = evoffset + azidx;
|
||||
|
||||
min_delay = mini(min_delay, mini(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]));
|
||||
max_delay = maxi(max_delay, maxi(Hrtf->delays[idx[c]][0], Hrtf->delays[idx[c]][1]));
|
||||
}
|
||||
|
||||
tmpres = al_calloc(16, NumChannels * sizeof(*tmpres));
|
||||
|
||||
memset(temps, 0, sizeof(temps));
|
||||
bandsplit_init(&splitter, 400.0f / (ALfloat)Hrtf->sampleRate);
|
||||
for(c = 0;c < AmbiCount;c++)
|
||||
@@ -227,20 +244,17 @@ ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsize
|
||||
ALsizei ldelay = Hrtf->delays[idx[c]][0] - min_delay;
|
||||
ALsizei rdelay = Hrtf->delays[idx[c]][1] - min_delay;
|
||||
|
||||
max_length = maxi(max_length,
|
||||
mini(maxi(ldelay, rdelay) + Hrtf->irSize, HRIR_LENGTH)
|
||||
);
|
||||
|
||||
if(NUM_BANDS == 1)
|
||||
{
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
ALdouble mult = (ALdouble)AmbiOrderHFGain[(ALsizei)sqrt(i)] * AmbiMatrix[c][i];
|
||||
ALsizei lidx = ldelay, ridx = rdelay;
|
||||
ALsizei j = 0;
|
||||
while(lidx < HRIR_LENGTH && ridx < HRIR_LENGTH && j < Hrtf->irSize)
|
||||
{
|
||||
state->Chan[i].Coeffs[lidx++][0] += fir[j][0] * AmbiMatrix[c][0][i];
|
||||
state->Chan[i].Coeffs[ridx++][1] += fir[j][1] * AmbiMatrix[c][0][i];
|
||||
tmpres[i][lidx++][0] += fir[j][0] * mult;
|
||||
tmpres[i][ridx++][1] += fir[j][1] * mult;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
@@ -256,12 +270,14 @@ ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsize
|
||||
/* Apply left ear response with delay. */
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
ALfloat hfgain = AmbiOrderHFGain[(ALsizei)sqrt(i)];
|
||||
for(b = 0;b < NUM_BANDS;b++)
|
||||
{
|
||||
ALdouble mult = AmbiMatrix[c][i] * (ALdouble)((b==0) ? hfgain : 1.0);
|
||||
ALsizei lidx = ldelay;
|
||||
ALsizei j = 0;
|
||||
while(lidx < HRIR_LENGTH)
|
||||
state->Chan[i].Coeffs[lidx++][0] += temps[b][j++] * AmbiMatrix[c][b][i];
|
||||
tmpres[i][lidx++][0] += temps[b][j++] * mult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,29 +290,58 @@ ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsize
|
||||
/* Apply right ear response with delay. */
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
ALfloat hfgain = AmbiOrderHFGain[(ALsizei)sqrt(i)];
|
||||
for(b = 0;b < NUM_BANDS;b++)
|
||||
{
|
||||
ALdouble mult = AmbiMatrix[c][i] * (ALdouble)((b==0) ? hfgain : 1.0);
|
||||
ALsizei ridx = rdelay;
|
||||
ALsizei j = 0;
|
||||
while(ridx < HRIR_LENGTH)
|
||||
state->Chan[i].Coeffs[ridx++][1] += temps[b][j++] * AmbiMatrix[c][b][i];
|
||||
tmpres[i][ridx++][1] += temps[b][j++] * mult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Round up to the next IR size multiple. */
|
||||
max_length = RoundUp(max_length, MOD_IR_SIZE);
|
||||
|
||||
TRACE("Skipped min delay: %d, new combined length: %d\n", min_delay, max_length);
|
||||
return max_length;
|
||||
for(i = 0;i < NumChannels;++i)
|
||||
{
|
||||
int idx;
|
||||
for(idx = 0;idx < HRIR_LENGTH;idx++)
|
||||
{
|
||||
state->Chan[i].Coeffs[idx][0] = (ALfloat)tmpres[i][idx][0];
|
||||
state->Chan[i].Coeffs[idx][1] = (ALfloat)tmpres[i][idx][1];
|
||||
}
|
||||
}
|
||||
al_free(tmpres);
|
||||
tmpres = NULL;
|
||||
al_free(idx);
|
||||
idx = NULL;
|
||||
|
||||
if(NUM_BANDS == 1)
|
||||
max_length = mini(max_delay-min_delay + Hrtf->irSize, HRIR_LENGTH);
|
||||
else
|
||||
{
|
||||
/* Increase the IR size by 2/3rds to account for the tail generated by
|
||||
* the band-split filter.
|
||||
*/
|
||||
const ALsizei irsize = mini(Hrtf->irSize*5/3, HRIR_LENGTH);
|
||||
max_length = mini(max_delay-min_delay + irsize, HRIR_LENGTH);
|
||||
}
|
||||
/* Round up to the next IR size multiple. */
|
||||
max_length += MOD_IR_SIZE-1;
|
||||
max_length -= max_length%MOD_IR_SIZE;
|
||||
|
||||
TRACE("Skipped delay: %d, max delay: %d, new FIR length: %d\n",
|
||||
min_delay, max_delay-min_delay, max_length);
|
||||
state->IrSize = max_length;
|
||||
#undef NUM_BANDS
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize, ALsizei evCount, ALsizei irCount,
|
||||
const ALubyte *azCount, const ALushort *evOffset,
|
||||
const ALfloat (*coeffs)[2], const ALubyte (*delays)[2],
|
||||
const char *filename)
|
||||
static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize,
|
||||
ALfloat distance, ALsizei evCount, ALsizei irCount, const ALubyte *azCount,
|
||||
const ALushort *evOffset, const ALfloat (*coeffs)[2], const ALubyte (*delays)[2],
|
||||
const char *filename)
|
||||
{
|
||||
struct Hrtf *Hrtf;
|
||||
size_t total;
|
||||
@@ -325,23 +370,26 @@ static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize, ALsizei evCount
|
||||
InitRef(&Hrtf->ref, 0);
|
||||
Hrtf->sampleRate = rate;
|
||||
Hrtf->irSize = irSize;
|
||||
Hrtf->distance = distance;
|
||||
Hrtf->evCount = evCount;
|
||||
|
||||
/* Set up pointers to storage following the main HRTF struct. */
|
||||
_azCount = (ALubyte*)(base + offset); Hrtf->azCount = _azCount;
|
||||
_azCount = (ALubyte*)(base + offset);
|
||||
offset += sizeof(_azCount[0])*evCount;
|
||||
|
||||
offset = RoundUp(offset, sizeof(ALushort)); /* Align for ushort fields */
|
||||
_evOffset = (ALushort*)(base + offset); Hrtf->evOffset = _evOffset;
|
||||
_evOffset = (ALushort*)(base + offset);
|
||||
offset += sizeof(_evOffset[0])*evCount;
|
||||
|
||||
offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */
|
||||
_coeffs = (ALfloat(*)[2])(base + offset); Hrtf->coeffs = _coeffs;
|
||||
_coeffs = (ALfloat(*)[2])(base + offset);
|
||||
offset += sizeof(_coeffs[0])*irSize*irCount;
|
||||
|
||||
_delays = (ALubyte(*)[2])(base + offset); Hrtf->delays = _delays;
|
||||
_delays = (ALubyte(*)[2])(base + offset);
|
||||
offset += sizeof(_delays[0])*irCount;
|
||||
|
||||
assert(offset == total);
|
||||
|
||||
/* Copy input data to storage. */
|
||||
for(i = 0;i < evCount;i++) _azCount[i] = azCount[i];
|
||||
for(i = 0;i < evCount;i++) _evOffset[i] = evOffset[i];
|
||||
@@ -356,7 +404,11 @@ static struct Hrtf *CreateHrtfStore(ALuint rate, ALsizei irSize, ALsizei evCount
|
||||
_delays[i][1] = delays[i][1];
|
||||
}
|
||||
|
||||
assert(offset == total);
|
||||
/* Finally, assign the storage pointers. */
|
||||
Hrtf->azCount = _azCount;
|
||||
Hrtf->evOffset = _evOffset;
|
||||
Hrtf->coeffs = _coeffs;
|
||||
Hrtf->delays = _delays;
|
||||
}
|
||||
|
||||
return Hrtf;
|
||||
@@ -383,9 +435,16 @@ static ALushort GetLE_ALushort(const ALubyte **data, size_t *len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ALint GetLE_ALuint(const ALubyte **data, size_t *len)
|
||||
static ALint GetLE_ALint24(const ALubyte **data, size_t *len)
|
||||
{
|
||||
ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24);
|
||||
ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16);
|
||||
*data += 3; *len -= 3;
|
||||
return (ret^0x800000) - 0x800000;
|
||||
}
|
||||
|
||||
static ALuint GetLE_ALuint(const ALubyte **data, size_t *len)
|
||||
{
|
||||
ALuint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24);
|
||||
*data += 4; *len -= 4;
|
||||
return ret;
|
||||
}
|
||||
@@ -399,7 +458,6 @@ static const ALubyte *Get_ALubytePtr(const ALubyte **data, size_t *len, size_t s
|
||||
|
||||
static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *filename)
|
||||
{
|
||||
const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0;
|
||||
@@ -525,9 +583,9 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i][0] = GetLE_ALubyte(&data, &datalen);
|
||||
if(delays[i][0] > maxDelay)
|
||||
if(delays[i][0] > MAX_HRIR_DELAY)
|
||||
{
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay);
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
@@ -552,7 +610,7 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
|
||||
}
|
||||
}
|
||||
|
||||
Hrtf = CreateHrtfStore(rate, irSize, evCount, irCount, azCount,
|
||||
Hrtf = CreateHrtfStore(rate, irSize, 0.0f, evCount, irCount, azCount,
|
||||
evOffset, coeffs, delays, filename);
|
||||
}
|
||||
|
||||
@@ -565,7 +623,6 @@ static struct Hrtf *LoadHrtf00(const ALubyte *data, size_t datalen, const char *
|
||||
|
||||
static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *filename)
|
||||
{
|
||||
const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0;
|
||||
@@ -674,9 +731,9 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i][0] = GetLE_ALubyte(&data, &datalen);
|
||||
if(delays[i][0] > maxDelay)
|
||||
if(delays[i][0] > MAX_HRIR_DELAY)
|
||||
{
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], maxDelay);
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
@@ -701,7 +758,7 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
|
||||
}
|
||||
}
|
||||
|
||||
Hrtf = CreateHrtfStore(rate, irSize, evCount, irCount, azCount,
|
||||
Hrtf = CreateHrtfStore(rate, irSize, 0.0f, evCount, irCount, azCount,
|
||||
evOffset, coeffs, delays, filename);
|
||||
}
|
||||
|
||||
@@ -711,6 +768,253 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
#define SAMPLETYPE_S16 0
|
||||
#define SAMPLETYPE_S24 1
|
||||
|
||||
#define CHANTYPE_LEFTONLY 0
|
||||
#define CHANTYPE_LEFTRIGHT 1
|
||||
|
||||
static struct Hrtf *LoadHrtf02(const ALubyte *data, size_t datalen, const char *filename)
|
||||
{
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0;
|
||||
ALubyte sampleType;
|
||||
ALubyte channelType;
|
||||
ALushort irCount = 0;
|
||||
ALushort irSize = 0;
|
||||
ALubyte fdCount = 0;
|
||||
ALushort distance = 0;
|
||||
ALubyte evCount = 0;
|
||||
const ALubyte *azCount = NULL;
|
||||
ALushort *evOffset = NULL;
|
||||
ALfloat (*coeffs)[2] = NULL;
|
||||
ALubyte (*delays)[2] = NULL;
|
||||
ALsizei i, j;
|
||||
|
||||
if(datalen < 8)
|
||||
{
|
||||
ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 8, datalen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rate = GetLE_ALuint(&data, &datalen);
|
||||
sampleType = GetLE_ALubyte(&data, &datalen);
|
||||
channelType = GetLE_ALubyte(&data, &datalen);
|
||||
|
||||
irSize = GetLE_ALubyte(&data, &datalen);
|
||||
|
||||
fdCount = GetLE_ALubyte(&data, &datalen);
|
||||
|
||||
if(sampleType > SAMPLETYPE_S24)
|
||||
{
|
||||
ERR("Unsupported sample type: %d\n", sampleType);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(channelType > CHANTYPE_LEFTRIGHT)
|
||||
{
|
||||
ERR("Unsupported channel type: %d\n", channelType);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
|
||||
irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(fdCount != 1)
|
||||
{
|
||||
ERR("Multiple field-depths not supported: fdCount=%d (%d to %d)\n",
|
||||
evCount, MIN_FD_COUNT, MAX_FD_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(failed)
|
||||
return NULL;
|
||||
|
||||
for(i = 0;i < fdCount;i++)
|
||||
{
|
||||
if(datalen < 3)
|
||||
{
|
||||
ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 3, datalen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
distance = GetLE_ALushort(&data, &datalen);
|
||||
if(distance < MIN_FD_DISTANCE || distance > MAX_FD_DISTANCE)
|
||||
{
|
||||
ERR("Unsupported field distance: distance=%d (%dmm to %dmm)\n",
|
||||
distance, MIN_FD_DISTANCE, MAX_FD_DISTANCE);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
evCount = GetLE_ALubyte(&data, &datalen);
|
||||
if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
|
||||
{
|
||||
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
|
||||
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(failed)
|
||||
return NULL;
|
||||
|
||||
if(datalen < evCount)
|
||||
{
|
||||
ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, evCount, datalen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
azCount = Get_ALubytePtr(&data, &datalen, evCount);
|
||||
for(j = 0;j < evCount;j++)
|
||||
{
|
||||
if(azCount[j] < MIN_AZ_COUNT || azCount[j] > MAX_AZ_COUNT)
|
||||
{
|
||||
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
|
||||
j, azCount[j], MIN_AZ_COUNT, MAX_AZ_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(failed)
|
||||
return NULL;
|
||||
|
||||
evOffset = malloc(sizeof(evOffset[0])*evCount);
|
||||
if(azCount == NULL || evOffset == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
evOffset[0] = 0;
|
||||
irCount = azCount[0];
|
||||
for(i = 1;i < evCount;i++)
|
||||
{
|
||||
evOffset[i] = evOffset[i-1] + azCount[i-1];
|
||||
irCount += azCount[i];
|
||||
}
|
||||
|
||||
coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
|
||||
delays = malloc(sizeof(delays[0])*irCount);
|
||||
if(coeffs == NULL || delays == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
size_t reqsize = 2*irSize*irCount + irCount;
|
||||
if(datalen < reqsize)
|
||||
{
|
||||
ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n",
|
||||
filename, reqsize, datalen);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
if(channelType == CHANTYPE_LEFTONLY)
|
||||
{
|
||||
if(sampleType == SAMPLETYPE_S16)
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
|
||||
}
|
||||
else if(sampleType == SAMPLETYPE_S24)
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
coeffs[i*irSize + j][0] = GetLE_ALint24(&data, &datalen) / 8388608.0f;
|
||||
}
|
||||
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i][0] = GetLE_ALubyte(&data, &datalen);
|
||||
if(delays[i][0] > MAX_HRIR_DELAY)
|
||||
{
|
||||
ERR("Invalid delays[%d][0]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(channelType == CHANTYPE_LEFTRIGHT)
|
||||
{
|
||||
if(sampleType == SAMPLETYPE_S16)
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
{
|
||||
coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
|
||||
coeffs[i*irSize + j][1] = GetLE_ALshort(&data, &datalen) / 32768.0f;
|
||||
}
|
||||
}
|
||||
else if(sampleType == SAMPLETYPE_S24)
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
{
|
||||
coeffs[i*irSize + j][0] = GetLE_ALint24(&data, &datalen) / 8388608.0f;
|
||||
coeffs[i*irSize + j][1] = GetLE_ALint24(&data, &datalen) / 8388608.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i][0] = GetLE_ALubyte(&data, &datalen);
|
||||
if(delays[i][0] > MAX_HRIR_DELAY)
|
||||
{
|
||||
ERR("Invalid delays[%d][0]: %d (%d)\n", i, delays[i][0], MAX_HRIR_DELAY);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
delays[i][1] = GetLE_ALubyte(&data, &datalen);
|
||||
if(delays[i][1] > MAX_HRIR_DELAY)
|
||||
{
|
||||
ERR("Invalid delays[%d][1]: %d (%d)\n", i, delays[i][1], MAX_HRIR_DELAY);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
if(channelType == CHANTYPE_LEFTONLY)
|
||||
{
|
||||
/* Mirror the left ear responses to the right ear. */
|
||||
for(i = 0;i < evCount;i++)
|
||||
{
|
||||
ALushort evoffset = evOffset[i];
|
||||
ALubyte azcount = azCount[i];
|
||||
for(j = 0;j < azcount;j++)
|
||||
{
|
||||
ALsizei lidx = evoffset + j;
|
||||
ALsizei ridx = evoffset + ((azcount-j) % azcount);
|
||||
ALsizei k;
|
||||
|
||||
for(k = 0;k < irSize;k++)
|
||||
coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
|
||||
delays[ridx][1] = delays[lidx][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Hrtf = CreateHrtfStore(rate, irSize,
|
||||
(ALfloat)distance / 1000.0f, evCount, irCount, azCount, evOffset,
|
||||
coeffs, delays, filename
|
||||
);
|
||||
}
|
||||
|
||||
free(evOffset);
|
||||
free(coeffs);
|
||||
free(delays);
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
|
||||
static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename)
|
||||
{
|
||||
@@ -730,12 +1034,12 @@ static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename)
|
||||
/* Check if this entry has already been added to the list. */
|
||||
#define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf)
|
||||
VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY);
|
||||
#undef MATCH_ENTRY
|
||||
if(iter != VECTOR_END(*list))
|
||||
{
|
||||
TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename));
|
||||
return;
|
||||
}
|
||||
#undef MATCH_FNAME
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -792,7 +1096,7 @@ static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename)
|
||||
/* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer
|
||||
* for input instead of opening the given filename.
|
||||
*/
|
||||
static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filename, size_t residx)
|
||||
static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filename, ALuint residx)
|
||||
{
|
||||
EnumeratedHrtf entry = { AL_STRING_INIT_STATIC(), NULL };
|
||||
struct HrtfEntry *loaded_entry;
|
||||
@@ -809,12 +1113,12 @@ static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filenam
|
||||
{
|
||||
#define MATCH_ENTRY(i) (loaded_entry == (i)->hrtf)
|
||||
VECTOR_FIND_IF(iter, const EnumeratedHrtf, *list, MATCH_ENTRY);
|
||||
#undef MATCH_ENTRY
|
||||
if(iter != VECTOR_END(*list))
|
||||
{
|
||||
TRACE("Skipping duplicate file entry %s\n", alstr_get_cstr(filename));
|
||||
return;
|
||||
}
|
||||
#undef MATCH_FNAME
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -832,7 +1136,7 @@ static void AddBuiltInEntry(vector_EnumeratedHrtf *list, const_al_string filenam
|
||||
);
|
||||
loaded_entry->next = LoadedHrtfs;
|
||||
loaded_entry->handle = hrtf;
|
||||
snprintf(loaded_entry->filename, namelen, "!"SZFMT"_%s",
|
||||
snprintf(loaded_entry->filename, namelen, "!%u_%s",
|
||||
residx, alstr_get_cstr(filename));
|
||||
LoadedHrtfs = loaded_entry;
|
||||
}
|
||||
@@ -1020,7 +1324,7 @@ struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry)
|
||||
struct FileMapping fmap;
|
||||
const ALubyte *rdata;
|
||||
const char *name;
|
||||
size_t residx;
|
||||
ALuint residx;
|
||||
size_t rsize;
|
||||
char ch;
|
||||
|
||||
@@ -1036,7 +1340,7 @@ struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry)
|
||||
|
||||
fmap.ptr = NULL;
|
||||
fmap.len = 0;
|
||||
if(sscanf(entry->filename, "!"SZFMT"%c", &residx, &ch) == 2 && ch == '_')
|
||||
if(sscanf(entry->filename, "!%u%c", &residx, &ch) == 2 && ch == '_')
|
||||
{
|
||||
name = strchr(entry->filename, ch)+1;
|
||||
|
||||
@@ -1044,7 +1348,7 @@ struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry)
|
||||
rdata = GetResource(residx, &rsize);
|
||||
if(rdata == NULL || rsize == 0)
|
||||
{
|
||||
ERR("Could not get resource "SZFMT", %s\n", residx, name);
|
||||
ERR("Could not get resource %u, %s\n", residx, name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
@@ -1064,8 +1368,15 @@ struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry)
|
||||
rsize = fmap.len;
|
||||
}
|
||||
|
||||
if(rsize < sizeof(magicMarker01))
|
||||
if(rsize < sizeof(magicMarker02))
|
||||
ERR("%s data is too short ("SZFMT" bytes)\n", name, rsize);
|
||||
else if(memcmp(rdata, magicMarker02, sizeof(magicMarker02)) == 0)
|
||||
{
|
||||
TRACE("Detected data set format v2\n");
|
||||
hrtf = LoadHrtf02(rdata+sizeof(magicMarker02),
|
||||
rsize-sizeof(magicMarker02), name
|
||||
);
|
||||
}
|
||||
else if(memcmp(rdata, magicMarker01, sizeof(magicMarker01)) == 0)
|
||||
{
|
||||
TRACE("Detected data set format v1\n");
|
||||
|
||||
+40
-8
@@ -9,10 +9,13 @@
|
||||
#include "atomic.h"
|
||||
|
||||
|
||||
/* The maximum number of virtual speakers used to generate HRTF coefficients
|
||||
* for decoding B-Format.
|
||||
*/
|
||||
#define HRTF_AMBI_MAX_CHANNELS 16
|
||||
#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)
|
||||
|
||||
|
||||
struct HrtfEntry;
|
||||
@@ -22,6 +25,8 @@ struct Hrtf {
|
||||
|
||||
ALuint sampleRate;
|
||||
ALsizei irSize;
|
||||
|
||||
ALfloat distance;
|
||||
ALubyte evCount;
|
||||
|
||||
const ALubyte *azCount;
|
||||
@@ -31,6 +36,33 @@ struct Hrtf {
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
|
||||
vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname);
|
||||
@@ -43,10 +75,10 @@ void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth,
|
||||
|
||||
/**
|
||||
* Produces HRTF filter coefficients for decoding B-Format, given a set of
|
||||
* virtual speaker positions and HF/LF matrices for decoding to them. The
|
||||
* returned coefficients are ordered and scaled according to the matrices.
|
||||
* Returns the maximum impulse-response length of the generated coefficients.
|
||||
* 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.
|
||||
*/
|
||||
ALsizei BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const ALfloat (*restrict AmbiPoints)[2], const ALfloat (*restrict AmbiMatrix)[2][MAX_AMBI_COEFFS], ALsizei AmbiCount);
|
||||
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 */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
+5
-28
@@ -2,37 +2,19 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "mastering.h"
|
||||
#include "alu.h"
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
extern inline ALuint GetCompressorSampleRate(const Compressor *Comp);
|
||||
|
||||
#define RMS_WINDOW_SIZE (1<<7)
|
||||
#define RMS_WINDOW_MASK (RMS_WINDOW_SIZE-1)
|
||||
#define RMS_VALUE_MAX (1<<24)
|
||||
|
||||
#define LOOKAHEAD_SIZE (1<<13)
|
||||
#define LOOKAHEAD_MASK (LOOKAHEAD_SIZE-1)
|
||||
|
||||
static_assert(RMS_VALUE_MAX < (UINT_MAX / RMS_WINDOW_SIZE), "RMS_VALUE_MAX is too big");
|
||||
|
||||
typedef struct Compressor {
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
ALboolean SummedLink;
|
||||
ALfloat AttackMin;
|
||||
ALfloat AttackMax;
|
||||
ALfloat ReleaseMin;
|
||||
ALfloat ReleaseMax;
|
||||
ALfloat Ratio;
|
||||
ALfloat Threshold;
|
||||
ALfloat Knee;
|
||||
ALuint SampleRate;
|
||||
|
||||
ALuint RmsSum;
|
||||
ALuint *RmsWindow;
|
||||
ALsizei RmsIndex;
|
||||
ALfloat Envelope[BUFFERSIZE];
|
||||
ALfloat EnvLast;
|
||||
} Compressor;
|
||||
|
||||
/* Multichannel compression is linked via one of two modes:
|
||||
*
|
||||
@@ -116,7 +98,7 @@ static void FollowEnvelope(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat env = maxf(-6.0f, log10f(Comp->Envelope[i]));
|
||||
ALfloat env = log10f(maxf(Comp->Envelope[i], 0.000001f));
|
||||
ALfloat slope = minf(1.0f, fabsf(env - last) / 4.5f);
|
||||
|
||||
if(env > last)
|
||||
@@ -209,11 +191,6 @@ Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
|
||||
return Comp;
|
||||
}
|
||||
|
||||
ALuint GetCompressorSampleRate(const Compressor *Comp)
|
||||
{
|
||||
return Comp->SampleRate;
|
||||
}
|
||||
|
||||
void ApplyCompression(Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef MASTERING_H
|
||||
#define MASTERING_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
/* For BUFFERSIZE. */
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct Compressor {
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
ALboolean SummedLink;
|
||||
ALfloat AttackMin;
|
||||
ALfloat AttackMax;
|
||||
ALfloat ReleaseMin;
|
||||
ALfloat ReleaseMax;
|
||||
ALfloat Ratio;
|
||||
ALfloat Threshold;
|
||||
ALfloat Knee;
|
||||
ALuint SampleRate;
|
||||
|
||||
ALuint RmsSum;
|
||||
ALuint *RmsWindow;
|
||||
ALsizei RmsIndex;
|
||||
ALfloat Envelope[BUFFERSIZE];
|
||||
ALfloat EnvLast;
|
||||
} Compressor;
|
||||
|
||||
/* The compressor requires the following information for proper
|
||||
* initialization:
|
||||
*
|
||||
* PreGainDb - Gain applied before detection (in dB).
|
||||
* PostGainDb - Gain applied after compression (in dB).
|
||||
* SummedLink - Whether to use summed (true) or maxed (false) linking.
|
||||
* RmsSensing - Whether to use RMS (true) or Peak (false) sensing.
|
||||
* AttackTimeMin - Minimum attack time (in seconds).
|
||||
* AttackTimeMax - Maximum attack time. Automates when min != max.
|
||||
* ReleaseTimeMin - Minimum release time (in seconds).
|
||||
* ReleaseTimeMax - Maximum release time. Automates when min != max.
|
||||
* Ratio - Compression ratio (x:1). Set to 0 for true limiter.
|
||||
* ThresholdDb - Triggering threshold (in dB).
|
||||
* KneeDb - Knee width (below threshold; in dB).
|
||||
* SampleRate - Sample rate to process.
|
||||
*/
|
||||
Compressor *CompressorInit(const ALfloat PreGainDb, const ALfloat PostGainDb,
|
||||
const ALboolean SummedLink, const ALboolean RmsSensing, const ALfloat AttackTimeMin,
|
||||
const ALfloat AttackTimeMax, const ALfloat ReleaseTimeMin, const ALfloat ReleaseTimeMax,
|
||||
const ALfloat Ratio, const ALfloat ThresholdDb, const ALfloat KneeDb,
|
||||
const ALuint SampleRate);
|
||||
|
||||
void ApplyCompression(struct Compressor *Comp, const ALsizei NumChans, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
|
||||
|
||||
inline ALuint GetCompressorSampleRate(const Compressor *Comp)
|
||||
{ return Comp->SampleRate; }
|
||||
|
||||
#endif /* MASTERING_H */
|
||||
@@ -12,11 +12,11 @@ struct MixHrtfParams;
|
||||
struct HrtfState;
|
||||
|
||||
/* C resamplers */
|
||||
const ALfloat *Resample_copy32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
const ALfloat *Resample_point32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
const ALfloat *Resample_lerp32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
const ALfloat *Resample_fir4_32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
const ALfloat *Resample_bsinc32_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
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 */
|
||||
@@ -76,23 +76,16 @@ inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restr
|
||||
}
|
||||
}
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE2(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
const ALfloat *Resample_lerp32_SSE41(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
const ALfloat *Resample_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_fir4_32_SSE3(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
const ALfloat *Resample_fir4_32_SSE41(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
|
||||
const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei dstlen);
|
||||
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,
|
||||
@@ -116,14 +109,11 @@ void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains,
|
||||
ALsizei InPos, ALsizei BufferSize);
|
||||
|
||||
/* Neon resamplers */
|
||||
const ALfloat *Resample_lerp32_Neon(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
const ALfloat *Resample_fir4_32_Neon(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
const ALfloat *Resample_bsinc32_Neon(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei dstlen);
|
||||
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 */
|
||||
@@ -4,9 +4,9 @@
|
||||
#include "alSource.h"
|
||||
|
||||
#include "hrtf.h"
|
||||
#include "mixer_defs.h"
|
||||
#include "align.h"
|
||||
#include "alu.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
@@ -22,18 +22,24 @@ void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
{
|
||||
const ALfloat (*Coeffs)[2] = ASSUME_ALIGNED(hrtfparams->Coeffs, 16);
|
||||
const ALsizei Delay[2] = { hrtfparams->Delay[0], hrtfparams->Delay[1] };
|
||||
ALfloat gainstep = hrtfparams->GainStep;
|
||||
ALfloat gain = hrtfparams->Gain;
|
||||
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++);
|
||||
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*gain;
|
||||
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*gain;
|
||||
|
||||
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;
|
||||
@@ -42,10 +48,10 @@ void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
gain += gainstep;
|
||||
stepcount += 1.0f;
|
||||
Offset++;
|
||||
}
|
||||
hrtfparams->Gain = gain;
|
||||
hrtfparams->Gain = gain + gainstep*stepcount;
|
||||
}
|
||||
|
||||
void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
@@ -56,15 +62,19 @@ void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
{
|
||||
const ALfloat (*OldCoeffs)[2] = ASSUME_ALIGNED(oldparams->Coeffs, 16);
|
||||
const ALsizei OldDelay[2] = { oldparams->Delay[0], oldparams->Delay[1] };
|
||||
ALfloat oldGain = oldparams->Gain;
|
||||
ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
|
||||
const ALfloat 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] };
|
||||
ALfloat newGain = newparams->Gain;
|
||||
ALfloat newGainStep = newparams->GainStep;
|
||||
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++)
|
||||
@@ -74,22 +84,23 @@ void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
|
||||
|
||||
left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*oldGain;
|
||||
right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*oldGain;
|
||||
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);
|
||||
|
||||
left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*newGain;
|
||||
right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*newGain;
|
||||
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];
|
||||
|
||||
oldGain += oldGainStep;
|
||||
newGain += newGainStep;
|
||||
stepcount += 1.0f;
|
||||
Offset++;
|
||||
}
|
||||
newparams->Gain = newGain;
|
||||
newparams->Gain = newGain + newGainStep*stepcount;
|
||||
}
|
||||
|
||||
void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
@@ -100,6 +111,9 @@ void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
ALfloat insample;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(IrSize >= 4);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(i = 0;i < BufferSize;i++)
|
||||
{
|
||||
Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
@@ -6,17 +6,17 @@
|
||||
#include "alu.h"
|
||||
#include "alSource.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
static inline ALfloat point32(const ALfloat *restrict vals, ALsizei UNUSED(frac))
|
||||
static inline ALfloat do_point(const ALfloat *restrict vals, ALsizei UNUSED(frac))
|
||||
{ return vals[0]; }
|
||||
static inline ALfloat lerp32(const ALfloat *restrict vals, ALsizei frac)
|
||||
static inline ALfloat do_lerp(const ALfloat *restrict vals, ALsizei frac)
|
||||
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat fir4_32(const ALfloat *restrict vals, ALsizei frac)
|
||||
{ return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); }
|
||||
static inline ALfloat do_cubic(const ALfloat *restrict vals, ALsizei frac)
|
||||
{ return cubic(vals[0], vals[1], vals[2], vals[3], frac * (1.0f/FRACTIONONE)); }
|
||||
|
||||
|
||||
const ALfloat *Resample_copy32_C(const InterpState* UNUSED(state),
|
||||
const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment),
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
@@ -29,12 +29,14 @@ const ALfloat *Resample_copy32_C(const InterpState* UNUSED(state),
|
||||
return dst;
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Sampler) \
|
||||
const ALfloat *Resample_##Sampler##_C(const InterpState* UNUSED(state), \
|
||||
#define DECL_TEMPLATE(Tag, Sampler, O) \
|
||||
const ALfloat *Resample_##Tag##_C(const InterpState* UNUSED(state), \
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment, \
|
||||
ALfloat *restrict dst, ALsizei numsamples) \
|
||||
{ \
|
||||
ALsizei i; \
|
||||
\
|
||||
src -= O; \
|
||||
for(i = 0;i < numsamples;i++) \
|
||||
{ \
|
||||
dst[i] = Sampler(src, frac); \
|
||||
@@ -46,22 +48,25 @@ const ALfloat *Resample_##Sampler##_C(const InterpState* UNUSED(state), \
|
||||
return dst; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(point32)
|
||||
DECL_TEMPLATE(lerp32)
|
||||
DECL_TEMPLATE(fir4_32)
|
||||
DECL_TEMPLATE(point, do_point, 0)
|
||||
DECL_TEMPLATE(lerp, do_lerp, 0)
|
||||
DECL_TEMPLATE(cubic, do_cubic, 1)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
const ALfloat *Resample_bsinc32_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)
|
||||
{
|
||||
const ALfloat *fil, *scd, *phd, *spd;
|
||||
const ALfloat *const filter = state->bsinc.filter;
|
||||
const ALfloat sf = state->bsinc.sf;
|
||||
const ALsizei m = state->bsinc.m;
|
||||
ALsizei j_f, pi, i;
|
||||
ALfloat pf, r;
|
||||
|
||||
ASSUME(m > 0);
|
||||
|
||||
src += state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
@@ -71,10 +76,10 @@ const ALfloat *Resample_bsinc32_C(const InterpState *state, const ALfloat *restr
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16);
|
||||
scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
|
||||
phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
|
||||
spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
|
||||
fil = ASSUME_ALIGNED(filter + m*pi*4, 16);
|
||||
scd = ASSUME_ALIGNED(fil + m, 16);
|
||||
phd = ASSUME_ALIGNED(scd + m, 16);
|
||||
spd = ASSUME_ALIGNED(phd + m, 16);
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r = 0.0f;
|
||||
@@ -90,47 +95,6 @@ const ALfloat *Resample_bsinc32_C(const InterpState *state, const ALfloat *restr
|
||||
}
|
||||
|
||||
|
||||
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
|
||||
{
|
||||
ALsizei i;
|
||||
if(numsamples > 1)
|
||||
{
|
||||
dst[0] = filter->b0 * src[0] +
|
||||
filter->b1 * filter->x[0] +
|
||||
filter->b2 * filter->x[1] -
|
||||
filter->a1 * filter->y[0] -
|
||||
filter->a2 * filter->y[1];
|
||||
dst[1] = filter->b0 * src[1] +
|
||||
filter->b1 * src[0] +
|
||||
filter->b2 * filter->x[0] -
|
||||
filter->a1 * dst[0] -
|
||||
filter->a2 * filter->y[0];
|
||||
for(i = 2;i < numsamples;i++)
|
||||
dst[i] = filter->b0 * src[i] +
|
||||
filter->b1 * src[i-1] +
|
||||
filter->b2 * src[i-2] -
|
||||
filter->a1 * dst[i-1] -
|
||||
filter->a2 * dst[i-2];
|
||||
filter->x[0] = src[i-1];
|
||||
filter->x[1] = src[i-2];
|
||||
filter->y[0] = dst[i-1];
|
||||
filter->y[1] = dst[i-2];
|
||||
}
|
||||
else if(numsamples == 1)
|
||||
{
|
||||
dst[0] = filter->b0 * src[0] +
|
||||
filter->b1 * filter->x[0] +
|
||||
filter->b2 * filter->x[1] -
|
||||
filter->a1 * filter->y[0] -
|
||||
filter->a2 * filter->y[1];
|
||||
filter->x[1] = filter->x[0];
|
||||
filter->x[0] = src[0];
|
||||
filter->y[1] = filter->y[0];
|
||||
filter->y[0] = dst[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2],
|
||||
@@ -148,8 +112,7 @@ static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
#define MixHrtf MixHrtf_C
|
||||
#define MixHrtfBlend MixHrtfBlend_C
|
||||
#define MixDirectHrtf MixDirectHrtf_C
|
||||
#include "mixer_inc.c"
|
||||
#undef MixHrtf
|
||||
#include "hrtf_inc.c"
|
||||
|
||||
|
||||
void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
@@ -159,6 +122,8 @@ void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[
|
||||
ALfloat gain, delta, step;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
@@ -169,13 +134,16 @@ void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
ALfloat step_count = 0.0f;
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain += step;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -196,6 +164,9 @@ void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict
|
||||
{
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALfloat gain = Gains[c];
|
||||
@@ -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) ALint pos_[4];
|
||||
alignas(16) ALsizei 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 step = (TargetGains[c] - gain) * delta;
|
||||
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -9,20 +9,24 @@
|
||||
|
||||
#include "alSource.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "mixer_defs.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei dstlen)
|
||||
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 ALfloat *fil, *scd, *phd, *spd;
|
||||
ALsizei pi, i, j;
|
||||
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++)
|
||||
{
|
||||
@@ -32,30 +36,32 @@ const ALfloat *Resample_bsinc32_SSE(const InterpState *state, const ALfloat *res
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16);
|
||||
scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
|
||||
phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
|
||||
spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
|
||||
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);
|
||||
#define LD4(x) _mm_load_ps(x)
|
||||
#define ULD4(x) _mm_loadu_ps(x)
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
|
||||
for(j = 0;j < m;j+=4)
|
||||
for(j = 0;j < count;j++)
|
||||
{
|
||||
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
|
||||
const __m128 f4 = MLA4(MLA4(LD4(&fil[j]), sf4, LD4(&scd[j])),
|
||||
pf4, MLA4(LD4(&phd[j]), sf4, LD4(&spd[j]))
|
||||
const __m128 f4 = MLA4(
|
||||
MLA4(fil[j], sf4, scd[j]),
|
||||
pf4, MLA4(phd[j], sf4, spd[j])
|
||||
);
|
||||
/* r += f*src */
|
||||
r4 = MLA4(r4, f4, ULD4(&src[j]));
|
||||
r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j*4]));
|
||||
}
|
||||
#undef MLA4
|
||||
#undef ULD4
|
||||
#undef LD4
|
||||
}
|
||||
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
@@ -126,61 +132,64 @@ static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
#define MixHrtf MixHrtf_SSE
|
||||
#define MixHrtfBlend MixHrtfBlend_SSE
|
||||
#define MixDirectHrtf MixDirectHrtf_SSE
|
||||
#include "mixer_inc.c"
|
||||
#undef MixHrtf
|
||||
#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)
|
||||
{
|
||||
ALfloat gain, delta, step;
|
||||
__m128 gain4;
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALsizei c;
|
||||
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat step = (TargetGains[c] - gain) * delta;
|
||||
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
ALfloat step_count = 0.0f;
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(minsize-pos > 3)
|
||||
if(LIKELY(minsize > 3))
|
||||
{
|
||||
__m128 step4;
|
||||
gain4 = _mm_setr_ps(
|
||||
gain,
|
||||
gain + step,
|
||||
gain + step + step,
|
||||
gain + step + step + step
|
||||
);
|
||||
step4 = _mm_set1_ps(step + step + step + step);
|
||||
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]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
gain4 = _mm_add_ps(gain4, step4);
|
||||
#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(minsize-pos > 3);
|
||||
/* NOTE: gain4 now represents the next four gains after the
|
||||
* last four mixed samples, so the lowest element represents
|
||||
* the next gain to apply.
|
||||
} 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.
|
||||
*/
|
||||
gain = _mm_cvtss_f32(gain4);
|
||||
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;
|
||||
gain += step;
|
||||
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. */
|
||||
@@ -191,13 +200,17 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
|
||||
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
if(LIKELY(BufferSize-pos > 3))
|
||||
{
|
||||
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);
|
||||
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;
|
||||
@@ -206,9 +219,11 @@ void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer
|
||||
|
||||
void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
|
||||
{
|
||||
__m128 gain4;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
@@ -216,13 +231,17 @@ void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restri
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
if(LIKELY(BufferSize > 3))
|
||||
{
|
||||
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);
|
||||
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;
|
||||
@@ -1,9 +1,6 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library, SSE3 mixer functions
|
||||
*
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
|
||||
* Copyright (C) 2015 by Chris Robinson <chris.kcat@gmail.com>.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
@@ -25,69 +22,60 @@
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#include <pmmintrin.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "mixer_defs.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_fir4_32_SSE3(const InterpState* UNUSED(state),
|
||||
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);
|
||||
union { alignas(16) ALint i[4]; float f[4]; } pos_;
|
||||
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
|
||||
ALint pos_[4];
|
||||
ALsizei frac_[4];
|
||||
__m128i frac4, pos4;
|
||||
ALint pos;
|
||||
ALsizei 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]);
|
||||
|
||||
--src;
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
todo = numsamples & ~3;
|
||||
for(i = 0;i < todo;i += 4)
|
||||
{
|
||||
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]);
|
||||
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
|
||||
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
|
||||
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
|
||||
__m128 k0 = _mm_load_ps(sinc4Tab[frac_.i[0]]);
|
||||
__m128 k1 = _mm_load_ps(sinc4Tab[frac_.i[1]]);
|
||||
__m128 k2 = _mm_load_ps(sinc4Tab[frac_.i[2]]);
|
||||
__m128 k3 = _mm_load_ps(sinc4Tab[frac_.i[3]]);
|
||||
__m128 out;
|
||||
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]);
|
||||
|
||||
k0 = _mm_mul_ps(k0, val0);
|
||||
k1 = _mm_mul_ps(k1, val1);
|
||||
k2 = _mm_mul_ps(k2, val2);
|
||||
k3 = _mm_mul_ps(k3, val3);
|
||||
k0 = _mm_hadd_ps(k0, k1);
|
||||
k2 = _mm_hadd_ps(k2, k3);
|
||||
out = _mm_hadd_ps(k0, k2);
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
|
||||
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
|
||||
|
||||
_mm_store_ps(&dst[i], out);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
|
||||
_mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
|
||||
_mm_store_ps(frac_.f, _mm_castsi128_ps(frac4));
|
||||
}
|
||||
|
||||
/* NOTE: These four elements represent the position *after* the last four
|
||||
* samples, so the lowest element is the next position to resample.
|
||||
*/
|
||||
pos = pos_.i[0];
|
||||
frac = frac_.i[0];
|
||||
pos = _mm_cvtsi128_si32(pos4);
|
||||
frac = _mm_cvtsi128_si32(frac4);
|
||||
|
||||
for(;i < numsamples;i++)
|
||||
for(;i < numsamples;++i)
|
||||
{
|
||||
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
@@ -22,33 +22,39 @@
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#include <smmintrin.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "mixer_defs.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE2(const InterpState* UNUSED(state),
|
||||
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);
|
||||
union { alignas(16) ALint i[4]; float f[4]; } pos_;
|
||||
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
|
||||
ALint pos_[4];
|
||||
ALsizei frac_[4];
|
||||
__m128i frac4, pos4;
|
||||
ALint pos;
|
||||
ALsizei 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 +66,15 @@ const ALfloat *Resample_lerp32_SSE2(const InterpState* UNUSED(state),
|
||||
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));
|
||||
}
|
||||
|
||||
/* NOTE: These four elements represent the position *after* the last four
|
||||
* samples, so the lowest element is the next position to resample.
|
||||
*/
|
||||
pos = pos_.i[0];
|
||||
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));
|
||||
|
||||
@@ -1,331 +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"
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp32_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) ALint pos_[4];
|
||||
alignas(16) ALsizei frac_[4];
|
||||
int32x4_t pos4;
|
||||
int32x4_t frac4;
|
||||
ALsizei i;
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
|
||||
|
||||
frac4 = vld1q_s32(frac_);
|
||||
pos4 = vld1q_s32(pos_);
|
||||
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const float32x4_t val1 = (float32x4_t){src[pos_[0]], src[pos_[1]], src[pos_[2]], src[pos_[3]]};
|
||||
const float32x4_t val2 = (float32x4_t){src[pos_[0]+1], src[pos_[1]+1], src[pos_[2]+1], src[pos_[3]+1]};
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const float32x4_t r0 = vsubq_f32(val2, val1);
|
||||
const float32x4_t mu = vmulq_f32(vcvtq_f32_s32(frac4), fracOne4);
|
||||
const float32x4_t out = vmlaq_f32(val1, mu, r0);
|
||||
|
||||
vst1q_f32(&dst[i], out);
|
||||
|
||||
frac4 = vaddq_s32(frac4, increment4);
|
||||
pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
|
||||
frac4 = vandq_s32(frac4, fracMask4);
|
||||
|
||||
vst1q_s32(pos_, pos4);
|
||||
}
|
||||
|
||||
if(i < numsamples)
|
||||
{
|
||||
/* NOTE: These four elements represent the position *after* the last
|
||||
* four samples, so the lowest element is the next position to
|
||||
* resample.
|
||||
*/
|
||||
ALint pos = pos_[0];
|
||||
frac = vgetq_lane_s32(frac4, 0);
|
||||
do {
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
} while(++i < numsamples);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
const ALfloat *Resample_fir4_32_Neon(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
const int32x4_t increment4 = vdupq_n_s32(increment*4);
|
||||
const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
|
||||
alignas(16) ALint pos_[4];
|
||||
alignas(16) ALsizei frac_[4];
|
||||
int32x4_t pos4;
|
||||
int32x4_t frac4;
|
||||
ALsizei i;
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
|
||||
|
||||
frac4 = vld1q_s32(frac_);
|
||||
pos4 = vld1q_s32(pos_);
|
||||
|
||||
--src;
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const float32x4_t val0 = vld1q_f32(&src[pos_[0]]);
|
||||
const float32x4_t val1 = vld1q_f32(&src[pos_[1]]);
|
||||
const float32x4_t val2 = vld1q_f32(&src[pos_[2]]);
|
||||
const float32x4_t val3 = vld1q_f32(&src[pos_[3]]);
|
||||
float32x4_t k0 = vld1q_f32(sinc4Tab[frac_[0]]);
|
||||
float32x4_t k1 = vld1q_f32(sinc4Tab[frac_[1]]);
|
||||
float32x4_t k2 = vld1q_f32(sinc4Tab[frac_[2]]);
|
||||
float32x4_t k3 = vld1q_f32(sinc4Tab[frac_[3]]);
|
||||
float32x4_t out;
|
||||
|
||||
k0 = vmulq_f32(k0, val0);
|
||||
k1 = vmulq_f32(k1, val1);
|
||||
k2 = vmulq_f32(k2, val2);
|
||||
k3 = vmulq_f32(k3, val3);
|
||||
k0 = vcombine_f32(vpadd_f32(vget_low_f32(k0), vget_high_f32(k0)),
|
||||
vpadd_f32(vget_low_f32(k1), vget_high_f32(k1)));
|
||||
k2 = vcombine_f32(vpadd_f32(vget_low_f32(k2), vget_high_f32(k2)),
|
||||
vpadd_f32(vget_low_f32(k3), vget_high_f32(k3)));
|
||||
out = vcombine_f32(vpadd_f32(vget_low_f32(k0), vget_high_f32(k0)),
|
||||
vpadd_f32(vget_low_f32(k2), vget_high_f32(k2)));
|
||||
|
||||
vst1q_f32(&dst[i], out);
|
||||
|
||||
frac4 = vaddq_s32(frac4, increment4);
|
||||
pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
|
||||
frac4 = vandq_s32(frac4, fracMask4);
|
||||
|
||||
vst1q_s32(pos_, pos4);
|
||||
vst1q_s32(frac_, frac4);
|
||||
}
|
||||
|
||||
if(i < numsamples)
|
||||
{
|
||||
/* NOTE: These four elements represent the position *after* the last
|
||||
* four samples, so the lowest element is the next position to
|
||||
* resample.
|
||||
*/
|
||||
ALint pos = pos_[0];
|
||||
frac = frac_[0];
|
||||
do {
|
||||
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
} while(++i < numsamples);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
const ALfloat *Resample_bsinc32_Neon(const InterpState *state,
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei dstlen)
|
||||
{
|
||||
const float32x4_t sf4 = vdupq_n_f32(state->bsinc.sf);
|
||||
const ALsizei m = state->bsinc.m;
|
||||
const ALfloat *fil, *scd, *phd, *spd;
|
||||
ALsizei pi, i, j;
|
||||
float32x4_t r4;
|
||||
ALfloat pf;
|
||||
|
||||
src += state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
|
||||
pi = frac >> FRAC_PHASE_BITDIFF;
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
fil = ASSUME_ALIGNED(state->bsinc.coeffs[pi].filter, 16);
|
||||
scd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].scDelta, 16);
|
||||
phd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].phDelta, 16);
|
||||
spd = ASSUME_ALIGNED(state->bsinc.coeffs[pi].spDelta, 16);
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r4 = vdupq_n_f32(0.0f);
|
||||
{
|
||||
const float32x4_t pf4 = vdupq_n_f32(pf);
|
||||
for(j = 0;j < m;j+=4)
|
||||
{
|
||||
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
|
||||
const float32x4_t f4 = vmlaq_f32(vmlaq_f32(vld1q_f32(&fil[j]),
|
||||
sf4, vld1q_f32(&scd[j])),
|
||||
pf4, vmlaq_f32(vld1q_f32(&phd[j]),
|
||||
sf4, vld1q_f32(&spd[j])
|
||||
)
|
||||
);
|
||||
/* r += f*src */
|
||||
r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j]));
|
||||
}
|
||||
}
|
||||
r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
|
||||
vrev64_f32(vget_low_f32(r4))));
|
||||
dst[i] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||
|
||||
frac += increment;
|
||||
src += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
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 "mixer_inc.c"
|
||||
#undef MixHrtf
|
||||
|
||||
|
||||
void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
ALfloat gain, delta, step;
|
||||
float32x4_t gain4;
|
||||
ALsizei c;
|
||||
|
||||
data = ASSUME_ALIGNED(data, 16);
|
||||
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
|
||||
|
||||
delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
gain = CurrentGains[c];
|
||||
step = (TargetGains[c] - gain) * delta;
|
||||
if(fabsf(step) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(minsize-pos > 3)
|
||||
{
|
||||
float32x4_t step4;
|
||||
gain4 = vsetq_lane_f32(gain, gain4, 0);
|
||||
gain4 = vsetq_lane_f32(gain + step, gain4, 1);
|
||||
gain4 = vsetq_lane_f32(gain + step + step, gain4, 2);
|
||||
gain4 = vsetq_lane_f32(gain + step + step + step, gain4, 3);
|
||||
step4 = vdupq_n_f32(step + step + step + step);
|
||||
do {
|
||||
const float32x4_t val4 = vld1q_f32(&data[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
gain4 = vaddq_f32(gain4, step4);
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
pos += 4;
|
||||
} while(minsize-pos > 3);
|
||||
/* NOTE: gain4 now represents the next four gains after the
|
||||
* last four mixed samples, so the lowest element represents
|
||||
* the next gain to apply.
|
||||
*/
|
||||
gain = vgetq_lane_f32(gain4, 0);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain += step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = TargetGains[c];
|
||||
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;
|
||||
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 = vmlaq_f32(dry4, val4, gain4);
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
|
||||
void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
|
||||
{
|
||||
float32x4_t gain4;
|
||||
ALsizei c;
|
||||
|
||||
data = ASSUME_ALIGNED(data, 16);
|
||||
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
gain4 = vdupq_n_f32(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
vst1q_f32(&OutBuffer[pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[pos] += data[c][InPos+pos]*gain;
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#include <smmintrin.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE41(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
union { alignas(16) ALint i[4]; float f[4]; } pos_;
|
||||
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALint pos;
|
||||
ALsizei i;
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
|
||||
|
||||
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
|
||||
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
|
||||
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
|
||||
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
|
||||
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
|
||||
|
||||
_mm_store_ps(&dst[i], out);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
|
||||
pos_.i[0] = _mm_extract_epi32(pos4, 0);
|
||||
pos_.i[1] = _mm_extract_epi32(pos4, 1);
|
||||
pos_.i[2] = _mm_extract_epi32(pos4, 2);
|
||||
pos_.i[3] = _mm_extract_epi32(pos4, 3);
|
||||
}
|
||||
|
||||
/* NOTE: These four elements represent the position *after* the last four
|
||||
* samples, so the lowest element is the next position to resample.
|
||||
*/
|
||||
pos = pos_.i[0];
|
||||
frac = _mm_cvtsi128_si32(frac4);
|
||||
|
||||
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_fir4_32_SSE41(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
union { alignas(16) ALint i[4]; float f[4]; } pos_;
|
||||
union { alignas(16) ALsizei i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALint pos;
|
||||
ALsizei i;
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
|
||||
|
||||
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
|
||||
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
|
||||
|
||||
--src;
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const __m128 val0 = _mm_loadu_ps(&src[pos_.i[0]]);
|
||||
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
|
||||
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
|
||||
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
|
||||
__m128 k0 = _mm_load_ps(sinc4Tab[frac_.i[0]]);
|
||||
__m128 k1 = _mm_load_ps(sinc4Tab[frac_.i[1]]);
|
||||
__m128 k2 = _mm_load_ps(sinc4Tab[frac_.i[2]]);
|
||||
__m128 k3 = _mm_load_ps(sinc4Tab[frac_.i[3]]);
|
||||
__m128 out;
|
||||
|
||||
k0 = _mm_mul_ps(k0, val0);
|
||||
k1 = _mm_mul_ps(k1, val1);
|
||||
k2 = _mm_mul_ps(k2, val2);
|
||||
k3 = _mm_mul_ps(k3, val3);
|
||||
k0 = _mm_hadd_ps(k0, k1);
|
||||
k2 = _mm_hadd_ps(k2, k3);
|
||||
out = _mm_hadd_ps(k0, k2);
|
||||
|
||||
_mm_store_ps(&dst[i], out);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
|
||||
pos_.i[0] = _mm_extract_epi32(pos4, 0);
|
||||
pos_.i[1] = _mm_extract_epi32(pos4, 1);
|
||||
pos_.i[2] = _mm_extract_epi32(pos4, 2);
|
||||
pos_.i[3] = _mm_extract_epi32(pos4, 3);
|
||||
frac_.i[0] = _mm_extract_epi32(frac4, 0);
|
||||
frac_.i[1] = _mm_extract_epi32(frac4, 1);
|
||||
frac_.i[2] = _mm_extract_epi32(frac4, 2);
|
||||
frac_.i[3] = _mm_extract_epi32(frac4, 3);
|
||||
}
|
||||
|
||||
pos = pos_.i[0];
|
||||
frac = frac_.i[0];
|
||||
|
||||
for(;i < numsamples;i++)
|
||||
{
|
||||
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
+264
-174
@@ -33,9 +33,13 @@
|
||||
#include "alBuffer.h"
|
||||
#include "alListener.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "sample_cvt.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include "mixer_defs.h"
|
||||
#include "cpu_caps.h"
|
||||
#include "mixer/defs.h"
|
||||
|
||||
|
||||
static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
|
||||
@@ -44,18 +48,18 @@ static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
|
||||
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
|
||||
|
||||
|
||||
/* BSinc requires up to 11 extra samples before the current position, and 12 after. */
|
||||
static_assert(MAX_PRE_SAMPLES >= 11, "MAX_PRE_SAMPLES must be at least 11!");
|
||||
static_assert(MAX_POST_SAMPLES >= 12, "MAX_POST_SAMPLES must be at least 12!");
|
||||
/* 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;
|
||||
|
||||
static MixerFunc MixSamples = Mix_C;
|
||||
MixerFunc MixSamples = Mix_C;
|
||||
RowMixerFunc MixRowSamples = MixRow_C;
|
||||
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
|
||||
HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
|
||||
static HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
|
||||
|
||||
MixerFunc SelectMixer(void)
|
||||
static MixerFunc SelectMixer(void)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
@@ -68,7 +72,7 @@ MixerFunc SelectMixer(void)
|
||||
return Mix_C;
|
||||
}
|
||||
|
||||
RowMixerFunc SelectRowMixer(void)
|
||||
static RowMixerFunc SelectRowMixer(void)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
@@ -112,48 +116,37 @@ ResamplerFunc SelectResampler(enum Resampler resampler)
|
||||
switch(resampler)
|
||||
{
|
||||
case PointResampler:
|
||||
return Resample_point32_C;
|
||||
return Resample_point_C;
|
||||
case LinearResampler:
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Resample_lerp32_Neon;
|
||||
return Resample_lerp_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE4_1
|
||||
if((CPUCapFlags&CPU_CAP_SSE4_1))
|
||||
return Resample_lerp32_SSE41;
|
||||
return Resample_lerp_SSE41;
|
||||
#endif
|
||||
#ifdef HAVE_SSE2
|
||||
if((CPUCapFlags&CPU_CAP_SSE2))
|
||||
return Resample_lerp32_SSE2;
|
||||
return Resample_lerp_SSE2;
|
||||
#endif
|
||||
return Resample_lerp32_C;
|
||||
return Resample_lerp_C;
|
||||
case FIR4Resampler:
|
||||
return Resample_cubic_C;
|
||||
case BSinc12Resampler:
|
||||
case BSinc24Resampler:
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Resample_fir4_32_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE4_1
|
||||
if((CPUCapFlags&CPU_CAP_SSE4_1))
|
||||
return Resample_fir4_32_SSE41;
|
||||
#endif
|
||||
#ifdef HAVE_SSE3
|
||||
if((CPUCapFlags&CPU_CAP_SSE3))
|
||||
return Resample_fir4_32_SSE3;
|
||||
#endif
|
||||
return Resample_fir4_32_C;
|
||||
case BSincResampler:
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Resample_bsinc32_Neon;
|
||||
return Resample_bsinc_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Resample_bsinc32_SSE;
|
||||
return Resample_bsinc_SSE;
|
||||
#endif
|
||||
return Resample_bsinc32_C;
|
||||
return Resample_bsinc_C;
|
||||
}
|
||||
|
||||
return Resample_point32_C;
|
||||
return Resample_point_C;
|
||||
}
|
||||
|
||||
|
||||
@@ -167,13 +160,20 @@ void aluInitMixer(void)
|
||||
ResamplerDefault = PointResampler;
|
||||
else if(strcasecmp(str, "linear") == 0)
|
||||
ResamplerDefault = LinearResampler;
|
||||
else if(strcasecmp(str, "sinc4") == 0)
|
||||
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)
|
||||
ResamplerDefault = BSincResampler;
|
||||
else if(strcasecmp(str, "cubic") == 0 || strcasecmp(str, "sinc8") == 0)
|
||||
{
|
||||
WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str);
|
||||
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
|
||||
@@ -190,11 +190,26 @@ void aluInitMixer(void)
|
||||
MixHrtfBlendSamples = SelectHrtfBlendMixer();
|
||||
MixHrtfSamples = SelectHrtfMixer();
|
||||
MixSamples = SelectMixer();
|
||||
MixRowSamples = SelectRowMixer();
|
||||
}
|
||||
|
||||
|
||||
static inline ALfloat Sample_ALbyte(ALbyte val)
|
||||
{ return val * (1.0f/128.0f); }
|
||||
static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type,
|
||||
ALuint objid, ALuint param, const char *msg)
|
||||
{
|
||||
AsyncEvent evt;
|
||||
evt.EnumType = enumtype;
|
||||
evt.Type = type;
|
||||
evt.ObjectId = objid;
|
||||
evt.Param = param;
|
||||
strcpy(evt.Message, 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); }
|
||||
@@ -202,45 +217,53 @@ static inline ALfloat Sample_ALshort(ALshort val)
|
||||
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 *dst, const T *src, ALint srcstep, ALsizei samples)\
|
||||
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]); \
|
||||
dst[i] += Sample_##T(src[i*srcstep]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
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 *dst, const ALvoid *src, ALint srcstep, enum FmtType srctype, ALsizei samples)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
HANDLE_FMT(FmtUByte, ALubyte);
|
||||
HANDLE_FMT(FmtShort, ALshort);
|
||||
HANDLE_FMT(FmtFloat, ALfloat);
|
||||
HANDLE_FMT(FmtDouble, ALdouble);
|
||||
HANDLE_FMT(FmtMulaw, ALmulaw);
|
||||
HANDLE_FMT(FmtAlaw, ALalaw);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void SilenceSamples(ALfloat *dst, ALsizei samples)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < samples;i++)
|
||||
dst[i] = 0.0f;
|
||||
#undef HANDLE_FMT
|
||||
}
|
||||
|
||||
|
||||
static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
|
||||
static const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
|
||||
ALfloat *restrict dst, const ALfloat *restrict src,
|
||||
ALsizei numsamples, enum ActiveFilters type)
|
||||
{
|
||||
@@ -248,17 +271,17 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
|
||||
switch(type)
|
||||
{
|
||||
case AF_None:
|
||||
ALfilterState_processPassthru(lpfilter, src, numsamples);
|
||||
ALfilterState_processPassthru(hpfilter, src, numsamples);
|
||||
BiquadFilter_passthru(lpfilter, numsamples);
|
||||
BiquadFilter_passthru(hpfilter, numsamples);
|
||||
break;
|
||||
|
||||
case AF_LowPass:
|
||||
ALfilterState_process(lpfilter, dst, src, numsamples);
|
||||
ALfilterState_processPassthru(hpfilter, dst, numsamples);
|
||||
BiquadFilter_process(lpfilter, dst, src, numsamples);
|
||||
BiquadFilter_passthru(hpfilter, numsamples);
|
||||
return dst;
|
||||
case AF_HighPass:
|
||||
ALfilterState_processPassthru(lpfilter, src, numsamples);
|
||||
ALfilterState_process(hpfilter, dst, src, numsamples);
|
||||
BiquadFilter_passthru(lpfilter, numsamples);
|
||||
BiquadFilter_process(hpfilter, dst, src, numsamples);
|
||||
return dst;
|
||||
|
||||
case AF_BandPass:
|
||||
@@ -267,8 +290,8 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
|
||||
ALfloat temp[256];
|
||||
ALsizei todo = mini(256, numsamples-i);
|
||||
|
||||
ALfilterState_process(lpfilter, temp, src+i, todo);
|
||||
ALfilterState_process(hpfilter, dst+i, temp, todo);
|
||||
BiquadFilter_process(lpfilter, temp, src+i, todo);
|
||||
BiquadFilter_process(hpfilter, dst+i, temp, todo);
|
||||
i += todo;
|
||||
}
|
||||
return dst;
|
||||
@@ -277,11 +300,19 @@ static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter
|
||||
}
|
||||
|
||||
|
||||
ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo)
|
||||
/* 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;
|
||||
@@ -292,11 +323,13 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
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);
|
||||
@@ -308,7 +341,7 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
|
||||
|
||||
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
|
||||
Resample_copy32_C : voice->Resampler);
|
||||
Resample_copy_C : voice->Resampler);
|
||||
|
||||
Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
|
||||
firstpass = true;
|
||||
@@ -322,81 +355,126 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
DataSize64 *= increment;
|
||||
DataSize64 += DataPosFrac+FRACTIONMASK;
|
||||
DataSize64 >>= FRACTIONBITS;
|
||||
DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
|
||||
|
||||
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_POST_SAMPLES+MAX_PRE_SAMPLES;
|
||||
DataSize64 -= MAX_RESAMPLE_PADDING*2;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
DstBufferSize = (ALsizei)((DataSize64+(increment-1)) / increment);
|
||||
DstBufferSize = mini(DstBufferSize, (SamplesToDo-OutPos));
|
||||
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(OutPos+DstBufferSize < SamplesToDo)
|
||||
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->SourceData;
|
||||
ALsizei SrcDataSize;
|
||||
ALfloat *SrcData = Device->TempBuffer[SOURCE_DATA_BUF];
|
||||
ALsizei FilledAmt;
|
||||
|
||||
/* Load the previous samples into the source data first. */
|
||||
memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat));
|
||||
SrcDataSize = MAX_PRE_SAMPLES;
|
||||
/* 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(Source->SourceType == AL_STATIC)
|
||||
if(isstatic)
|
||||
{
|
||||
const ALbuffer *ALBuffer = BufferListItem->buffer;
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
ALsizei DataSize;
|
||||
|
||||
/* Offset buffer data to current channel */
|
||||
Data += chan*SampleSize;
|
||||
/* 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 >= ALBuffer->LoopEnd)
|
||||
if(!BufferLoopItem || DataPosInt >= LoopEnd)
|
||||
{
|
||||
ALsizei SizeToDo = SrcBufferSize - FilledAmt;
|
||||
ALsizei CompLen = 0;
|
||||
ALsizei i;
|
||||
|
||||
BufferLoopItem = NULL;
|
||||
|
||||
/* Load what's left to play from the source buffer, and
|
||||
* clear the rest of the temp buffer */
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize,
|
||||
ALBuffer->SampleLen - DataPosInt);
|
||||
for(i = 0;i < BufferListItem->num_buffers;i++)
|
||||
{
|
||||
const ALbuffer *buffer = BufferListItem->buffers[i];
|
||||
const ALubyte *Data = buffer->data;
|
||||
ALsizei DataSize;
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
if(DataPosInt >= buffer->SampleLen)
|
||||
continue;
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
|
||||
SrcDataSize += SrcBufferSize - SrcDataSize;
|
||||
/* 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 LoopStart = ALBuffer->LoopStart;
|
||||
ALsizei LoopEnd = ALBuffer->LoopEnd;
|
||||
ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopEnd - DataPosInt);
|
||||
ALsizei CompLen = 0;
|
||||
ALsizei i;
|
||||
|
||||
/* Load what's left of this loop iteration, then load
|
||||
* repeats of the loop section */
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, LoopEnd - DataPosInt);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
DataSize = LoopEnd-LoopStart;
|
||||
while(SrcBufferSize > SrcDataSize)
|
||||
for(i = 0;i < BufferListItem->num_buffers;i++)
|
||||
{
|
||||
DataSize = mini(SrcBufferSize - SrcDataSize, DataSize);
|
||||
const ALbuffer *buffer = BufferListItem->buffers[i];
|
||||
const ALubyte *Data = buffer->data;
|
||||
ALsizei DataSize;
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,36 +484,37 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
ALbufferlistitem *tmpiter = BufferListItem;
|
||||
ALsizei pos = DataPosInt;
|
||||
|
||||
while(tmpiter && SrcBufferSize > SrcDataSize)
|
||||
while(tmpiter && SrcBufferSize > FilledAmt)
|
||||
{
|
||||
const ALbuffer *ALBuffer;
|
||||
if((ALBuffer=tmpiter->buffer) != NULL)
|
||||
ALsizei SizeToDo = SrcBufferSize - FilledAmt;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < tmpiter->num_buffers;i++)
|
||||
{
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
ALsizei DataSize = ALBuffer->SampleLen;
|
||||
const ALbuffer *ALBuffer = tmpiter->buffers[i];
|
||||
ALsizei DataSize = ALBuffer ? ALBuffer->SampleLen : 0;
|
||||
|
||||
/* Skip the data already played */
|
||||
if(DataSize <= pos)
|
||||
pos -= DataSize;
|
||||
else
|
||||
if(DataSize > pos)
|
||||
{
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
Data += (pos*NumChannels + chan)*SampleSize;
|
||||
DataSize -= pos;
|
||||
pos -= pos;
|
||||
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
|
||||
DataSize = minu(SizeToDo, DataSize - pos);
|
||||
LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
|
||||
ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
}
|
||||
}
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter && BufferLoopItem)
|
||||
tmpiter = BufferLoopItem;
|
||||
else if(!tmpiter)
|
||||
if(pos > tmpiter->max_samples)
|
||||
pos -= tmpiter->max_samples;
|
||||
else
|
||||
{
|
||||
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
|
||||
SrcDataSize += SrcBufferSize - SrcDataSize;
|
||||
FilledAmt += tmpiter->max_samples - pos;
|
||||
pos = 0;
|
||||
}
|
||||
if(SrcBufferSize > FilledAmt)
|
||||
{
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter) tmpiter = BufferLoopItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -443,20 +522,20 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
/* Store the last source samples used for next time. */
|
||||
memcpy(voice->PrevSamples[chan],
|
||||
&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
|
||||
MAX_PRE_SAMPLES*sizeof(ALfloat)
|
||||
MAX_RESAMPLE_PADDING*sizeof(ALfloat)
|
||||
);
|
||||
|
||||
/* Now resample, then filter and mix to the appropriate outputs. */
|
||||
ResampledData = Resample(&voice->ResampleState,
|
||||
&SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
|
||||
Device->ResampledData, DstBufferSize
|
||||
&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->FilteredData,
|
||||
&parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
|
||||
ResampledData, DstBufferSize, voice->Direct.FilterType
|
||||
);
|
||||
if(!(voice->Flags&VOICE_HAS_HRTF))
|
||||
@@ -471,7 +550,7 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
);
|
||||
else
|
||||
{
|
||||
ALfloat *nfcsamples = Device->NFCtrlData;
|
||||
ALfloat *nfcsamples = Device->TempBuffer[NFC_DATA_BUF];
|
||||
ALsizei chanoffset = 0;
|
||||
|
||||
MixSamples(samples,
|
||||
@@ -483,8 +562,8 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
#define APPLY_NFC_MIX(order) \
|
||||
if(voice->Direct.ChannelsPerOrder[order] > 0) \
|
||||
{ \
|
||||
NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples, \
|
||||
samples, DstBufferSize); \
|
||||
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 \
|
||||
@@ -503,8 +582,8 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
ALsizei fademix = 0;
|
||||
int lidx, ridx;
|
||||
|
||||
lidx = GetChannelIdxByName(Device->RealOut, FrontLeft);
|
||||
ridx = GetChannelIdxByName(Device->RealOut, FrontRight);
|
||||
lidx = GetChannelIdxByName(&Device->RealOut, FrontLeft);
|
||||
ridx = GetChannelIdxByName(&Device->RealOut, FrontRight);
|
||||
assert(lidx != -1 && ridx != -1);
|
||||
|
||||
if(!Counter)
|
||||
@@ -536,7 +615,7 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
*/
|
||||
gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
|
||||
minf(1.0f, (ALfloat)fademix/Counter));
|
||||
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
|
||||
hrtfparams.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;
|
||||
@@ -565,7 +644,7 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
gain = lerp(parms->Hrtf.Old.Gain, gain,
|
||||
(ALfloat)todo/(Counter-fademix));
|
||||
|
||||
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
|
||||
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;
|
||||
@@ -595,7 +674,7 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
continue;
|
||||
|
||||
samples = DoFilters(
|
||||
&parms->LowPass, &parms->HighPass, Device->FilteredData,
|
||||
&parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
|
||||
ResampledData, DstBufferSize, voice->Send[send].FilterType
|
||||
);
|
||||
|
||||
@@ -617,47 +696,50 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
|
||||
firstpass = false;
|
||||
|
||||
/* Handle looping sources */
|
||||
while(1)
|
||||
if(isstatic)
|
||||
{
|
||||
const ALbuffer *ALBuffer;
|
||||
ALsizei DataSize = 0;
|
||||
ALsizei LoopStart = 0;
|
||||
ALsizei LoopEnd = 0;
|
||||
|
||||
if((ALBuffer=BufferListItem->buffer) != NULL)
|
||||
if(BufferLoopItem)
|
||||
{
|
||||
DataSize = ALBuffer->SampleLen;
|
||||
LoopStart = ALBuffer->LoopStart;
|
||||
LoopEnd = ALBuffer->LoopEnd;
|
||||
if(LoopEnd > DataPosInt)
|
||||
break;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
if(BufferLoopItem && Source->SourceType == AL_STATIC)
|
||||
else
|
||||
{
|
||||
assert(LoopEnd > LoopStart);
|
||||
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
|
||||
break;
|
||||
}
|
||||
|
||||
if(DataSize > DataPosInt)
|
||||
break;
|
||||
|
||||
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
|
||||
if(!BufferListItem)
|
||||
{
|
||||
BufferListItem = BufferLoopItem;
|
||||
if(!BufferListItem)
|
||||
/* 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 -= DataSize;
|
||||
buffers_done += BufferListItem->num_buffers;
|
||||
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
|
||||
if(!BufferListItem && !(BufferListItem=BufferLoopItem))
|
||||
{
|
||||
isplaying = false;
|
||||
DataPosInt = 0;
|
||||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
DataPosInt -= BufferListItem->max_samples;
|
||||
}
|
||||
} while(isplaying && OutPos < SamplesToDo);
|
||||
|
||||
@@ -667,5 +749,13 @@ ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei
|
||||
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;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef NFCFILTER_H
|
||||
#define NFCFILTER_H
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct NfcFilter {
|
||||
float g;
|
||||
float coeffs[MAX_AMBI_ORDER*2 + 1];
|
||||
float history[MAX_AMBI_ORDER];
|
||||
} NfcFilter;
|
||||
|
||||
/* NOTE:
|
||||
* w0 = speed_of_sound / (source_distance * sample_rate);
|
||||
* w1 = speed_of_sound / (control_distance * sample_rate);
|
||||
*
|
||||
* Generally speaking, the control distance should be approximately the average
|
||||
* speaker distance, or based on the reference delay if outputing NFC-HOA. It
|
||||
* must not be negative, 0, or infinite. The source distance should not be too
|
||||
* small relative to the control distance.
|
||||
*/
|
||||
|
||||
/* Near-field control filter for first-order ambisonic channels (1-3). */
|
||||
void NfcFilterCreate1(NfcFilter *nfc, const float w0, const float w1);
|
||||
void NfcFilterAdjust1(NfcFilter *nfc, const float w0);
|
||||
void NfcFilterUpdate1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
|
||||
|
||||
/* Near-field control filter for second-order ambisonic channels (4-8). */
|
||||
void NfcFilterCreate2(NfcFilter *nfc, const float w0, const float w1);
|
||||
void NfcFilterAdjust2(NfcFilter *nfc, const float w0);
|
||||
void NfcFilterUpdate2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
|
||||
|
||||
/* Near-field control filter for third-order ambisonic channels (9-15). */
|
||||
void NfcFilterCreate3(NfcFilter *nfc, const float w0, const float w1);
|
||||
void NfcFilterAdjust3(NfcFilter *nfc, const float w0);
|
||||
void NfcFilterUpdate3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
|
||||
|
||||
#endif /* NFCFILTER_H */
|
||||
+228
-234
@@ -29,14 +29,20 @@
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "bool.h"
|
||||
#include "ambdec.h"
|
||||
#include "bformatdec.h"
|
||||
#include "filters/splitter.h"
|
||||
#include "uhjfilter.h"
|
||||
#include "bs2b.h"
|
||||
|
||||
|
||||
extern inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
extern inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
extern inline float ScaleAzimuthFront(float azimuth, float scale);
|
||||
extern inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
extern inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
|
||||
|
||||
static const ALsizei FuMa2ACN[MAX_AMBI_COEFFS] = {
|
||||
@@ -62,58 +68,10 @@ static const ALsizei ACN2ACN[MAX_AMBI_COEFFS] = {
|
||||
8, 9, 10, 11, 12, 13, 14, 15
|
||||
};
|
||||
|
||||
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
|
||||
* coefficients should be divided by these values to get proper N3D scalings.
|
||||
*/
|
||||
static const ALfloat UnitScale[MAX_AMBI_COEFFS] = {
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
static const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
1.000000000f, /* ACN 0 (W), sqrt(1) */
|
||||
1.732050808f, /* ACN 1 (Y), sqrt(3) */
|
||||
1.732050808f, /* ACN 2 (Z), sqrt(3) */
|
||||
1.732050808f, /* ACN 3 (X), sqrt(3) */
|
||||
2.236067978f, /* ACN 4 (V), sqrt(5) */
|
||||
2.236067978f, /* ACN 5 (T), sqrt(5) */
|
||||
2.236067978f, /* ACN 6 (R), sqrt(5) */
|
||||
2.236067978f, /* ACN 7 (S), sqrt(5) */
|
||||
2.236067978f, /* ACN 8 (U), sqrt(5) */
|
||||
2.645751311f, /* ACN 9 (Q), sqrt(7) */
|
||||
2.645751311f, /* ACN 10 (O), sqrt(7) */
|
||||
2.645751311f, /* ACN 11 (M), sqrt(7) */
|
||||
2.645751311f, /* ACN 12 (K), sqrt(7) */
|
||||
2.645751311f, /* ACN 13 (L), sqrt(7) */
|
||||
2.645751311f, /* ACN 14 (N), sqrt(7) */
|
||||
2.645751311f, /* ACN 15 (P), sqrt(7) */
|
||||
};
|
||||
static const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
1.414213562f, /* ACN 0 (W), sqrt(2) */
|
||||
1.732050808f, /* ACN 1 (Y), sqrt(3) */
|
||||
1.732050808f, /* ACN 2 (Z), sqrt(3) */
|
||||
1.732050808f, /* ACN 3 (X), sqrt(3) */
|
||||
1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
|
||||
1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
|
||||
2.236067978f, /* ACN 6 (R), sqrt(5) */
|
||||
1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
|
||||
1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
|
||||
2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
|
||||
1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
|
||||
2.231093404f, /* ACN 11 (M), sqrt(224/45) */
|
||||
2.645751311f, /* ACN 12 (K), sqrt(7) */
|
||||
2.231093404f, /* ACN 13 (L), sqrt(224/45) */
|
||||
1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
|
||||
2.091650066f, /* ACN 15 (P), sqrt(35/8) */
|
||||
};
|
||||
|
||||
|
||||
void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
/* Convert from OpenAL coords to Ambisonics. */
|
||||
ALfloat x = -dir[2];
|
||||
ALfloat y = -dir[0];
|
||||
ALfloat z = dir[1];
|
||||
|
||||
/* Zeroth-order */
|
||||
coeffs[0] = 1.0f; /* ACN 0 = 1 */
|
||||
/* First-order */
|
||||
@@ -193,39 +151,6 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
|
||||
}
|
||||
}
|
||||
|
||||
void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
ALfloat sign = (azimuth < 0.0f) ? -1.0f : 1.0f;
|
||||
if(!(fabsf(azimuth) > F_PI_2))
|
||||
azimuth = minf(fabsf(azimuth) * F_PI_2 / (F_PI/6.0f), F_PI_2) * sign;
|
||||
CalcAngleCoeffs(azimuth, elevation, spread, coeffs);
|
||||
}
|
||||
|
||||
|
||||
void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < numchans;i++)
|
||||
gains[i] = chancoeffs[i][0] * 1.414213562f * ingain;
|
||||
for(;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ALfloat gain = 0.0f;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < numchans;i++)
|
||||
{
|
||||
if(chanmap[i].Index == 0)
|
||||
gain += chanmap[i].Scale;
|
||||
}
|
||||
gains[0] = gain * 1.414213562f * ingain;
|
||||
for(i = 1;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
gains[i] = 0.0f;
|
||||
}
|
||||
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
@@ -329,34 +254,28 @@ typedef struct ChannelMap {
|
||||
ChannelConfig Config;
|
||||
} ChannelMap;
|
||||
|
||||
static void SetChannelMap(const enum Channel *devchans, ChannelConfig *ambicoeffs,
|
||||
const ChannelMap *chanmap, size_t count, ALsizei *outcount)
|
||||
static void SetChannelMap(const enum Channel devchans[MAX_OUTPUT_CHANNELS],
|
||||
ChannelConfig *ambicoeffs, const ChannelMap *chanmap,
|
||||
ALsizei count, ALsizei *outcount)
|
||||
{
|
||||
size_t j, k;
|
||||
ALsizei i;
|
||||
ALsizei maxchans = 0;
|
||||
ALsizei i, j;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS && devchans[i] != InvalidChannel;i++)
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
if(devchans[i] == LFE)
|
||||
ALint idx = GetChannelIndex(devchans, chanmap[i].ChanName);
|
||||
if(idx < 0)
|
||||
{
|
||||
for(j = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
ambicoeffs[i][j] = 0.0f;
|
||||
ERR("Failed to find %s channel in device\n",
|
||||
GetLabelFromChannel(chanmap[i].ChanName));
|
||||
continue;
|
||||
}
|
||||
|
||||
for(j = 0;j < count;j++)
|
||||
{
|
||||
if(devchans[i] != chanmap[j].ChanName)
|
||||
continue;
|
||||
|
||||
for(k = 0;k < MAX_AMBI_COEFFS;++k)
|
||||
ambicoeffs[i][k] = chanmap[j].Config[k];
|
||||
break;
|
||||
}
|
||||
if(j == count)
|
||||
ERR("Failed to match %s channel (%u) in channel map\n", GetLabelFromChannel(devchans[i]), i);
|
||||
maxchans = maxi(maxchans, idx+1);
|
||||
for(j = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
ambicoeffs[idx][j] = chanmap[i].Config[j];
|
||||
}
|
||||
*outcount = i;
|
||||
*outcount = mini(maxchans, MAX_OUTPUT_CHANNELS);
|
||||
}
|
||||
|
||||
static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei speakermap[MAX_OUTPUT_CHANNELS])
|
||||
@@ -365,7 +284,8 @@ static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei sp
|
||||
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
{
|
||||
int c = -1;
|
||||
enum Channel ch;
|
||||
int chidx = -1;
|
||||
|
||||
/* NOTE: AmbDec does not define any standard speaker names, however
|
||||
* for this to work we have to by able to find the output channel
|
||||
@@ -388,62 +308,63 @@ static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei sp
|
||||
* and vice-versa.
|
||||
*/
|
||||
if(alstr_cmp_cstr(conf->Speakers[i].Name, "LF") == 0)
|
||||
c = GetChannelIdxByName(device->RealOut, FrontLeft);
|
||||
ch = FrontLeft;
|
||||
else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RF") == 0)
|
||||
c = GetChannelIdxByName(device->RealOut, FrontRight);
|
||||
ch = FrontRight;
|
||||
else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CE") == 0)
|
||||
c = GetChannelIdxByName(device->RealOut, FrontCenter);
|
||||
ch = FrontCenter;
|
||||
else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LS") == 0)
|
||||
{
|
||||
if(device->FmtChans == DevFmtX51Rear)
|
||||
c = GetChannelIdxByName(device->RealOut, BackLeft);
|
||||
ch = BackLeft;
|
||||
else
|
||||
c = GetChannelIdxByName(device->RealOut, SideLeft);
|
||||
ch = SideLeft;
|
||||
}
|
||||
else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RS") == 0)
|
||||
{
|
||||
if(device->FmtChans == DevFmtX51Rear)
|
||||
c = GetChannelIdxByName(device->RealOut, BackRight);
|
||||
ch = BackRight;
|
||||
else
|
||||
c = GetChannelIdxByName(device->RealOut, SideRight);
|
||||
ch = SideRight;
|
||||
}
|
||||
else if(alstr_cmp_cstr(conf->Speakers[i].Name, "LB") == 0)
|
||||
{
|
||||
if(device->FmtChans == DevFmtX51)
|
||||
c = GetChannelIdxByName(device->RealOut, SideLeft);
|
||||
ch = SideLeft;
|
||||
else
|
||||
c = GetChannelIdxByName(device->RealOut, BackLeft);
|
||||
ch = BackLeft;
|
||||
}
|
||||
else if(alstr_cmp_cstr(conf->Speakers[i].Name, "RB") == 0)
|
||||
{
|
||||
if(device->FmtChans == DevFmtX51)
|
||||
c = GetChannelIdxByName(device->RealOut, SideRight);
|
||||
ch = SideRight;
|
||||
else
|
||||
c = GetChannelIdxByName(device->RealOut, BackRight);
|
||||
ch = BackRight;
|
||||
}
|
||||
else if(alstr_cmp_cstr(conf->Speakers[i].Name, "CB") == 0)
|
||||
c = GetChannelIdxByName(device->RealOut, BackCenter);
|
||||
ch = BackCenter;
|
||||
else
|
||||
{
|
||||
const char *name = alstr_get_cstr(conf->Speakers[i].Name);
|
||||
unsigned int n;
|
||||
char ch;
|
||||
char c;
|
||||
|
||||
if(sscanf(name, "AUX%u%c", &n, &ch) == 1 && n < 16)
|
||||
c = GetChannelIdxByName(device->RealOut, Aux0+n);
|
||||
if(sscanf(name, "AUX%u%c", &n, &c) == 1 && n < 16)
|
||||
ch = Aux0+n;
|
||||
else
|
||||
{
|
||||
ERR("AmbDec speaker label \"%s\" not recognized\n", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(c == -1)
|
||||
chidx = GetChannelIdxByName(&device->RealOut, ch);
|
||||
if(chidx == -1)
|
||||
{
|
||||
ERR("Failed to lookup AmbDec speaker label %s\n",
|
||||
alstr_get_cstr(conf->Speakers[i].Name));
|
||||
return false;
|
||||
}
|
||||
speakermap[i] = c;
|
||||
speakermap[i] = chidx;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -453,31 +374,28 @@ static bool MakeSpeakerMap(ALCdevice *device, const AmbDecConf *conf, ALsizei sp
|
||||
static const ChannelMap MonoCfg[1] = {
|
||||
{ FrontCenter, { 1.0f } },
|
||||
}, StereoCfg[2] = {
|
||||
{ FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 1.19573156e-1f } },
|
||||
{ FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 1.19573156e-1f } },
|
||||
{ FrontLeft, { 5.00000000e-1f, 2.88675135e-1f, 0.0f, 5.52305643e-2f } },
|
||||
{ FrontRight, { 5.00000000e-1f, -2.88675135e-1f, 0.0f, 5.52305643e-2f } },
|
||||
}, QuadCfg[4] = {
|
||||
{ BackLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, -2.04124145e-1f } },
|
||||
{ FrontLeft, { 3.53553391e-1f, 2.04124145e-1f, 0.0f, 2.04124145e-1f } },
|
||||
{ FrontRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, 2.04124145e-1f } },
|
||||
{ BackRight, { 3.53553391e-1f, -2.04124145e-1f, 0.0f, -2.04124145e-1f } },
|
||||
}, X51SideCfg[5] = {
|
||||
{ SideLeft, { 3.33001372e-1f, 1.89085671e-1f, 0.0f, -2.00041334e-1f, -2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } },
|
||||
{ FrontLeft, { 1.47751298e-1f, 1.28994110e-1f, 0.0f, 1.15190495e-1f, 7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } },
|
||||
{ FrontCenter, { 7.73595729e-2f, 0.00000000e+0f, 0.0f, 9.71390298e-2f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 5.18625335e-2f } },
|
||||
{ FrontRight, { 1.47751298e-1f, -1.28994110e-1f, 0.0f, 1.15190495e-1f, -7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } },
|
||||
{ SideRight, { 3.33001372e-1f, -1.89085671e-1f, 0.0f, -2.00041334e-1f, 2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } },
|
||||
}, X51RearCfg[5] = {
|
||||
{ BackLeft, { 3.33001372e-1f, 1.89085671e-1f, 0.0f, -2.00041334e-1f, -2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } },
|
||||
{ FrontLeft, { 1.47751298e-1f, 1.28994110e-1f, 0.0f, 1.15190495e-1f, 7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } },
|
||||
{ FrontCenter, { 7.73595729e-2f, 0.00000000e+0f, 0.0f, 9.71390298e-2f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 5.18625335e-2f } },
|
||||
{ FrontRight, { 1.47751298e-1f, -1.28994110e-1f, 0.0f, 1.15190495e-1f, -7.44949143e-2f, 0.0f, 0.0f, 0.0f, -6.47739980e-3f } },
|
||||
{ BackRight, { 3.33001372e-1f, -1.89085671e-1f, 0.0f, -2.00041334e-1f, 2.12309737e-2f, 0.0f, 0.0f, 0.0f, -1.14573483e-2f } },
|
||||
}, X51SideCfg[4] = {
|
||||
{ SideLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
|
||||
{ FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
|
||||
{ FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
|
||||
{ SideRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
|
||||
}, X51RearCfg[4] = {
|
||||
{ BackLeft, { 3.33000782e-1f, 1.89084803e-1f, 0.0f, -2.00042375e-1f, -2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
|
||||
{ FrontLeft, { 1.88542860e-1f, 1.27709292e-1f, 0.0f, 1.66295695e-1f, 7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
|
||||
{ FrontRight, { 1.88542860e-1f, -1.27709292e-1f, 0.0f, 1.66295695e-1f, -7.30571517e-2f, 0.0f, 0.0f, 0.0f, 2.10901184e-2f } },
|
||||
{ BackRight, { 3.33000782e-1f, -1.89084803e-1f, 0.0f, -2.00042375e-1f, 2.12307769e-2f, 0.0f, 0.0f, 0.0f, -1.14579885e-2f } },
|
||||
}, X61Cfg[6] = {
|
||||
{ SideLeft, { 2.04462744e-1f, 2.17178497e-1f, 0.0f, -4.39990188e-2f, -2.60787329e-2f, 0.0f, 0.0f, 0.0f, -6.87238843e-2f } },
|
||||
{ FrontLeft, { 1.18130342e-1f, 9.34633906e-2f, 0.0f, 1.08553749e-1f, 6.80658795e-2f, 0.0f, 0.0f, 0.0f, 1.08999485e-2f } },
|
||||
{ FrontCenter, { 7.73595729e-2f, 0.00000000e+0f, 0.0f, 9.71390298e-2f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 5.18625335e-2f } },
|
||||
{ FrontRight, { 1.18130342e-1f, -9.34633906e-2f, 0.0f, 1.08553749e-1f, -6.80658795e-2f, 0.0f, 0.0f, 0.0f, 1.08999485e-2f } },
|
||||
{ SideRight, { 2.04462744e-1f, -2.17178497e-1f, 0.0f, -4.39990188e-2f, 2.60787329e-2f, 0.0f, 0.0f, 0.0f, -6.87238843e-2f } },
|
||||
{ SideLeft, { 2.04460341e-1f, 2.17177926e-1f, 0.0f, -4.39996780e-2f, -2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } },
|
||||
{ FrontLeft, { 1.58923161e-1f, 9.21772680e-2f, 0.0f, 1.59658796e-1f, 6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } },
|
||||
{ FrontRight, { 1.58923161e-1f, -9.21772680e-2f, 0.0f, 1.59658796e-1f, -6.66278083e-2f, 0.0f, 0.0f, 0.0f, 3.84686854e-2f } },
|
||||
{ SideRight, { 2.04460341e-1f, -2.17177926e-1f, 0.0f, -4.39996780e-2f, 2.60790269e-2f, 0.0f, 0.0f, 0.0f, -6.87239792e-2f } },
|
||||
{ BackCenter, { 2.50001688e-1f, 0.00000000e+0f, 0.0f, -2.50000094e-1f, 0.00000000e+0f, 0.0f, 0.0f, 0.0f, 6.05133395e-2f } },
|
||||
}, X71Cfg[6] = {
|
||||
{ BackLeft, { 2.04124145e-1f, 1.08880247e-1f, 0.0f, -1.88586120e-1f, -1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, 3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
|
||||
@@ -488,26 +406,22 @@ static const ChannelMap MonoCfg[1] = {
|
||||
{ BackRight, { 2.04124145e-1f, -1.08880247e-1f, 0.0f, -1.88586120e-1f, 1.29099444e-1f, 0.0f, 0.0f, 0.0f, 7.45355993e-2f, -3.73460789e-2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.00000000e+0f } },
|
||||
};
|
||||
|
||||
static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order, bool periphonic)
|
||||
static void InitNearFieldCtrl(ALCdevice *device, ALfloat ctrl_dist, ALsizei order,
|
||||
const ALsizei *restrict chans_per_order)
|
||||
{
|
||||
const char *devname = alstr_get_cstr(device->DeviceName);
|
||||
ALsizei i;
|
||||
|
||||
if(GetConfigValueBool(devname, "decoder", "nfc", 1) && ctrl_dist > 0.0f)
|
||||
{
|
||||
/* NFC is only used when AvgSpeakerDist is greater than 0, and
|
||||
* METERS_PER_UNIT is also greater than 0. In addition, NFC can only be
|
||||
* used when rendering to an ambisonic buffer.
|
||||
/* NFC is only used when AvgSpeakerDist is greater than 0, and can only
|
||||
* be used when rendering to an ambisonic buffer.
|
||||
*/
|
||||
device->AvgSpeakerDist = ctrl_dist;
|
||||
device->AvgSpeakerDist = minf(ctrl_dist, 10.0f);
|
||||
TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist);
|
||||
|
||||
device->Dry.NumChannelsPerOrder[0] = 1;
|
||||
if(periphonic)
|
||||
for(i = 1;i < order+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = (i+1)*(i+1) - i*i;
|
||||
else
|
||||
for(i = 1;i < order+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = (i*2+1) - ((i-1)*2+1);
|
||||
for(i = 0;i < order+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = chans_per_order[i];
|
||||
for(;i < MAX_AMBI_ORDER+1;i++)
|
||||
device->Dry.NumChannelsPerOrder[i] = 0;
|
||||
}
|
||||
@@ -517,7 +431,7 @@ static void InitDistanceComp(ALCdevice *device, const AmbDecConf *conf, const AL
|
||||
{
|
||||
const char *devname = alstr_get_cstr(device->DeviceName);
|
||||
ALfloat maxdist = 0.0f;
|
||||
ALsizei total = 0;
|
||||
size_t total = 0;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
@@ -631,7 +545,7 @@ static void InitPanning(ALCdevice *device)
|
||||
const ALsizei *acnmap = (device->AmbiLayout == AmbiLayout_FuMa) ? FuMa2ACN : ACN2ACN;
|
||||
const ALfloat *n3dscale = (device->AmbiScale == AmbiNorm_FuMa) ? FuMa2N3DScale :
|
||||
(device->AmbiScale == AmbiNorm_SN3D) ? SN3D2N3DScale :
|
||||
/*(device->AmbiScale == AmbiNorm_N3D) ?*/ UnitScale;
|
||||
/*(device->AmbiScale == AmbiNorm_N3D) ?*/ N3D2N3DScale;
|
||||
ALfloat nfc_delay = 0.0f;
|
||||
|
||||
count = (device->AmbiOrder == 3) ? 16 :
|
||||
@@ -654,6 +568,8 @@ static void InitPanning(ALCdevice *device)
|
||||
}
|
||||
else
|
||||
{
|
||||
ALfloat w_scale=1.0f, xyz_scale=1.0f;
|
||||
|
||||
/* FOA output is always ACN+N3D for higher-order ambisonic output.
|
||||
* The upsampler expects this and will convert it for output.
|
||||
*/
|
||||
@@ -666,14 +582,27 @@ static void InitPanning(ALCdevice *device)
|
||||
device->FOAOut.CoeffCount = 0;
|
||||
device->FOAOut.NumChannels = 4;
|
||||
|
||||
ambiup_reset(device->AmbiUp, device);
|
||||
if(device->AmbiOrder >= 3)
|
||||
{
|
||||
w_scale = W_SCALE_3H3P;
|
||||
xyz_scale = XYZ_SCALE_3H3P;
|
||||
}
|
||||
else
|
||||
{
|
||||
w_scale = W_SCALE_2H2P;
|
||||
xyz_scale = XYZ_SCALE_2H2P;
|
||||
}
|
||||
ambiup_reset(device->AmbiUp, device, w_scale, xyz_scale);
|
||||
}
|
||||
|
||||
if(ConfigValueFloat(devname, "decoder", "nfc-ref-delay", &nfc_delay) && nfc_delay > 0.0f)
|
||||
{
|
||||
static const ALsizei chans_per_order[MAX_AMBI_ORDER+1] = {
|
||||
1, 3, 5, 7
|
||||
};
|
||||
nfc_delay = clampf(nfc_delay, 0.001f, 1000.0f);
|
||||
InitNearFieldCtrl(device, nfc_delay * SPEEDOFSOUNDMETRESPERSEC,
|
||||
device->AmbiOrder, true);
|
||||
device->AmbiOrder, chans_per_order);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -684,10 +613,10 @@ static void InitPanning(ALCdevice *device)
|
||||
chanmap, count, &device->Dry.NumChannels);
|
||||
device->Dry.CoeffCount = coeffcount;
|
||||
|
||||
w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE2D_THIRD :
|
||||
(device->Dry.CoeffCount > 4) ? W_SCALE2D_SECOND : 1.0f;
|
||||
xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE2D_THIRD :
|
||||
(device->Dry.CoeffCount > 4) ? XYZ_SCALE2D_SECOND : 1.0f;
|
||||
w_scale = (device->Dry.CoeffCount > 9) ? W_SCALE_3H0P :
|
||||
(device->Dry.CoeffCount > 4) ? W_SCALE_2H0P : 1.0f;
|
||||
xyz_scale = (device->Dry.CoeffCount > 9) ? XYZ_SCALE_3H0P :
|
||||
(device->Dry.CoeffCount > 4) ? XYZ_SCALE_2H0P : 1.0f;
|
||||
|
||||
memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
|
||||
for(i = 0;i < device->Dry.NumChannels;i++)
|
||||
@@ -705,7 +634,7 @@ static void InitPanning(ALCdevice *device)
|
||||
static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
ChannelMap chanmap[MAX_OUTPUT_CHANNELS];
|
||||
const ALfloat *coeff_scale = UnitScale;
|
||||
const ALfloat *coeff_scale = N3D2N3DScale;
|
||||
ALfloat w_scale = 1.0f;
|
||||
ALfloat xyz_scale = 1.0f;
|
||||
ALsizei i, j;
|
||||
@@ -718,26 +647,26 @@ static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const A
|
||||
{
|
||||
if(conf->ChanMask > 0x1ff)
|
||||
{
|
||||
w_scale = W_SCALE3D_THIRD;
|
||||
xyz_scale = XYZ_SCALE3D_THIRD;
|
||||
w_scale = W_SCALE_3H3P;
|
||||
xyz_scale = XYZ_SCALE_3H3P;
|
||||
}
|
||||
else if(conf->ChanMask > 0xf)
|
||||
{
|
||||
w_scale = W_SCALE3D_SECOND;
|
||||
xyz_scale = XYZ_SCALE3D_SECOND;
|
||||
w_scale = W_SCALE_2H2P;
|
||||
xyz_scale = XYZ_SCALE_2H2P;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(conf->ChanMask > 0x1ff)
|
||||
{
|
||||
w_scale = W_SCALE2D_THIRD;
|
||||
xyz_scale = XYZ_SCALE2D_THIRD;
|
||||
w_scale = W_SCALE_3H0P;
|
||||
xyz_scale = XYZ_SCALE_3H0P;
|
||||
}
|
||||
else if(conf->ChanMask > 0xf)
|
||||
{
|
||||
w_scale = W_SCALE2D_SECOND;
|
||||
xyz_scale = XYZ_SCALE2D_SECOND;
|
||||
w_scale = W_SCALE_2H0P;
|
||||
xyz_scale = XYZ_SCALE_2H0P;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,6 +718,8 @@ static void InitCustomPanning(ALCdevice *device, const AmbDecConf *conf, const A
|
||||
|
||||
static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsizei speakermap[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
static const ALsizei chans_per_order2d[MAX_AMBI_ORDER+1] = { 1, 2, 2, 2 };
|
||||
static const ALsizei chans_per_order3d[MAX_AMBI_ORDER+1] = { 1, 3, 5, 7 };
|
||||
ALfloat avg_dist;
|
||||
ALsizei count;
|
||||
ALsizei i;
|
||||
@@ -865,7 +796,7 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsiz
|
||||
avg_dist /= (ALfloat)conf->NumSpeakers;
|
||||
InitNearFieldCtrl(device, avg_dist,
|
||||
(conf->ChanMask > 0x1ff) ? 3 : (conf->ChanMask > 0xf) ? 2 : 1,
|
||||
!!(conf->ChanMask&AMBI_PERIPHONIC_MASK)
|
||||
(conf->ChanMask&AMBI_PERIPHONIC_MASK) ? chans_per_order3d : chans_per_order2d
|
||||
);
|
||||
|
||||
InitDistanceComp(device, conf, speakermap);
|
||||
@@ -874,66 +805,93 @@ static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALsiz
|
||||
static void InitHrtfPanning(ALCdevice *device)
|
||||
{
|
||||
/* NOTE: azimuth goes clockwise. */
|
||||
static const ALfloat AmbiPoints[][2] = {
|
||||
static const struct AngularPoint AmbiPoints[] = {
|
||||
{ DEG2RAD( 90.0f), DEG2RAD( 0.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD( -45.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD( 35.0f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD( 35.2643897f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD( 35.2643897f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD( 35.2643897f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD( 35.2643897f), DEG2RAD( -45.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 0.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 90.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( 180.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( -90.0f) },
|
||||
{ DEG2RAD(-35.0f), DEG2RAD( -45.0f) },
|
||||
{ DEG2RAD(-35.0f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD(-35.0f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD(-35.0f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD( 0.0f), DEG2RAD( -45.0f) },
|
||||
{ DEG2RAD(-35.2643897f), DEG2RAD( 45.0f) },
|
||||
{ DEG2RAD(-35.2643897f), DEG2RAD( 135.0f) },
|
||||
{ DEG2RAD(-35.2643897f), DEG2RAD(-135.0f) },
|
||||
{ DEG2RAD(-35.2643897f), DEG2RAD( -45.0f) },
|
||||
{ DEG2RAD(-90.0f), DEG2RAD( 0.0f) },
|
||||
};
|
||||
static const ALfloat AmbiMatrixFOA[][2][MAX_AMBI_COEFFS] = {
|
||||
{ { 1.88982237e-001f, 0.00000000e+000f, 1.90399923e-001f, 0.00000000e+000f }, { 7.14285714e-002f, 0.00000000e+000f, 1.24646009e-001f, 0.00000000e+000f } },
|
||||
{ { 1.88982237e-001f, 1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f } },
|
||||
{ { 1.88982237e-001f, -1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f } },
|
||||
{ { 1.88982237e-001f, -1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f } },
|
||||
{ { 1.88982237e-001f, 1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f } },
|
||||
{ { 1.88982237e-001f, 0.00000000e+000f, 0.00000000e+000f, 1.88281281e-001f }, { 7.14285714e-002f, 0.00000000e+000f, 0.00000000e+000f, 1.23259031e-001f } },
|
||||
{ { 1.88982237e-001f, -1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.14285714e-002f, -1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f } },
|
||||
{ { 1.88982237e-001f, 0.00000000e+000f, 0.00000000e+000f, -1.88281281e-001f }, { 7.14285714e-002f, 0.00000000e+000f, 0.00000000e+000f, -1.23259031e-001f } },
|
||||
{ { 1.88982237e-001f, 1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.14285714e-002f, 1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f } },
|
||||
{ { 1.88982237e-001f, 1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f } },
|
||||
{ { 1.88982237e-001f, -1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f } },
|
||||
{ { 1.88982237e-001f, -1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, -7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f } },
|
||||
{ { 1.88982237e-001f, 1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f }, { 7.14285714e-002f, 7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f } },
|
||||
{ { 1.88982237e-001f, 0.00000000e+000f, -1.90399923e-001f, 0.00000000e+000f }, { 7.14285714e-002f, 0.00000000e+000f, -1.24646009e-001f, 0.00000000e+000f } }
|
||||
}, AmbiMatrixHOA[][2][MAX_AMBI_COEFFS] = {
|
||||
{ { 1.43315266e-001f, 0.00000000e+000f, 1.90399923e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.18020996e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.26741039e-002f, 0.00000000e+000f, 1.24646009e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.49618920e-001f, 0.00000000e+000f, 0.00000000e+000f } },
|
||||
{ { 1.40852210e-001f, 1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f, 7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f, 9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } },
|
||||
{ { 1.40852210e-001f, -1.09057783e-001f, 1.09208910e-001f, 1.09057783e-001f, -7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, 7.14940135e-002f, 7.13950780e-002f, -9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } },
|
||||
{ { 1.40852210e-001f, -1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f, 7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f, 9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } },
|
||||
{ { 1.40852210e-001f, 1.09057783e-001f, 1.09208910e-001f, -1.09057783e-001f, -7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, 7.14940135e-002f, -7.13950780e-002f, -9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } },
|
||||
{ { 1.39644596e-001f, 0.00000000e+000f, 0.00000000e+000f, 1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, 1.01835015e-001f }, { 7.08127349e-002f, 0.00000000e+000f, 0.00000000e+000f, 1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, 1.29099445e-001f } },
|
||||
{ { 1.39644596e-001f, -1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, -1.01835015e-001f }, { 7.08127349e-002f, -1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, -1.29099445e-001f } },
|
||||
{ { 1.39644596e-001f, 0.00000000e+000f, 0.00000000e+000f, -1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, 1.01835015e-001f }, { 7.08127349e-002f, 0.00000000e+000f, 0.00000000e+000f, -1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, 1.29099445e-001f } },
|
||||
{ { 1.39644596e-001f, 1.88281281e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -5.83538687e-002f, 0.00000000e+000f, -1.01835015e-001f }, { 7.08127349e-002f, 1.23259031e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, -7.39770307e-002f, 0.00000000e+000f, -1.29099445e-001f } },
|
||||
{ { 1.40852210e-001f, 1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f, 7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f, 9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } },
|
||||
{ { 1.40852210e-001f, -1.09057783e-001f, -1.09208910e-001f, 1.09057783e-001f, -7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, -7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, -7.14940135e-002f, 7.13950780e-002f, -9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, -9.71456952e-002f, 0.00000000e+000f } },
|
||||
{ { 1.40852210e-001f, -1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f, 7.58818830e-002f, 7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, -7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f, 9.61978444e-002f, 9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } },
|
||||
{ { 1.40852210e-001f, 1.09057783e-001f, -1.09208910e-001f, -1.09057783e-001f, -7.58818830e-002f, -7.66295578e-002f, -3.28314629e-004f, 7.66295578e-002f, 0.00000000e+000f }, { 7.14251066e-002f, 7.13950780e-002f, -7.14940135e-002f, -7.13950780e-002f, -9.61978444e-002f, -9.71456952e-002f, -4.16214759e-004f, 9.71456952e-002f, 0.00000000e+000f } },
|
||||
{ { 1.43315266e-001f, 0.00000000e+000f, -1.90399923e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.18020996e-001f, 0.00000000e+000f, 0.00000000e+000f }, { 7.26741039e-002f, 0.00000000e+000f, -1.24646009e-001f, 0.00000000e+000f, 0.00000000e+000f, 0.00000000e+000f, 1.49618920e-001f, 0.00000000e+000f, 0.00000000e+000f } },
|
||||
static const ALfloat AmbiMatrixFOA[][MAX_AMBI_COEFFS] = {
|
||||
{ 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f },
|
||||
{ 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f },
|
||||
{ 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f },
|
||||
{ 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f },
|
||||
{ 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f },
|
||||
{ 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f },
|
||||
{ 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f },
|
||||
{ 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f },
|
||||
{ 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f },
|
||||
{ 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f },
|
||||
{ 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f },
|
||||
{ 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f },
|
||||
{ 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f },
|
||||
{ 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f },
|
||||
{ 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f },
|
||||
}, AmbiMatrixHOA[][MAX_AMBI_COEFFS] = {
|
||||
{ 5.55555556e-02f, 0.00000000e+00f, 1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, -5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 5.00000000e-02f, 7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, 8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f },
|
||||
{ 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, -8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f },
|
||||
{ 5.55555556e-02f, -6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 0.00000000e+00f, 0.00000000e+00f, -8.66025404e-02f, 0.00000000e+00f, 1.29099445e-01f },
|
||||
{ 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, -6.12372435e-02f, -6.83467648e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 8.66025404e-02f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f, -1.29099445e-01f },
|
||||
{ 5.55555556e-02f, 6.12372435e-02f, 0.00000000e+00f, 6.12372435e-02f, 6.83467648e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, -5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, -5.00000000e-02f, -4.55645099e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 5.00000000e-02f, -7.14285715e-02f, 5.00000000e-02f, 4.55645099e-02f, 0.00000000e+00f },
|
||||
{ 5.55555556e-02f, 0.00000000e+00f, -1.23717915e-01f, 0.00000000e+00f, 0.00000000e+00f, 0.00000000e+00f },
|
||||
};
|
||||
const ALfloat (*AmbiMatrix)[2][MAX_AMBI_COEFFS] = device->AmbiUp ? AmbiMatrixHOA :
|
||||
AmbiMatrixFOA;
|
||||
ALsizei count = device->AmbiUp ? 9 : 4;
|
||||
static const ALfloat AmbiOrderHFGainFOA[MAX_AMBI_ORDER+1] = {
|
||||
3.00000000e+00f, 1.73205081e+00f
|
||||
}, AmbiOrderHFGainHOA[MAX_AMBI_ORDER+1] = {
|
||||
2.40192231e+00f, 1.86052102e+00f, 9.60768923e-01f
|
||||
};
|
||||
static const ALsizei IndexMap[6] = { 0, 1, 2, 3, 4, 8 };
|
||||
static const ALsizei ChansPerOrder[MAX_AMBI_ORDER+1] = { 1, 3, 2, 0 };
|
||||
const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS] = AmbiMatrixFOA;
|
||||
const ALfloat *restrict AmbiOrderHFGain = AmbiOrderHFGainFOA;
|
||||
ALsizei count = 4;
|
||||
ALsizei i;
|
||||
|
||||
static_assert(COUNTOF(AmbiPoints) <= HRTF_AMBI_MAX_CHANNELS, "HRTF_AMBI_MAX_CHANNELS is too small");
|
||||
static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixFOA), "FOA Ambisonic HRTF mismatch");
|
||||
static_assert(COUNTOF(AmbiPoints) == COUNTOF(AmbiMatrixHOA), "HOA Ambisonic HRTF mismatch");
|
||||
|
||||
if(device->AmbiUp)
|
||||
{
|
||||
AmbiMatrix = AmbiMatrixHOA;
|
||||
AmbiOrderHFGain = AmbiOrderHFGainHOA;
|
||||
count = COUNTOF(IndexMap);
|
||||
}
|
||||
|
||||
device->Hrtf = al_calloc(16, FAM_SIZE(DirectHrtfState, Chan, count));
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
device->Dry.Ambi.Map[i].Scale = 1.0f;
|
||||
device->Dry.Ambi.Map[i].Index = i;
|
||||
device->Dry.Ambi.Map[i].Index = IndexMap[i];
|
||||
}
|
||||
device->Dry.CoeffCount = 0;
|
||||
device->Dry.NumChannels = count;
|
||||
@@ -949,7 +907,8 @@ static void InitHrtfPanning(ALCdevice *device)
|
||||
device->FOAOut.CoeffCount = 0;
|
||||
device->FOAOut.NumChannels = 4;
|
||||
|
||||
ambiup_reset(device->AmbiUp, device);
|
||||
ambiup_reset(device->AmbiUp, device, AmbiOrderHFGainFOA[0] / AmbiOrderHFGain[0],
|
||||
AmbiOrderHFGainFOA[1] / AmbiOrderHFGain[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -960,10 +919,13 @@ static void InitHrtfPanning(ALCdevice *device)
|
||||
|
||||
device->RealOut.NumChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
|
||||
device->Hrtf->IrSize = BuildBFormatHrtf(device->HrtfHandle,
|
||||
device->Hrtf, device->Dry.NumChannels,
|
||||
AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints)
|
||||
BuildBFormatHrtf(device->HrtfHandle,
|
||||
device->Hrtf, device->Dry.NumChannels, AmbiPoints, AmbiMatrix, COUNTOF(AmbiPoints),
|
||||
AmbiOrderHFGain
|
||||
);
|
||||
|
||||
InitNearFieldCtrl(device, device->HrtfHandle->distance, device->AmbiUp ? 2 : 1,
|
||||
ChansPerOrder);
|
||||
}
|
||||
|
||||
static void InitUhjPanning(ALCdevice *device)
|
||||
@@ -1016,6 +978,9 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
device->ChannelDelay[i].Length = 0;
|
||||
}
|
||||
|
||||
al_free(device->Stablizer);
|
||||
device->Stablizer = NULL;
|
||||
|
||||
if(device->FmtChans != DevFmtStereo)
|
||||
{
|
||||
ALsizei speakermap[MAX_OUTPUT_CHANNELS];
|
||||
@@ -1066,25 +1031,20 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
|
||||
if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
|
||||
{
|
||||
ambiup_free(device->AmbiUp);
|
||||
device->AmbiUp = NULL;
|
||||
ambiup_free(&device->AmbiUp);
|
||||
if(!device->AmbiDecoder)
|
||||
device->AmbiDecoder = bformatdec_alloc();
|
||||
}
|
||||
else
|
||||
{
|
||||
bformatdec_free(device->AmbiDecoder);
|
||||
device->AmbiDecoder = NULL;
|
||||
if(device->FmtChans == DevFmtAmbi3D && device->AmbiOrder > 1)
|
||||
bformatdec_free(&device->AmbiDecoder);
|
||||
if(device->FmtChans != DevFmtAmbi3D || device->AmbiOrder < 2)
|
||||
ambiup_free(&device->AmbiUp);
|
||||
else
|
||||
{
|
||||
if(!device->AmbiUp)
|
||||
device->AmbiUp = ambiup_alloc();
|
||||
}
|
||||
else
|
||||
{
|
||||
ambiup_free(device->AmbiUp);
|
||||
device->AmbiUp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(!pconf)
|
||||
@@ -1094,12 +1054,48 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
else
|
||||
InitCustomPanning(device, pconf, speakermap);
|
||||
|
||||
/* Enable the stablizer only for formats that have front-left, front-
|
||||
* right, and front-center outputs.
|
||||
*/
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Rear:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
if(GetConfigValueBool(devname, NULL, "front-stablizer", 0))
|
||||
{
|
||||
/* Initialize band-splitting filters for the front-left and
|
||||
* front-right channels, with a crossover at 5khz (could be
|
||||
* higher).
|
||||
*/
|
||||
ALfloat scale = (ALfloat)(5000.0 / device->Frequency);
|
||||
FrontStablizer *stablizer = al_calloc(16, sizeof(*stablizer));
|
||||
|
||||
bandsplit_init(&stablizer->LFilter, scale);
|
||||
stablizer->RFilter = stablizer->LFilter;
|
||||
|
||||
/* Initialize all-pass filters for all other channels. */
|
||||
splitterap_init(&stablizer->APFilter[0], scale);
|
||||
for(i = 1;i < (size_t)device->RealOut.NumChannels;i++)
|
||||
stablizer->APFilter[i] = stablizer->APFilter[0];
|
||||
|
||||
device->Stablizer = stablizer;
|
||||
}
|
||||
break;
|
||||
case DevFmtMono:
|
||||
case DevFmtStereo:
|
||||
case DevFmtQuad:
|
||||
case DevFmtAmbi3D:
|
||||
break;
|
||||
}
|
||||
TRACE("Front stablizer %s\n", device->Stablizer ? "enabled" : "disabled");
|
||||
|
||||
ambdec_deinit(&conf);
|
||||
return;
|
||||
}
|
||||
|
||||
bformatdec_free(device->AmbiDecoder);
|
||||
device->AmbiDecoder = NULL;
|
||||
bformatdec_free(&device->AmbiDecoder);
|
||||
|
||||
headphones = device->IsHeadphones;
|
||||
if(device->Type != Loopback)
|
||||
@@ -1191,8 +1187,7 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
/* Don't bother with HOA when using full HRTF rendering. Nothing
|
||||
* needs it, and it eases the CPU/memory load.
|
||||
*/
|
||||
ambiup_free(device->AmbiUp);
|
||||
device->AmbiUp = NULL;
|
||||
ambiup_free(&device->AmbiUp);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1217,8 +1212,7 @@ no_hrtf:
|
||||
|
||||
device->Render_Mode = StereoPair;
|
||||
|
||||
ambiup_free(device->AmbiUp);
|
||||
device->AmbiUp = NULL;
|
||||
ambiup_free(&device->AmbiUp);
|
||||
|
||||
bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) ||
|
||||
(hrtf_appreq == Hrtf_Enable)) ? 5 : 0;
|
||||
|
||||
@@ -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 */
|
||||
@@ -22,8 +22,11 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "align.h"
|
||||
#include "atomic.h"
|
||||
#include "threads.h"
|
||||
#include "almalloc.h"
|
||||
#include "compat.h"
|
||||
@@ -39,85 +42,70 @@ struct ll_ringbuffer {
|
||||
size_t size;
|
||||
size_t size_mask;
|
||||
size_t elem_size;
|
||||
int mlocked;
|
||||
|
||||
alignas(16) char buf[];
|
||||
};
|
||||
|
||||
/* Create a new ringbuffer to hold at least `sz' elements of `elem_sz' bytes.
|
||||
* The number of elements is rounded up to the next power of two. */
|
||||
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz)
|
||||
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes)
|
||||
{
|
||||
ll_ringbuffer_t *rb;
|
||||
ALuint power_of_two;
|
||||
size_t power_of_two = 0;
|
||||
|
||||
power_of_two = NextPowerOf2(sz);
|
||||
if(power_of_two < sz)
|
||||
return NULL;
|
||||
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 = power_of_two;
|
||||
rb->size_mask = rb->size - 1;
|
||||
rb->size = limit_writes ? sz : power_of_two;
|
||||
rb->size_mask = power_of_two - 1;
|
||||
rb->elem_size = elem_sz;
|
||||
rb->mlocked = 0;
|
||||
return rb;
|
||||
}
|
||||
|
||||
/* Free all data associated with the ringbuffer `rb'. */
|
||||
void ll_ringbuffer_free(ll_ringbuffer_t *rb)
|
||||
{
|
||||
if(rb)
|
||||
{
|
||||
#ifdef USE_MLOCK
|
||||
if(rb->mlocked)
|
||||
munlock(rb, sizeof(*rb) + rb->size*rb->elem_size);
|
||||
#endif /* USE_MLOCK */
|
||||
al_free(rb);
|
||||
}
|
||||
al_free(rb);
|
||||
}
|
||||
|
||||
/* Lock the data block of `rb' using the system call 'mlock'. */
|
||||
int ll_ringbuffer_mlock(ll_ringbuffer_t *rb)
|
||||
{
|
||||
#ifdef USE_MLOCK
|
||||
if(!rb->mlocked && mlock(rb, sizeof(*rb) + rb->size*rb->elem_size))
|
||||
return -1;
|
||||
#endif /* USE_MLOCK */
|
||||
rb->mlocked = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reset the read and write pointers to zero. This is not thread safe. */
|
||||
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*rb->elem_size);
|
||||
memset(rb->buf, 0, (rb->size_mask+1)*rb->elem_size);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
return (w-r) & rb->size_mask;
|
||||
}
|
||||
/* Return the number of elements available for writing. This is the number of
|
||||
* elements in front of the write pointer and behind the read pointer. */
|
||||
|
||||
size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb)
|
||||
{
|
||||
size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
return (r-w-1) & rb->size_mask;
|
||||
w = (r-w-1) & rb->size_mask;
|
||||
return (w > rb->size) ? rb->size : w;
|
||||
}
|
||||
|
||||
/* The copying data reader. Copy at most `cnt' elements from `rb' to `dest'.
|
||||
* Returns the actual number of elements copied. */
|
||||
|
||||
size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
{
|
||||
size_t read_ptr;
|
||||
@@ -133,9 +121,9 @@ size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = read_ptr + to_read;
|
||||
if(cnt2 > rb->size)
|
||||
if(cnt2 > rb->size_mask+1)
|
||||
{
|
||||
n1 = rb->size - read_ptr;
|
||||
n1 = rb->size_mask+1 - read_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
@@ -156,9 +144,6 @@ size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
return to_read;
|
||||
}
|
||||
|
||||
/* The copying data reader w/o read pointer advance. Copy at most `cnt'
|
||||
* elements from `rb' to `dest'. Returns the actual number of elements copied.
|
||||
*/
|
||||
size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
{
|
||||
size_t free_cnt;
|
||||
@@ -174,9 +159,9 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = read_ptr + to_read;
|
||||
if(cnt2 > rb->size)
|
||||
if(cnt2 > rb->size_mask+1)
|
||||
{
|
||||
n1 = rb->size - read_ptr;
|
||||
n1 = rb->size_mask+1 - read_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
@@ -195,8 +180,6 @@ size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
return to_read;
|
||||
}
|
||||
|
||||
/* The copying data writer. Copy at most `cnt' elements to `rb' from `src'.
|
||||
* Returns the actual number of elements copied. */
|
||||
size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
|
||||
{
|
||||
size_t write_ptr;
|
||||
@@ -212,9 +195,9 @@ size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
|
||||
write_ptr = ATOMIC_LOAD(&rb->write_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = write_ptr + to_write;
|
||||
if(cnt2 > rb->size)
|
||||
if(cnt2 > rb->size_mask+1)
|
||||
{
|
||||
n1 = rb->size - write_ptr;
|
||||
n1 = rb->size_mask+1 - write_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
@@ -235,22 +218,19 @@ size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
|
||||
return to_write;
|
||||
}
|
||||
|
||||
/* Advance the read pointer `cnt' places. */
|
||||
|
||||
void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
|
||||
{
|
||||
ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel);
|
||||
}
|
||||
|
||||
/* Advance the write pointer `cnt' places. */
|
||||
void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
|
||||
{
|
||||
ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel);
|
||||
}
|
||||
|
||||
/* The non-copying data reader. `vec' is an array of two places. Set the values
|
||||
* at `vec' to hold the current readable data at `rb'. If the readable data is
|
||||
* in one segment the second segment has zero length. */
|
||||
void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t * vec)
|
||||
|
||||
void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2])
|
||||
{
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
@@ -263,12 +243,12 @@ void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data
|
||||
free_cnt = (w-r) & rb->size_mask;
|
||||
|
||||
cnt2 = r + free_cnt;
|
||||
if(cnt2 > rb->size)
|
||||
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 - r;
|
||||
vec[0].len = rb->size_mask+1 - r;
|
||||
vec[1].buf = (char*)rb->buf;
|
||||
vec[1].len = cnt2 & rb->size_mask;
|
||||
}
|
||||
@@ -282,10 +262,7 @@ void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2])
|
||||
{
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
@@ -296,14 +273,15 @@ void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_dat
|
||||
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)
|
||||
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 - w;
|
||||
vec[0].len = rb->size_mask+1 - w;
|
||||
vec[1].buf = (char*)rb->buf;
|
||||
vec[1].len = cnt2 & rb->size_mask;
|
||||
}
|
||||
@@ -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 */
|
||||
+33
-47
@@ -9,36 +9,29 @@
|
||||
#define MAX_UPDATE_SAMPLES 128
|
||||
|
||||
|
||||
static const ALfloat Filter1Coeff[4] = {
|
||||
0.6923878f, 0.9360654322959f, 0.9882295226860f, 0.9987488452737f
|
||||
static const ALfloat Filter1CoeffSqr[4] = {
|
||||
0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
|
||||
};
|
||||
static const ALfloat Filter2Coeff[4] = {
|
||||
0.4021921162426f, 0.8561710882420f, 0.9722909545651f, 0.9952884791278f
|
||||
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;
|
||||
|
||||
if(todo > 1)
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
|
||||
dst[1] = aa*(src[1] + state->y[0]) - state->x[0];
|
||||
for(i = 2;i < todo;i++)
|
||||
dst[i] = aa*(src[i] + dst[i-2]) - src[i-2];
|
||||
state->x[1] = src[i-2];
|
||||
state->x[0] = src[i-1];
|
||||
state->y[1] = dst[i-2];
|
||||
state->y[0] = dst[i-1];
|
||||
}
|
||||
else if(todo == 1)
|
||||
{
|
||||
dst[0] = aa*(src[0] + state->y[1]) - state->x[1];
|
||||
state->x[1] = state->x[0];
|
||||
state->x[0] = src[0];
|
||||
state->y[1] = state->y[0];
|
||||
state->y[0] = dst[0];
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,41 +61,37 @@ void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict R
|
||||
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],
|
||||
Filter1Coeff[0]*Filter1Coeff[0], todo);
|
||||
allpass_process(&enc->Filter1_Y[1], temp[0], temp[1],
|
||||
Filter1Coeff[1]*Filter1Coeff[1], todo);
|
||||
allpass_process(&enc->Filter1_Y[2], temp[1], temp[0],
|
||||
Filter1Coeff[2]*Filter1Coeff[2], todo);
|
||||
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->Filter1_Y[3].y[0];
|
||||
allpass_process(&enc->Filter1_Y[3], temp[0], temp[1],
|
||||
Filter1Coeff[3]*Filter1Coeff[3], todo);
|
||||
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],
|
||||
Filter2Coeff[0]*Filter2Coeff[0], todo);
|
||||
allpass_process(&enc->Filter2_WX[1], temp[0], temp[1],
|
||||
Filter2Coeff[1]*Filter2Coeff[1], todo);
|
||||
allpass_process(&enc->Filter2_WX[2], temp[1], temp[0],
|
||||
Filter2Coeff[2]*Filter2Coeff[2], todo);
|
||||
allpass_process(&enc->Filter2_WX[3], temp[0], temp[1],
|
||||
Filter2Coeff[3]*Filter2Coeff[3], todo);
|
||||
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];
|
||||
|
||||
@@ -110,17 +99,14 @@ void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict R
|
||||
for(i = 0;i < todo;i++)
|
||||
temp[0][i] = 0.9396926f*InSamples[0][base+i] +
|
||||
0.1855740f*InSamples[1][base+i];
|
||||
allpass_process(&enc->Filter1_WX[0], temp[1], temp[0],
|
||||
Filter1Coeff[0]*Filter1Coeff[0], todo);
|
||||
allpass_process(&enc->Filter1_WX[1], temp[0], temp[1],
|
||||
Filter1Coeff[1]*Filter1Coeff[1], todo);
|
||||
allpass_process(&enc->Filter1_WX[2], temp[1], temp[0],
|
||||
Filter1Coeff[2]*Filter1Coeff[2], todo);
|
||||
S[0] = enc->Filter1_WX[3].y[0];
|
||||
allpass_process(&enc->Filter1_WX[3], temp[0], temp[1],
|
||||
Filter1Coeff[3]*Filter1Coeff[3], todo);
|
||||
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++)
|
||||
|
||||
+3
-3
@@ -6,8 +6,7 @@
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct AllPassState {
|
||||
ALfloat x[2]; /* Last two input samples */
|
||||
ALfloat y[2]; /* Last two output samples */
|
||||
ALfloat z[2];
|
||||
} AllPassState;
|
||||
|
||||
/* Encoding 2-channel UHJ from B-Format is done as:
|
||||
@@ -36,9 +35,10 @@ typedef struct AllPassState {
|
||||
*/
|
||||
|
||||
typedef struct Uhj2Encoder {
|
||||
AllPassState Filter1_WX[4];
|
||||
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
|
||||
|
||||
+2
-1
@@ -37,7 +37,8 @@ typedef const _##N* const_##N;
|
||||
\
|
||||
if(((_x) ? (_x)->Capacity : 0) < _cap) \
|
||||
{ \
|
||||
ptrdiff_t data_offset = (char*)((_x)->Data) - (char*)(_x); \
|
||||
ptrdiff_t data_offset = (_x) ? (char*)((_x)->Data) - (char*)(_x) : \
|
||||
sizeof(*(_x)); \
|
||||
size_t old_size = ((_x) ? (_x)->Size : 0); \
|
||||
void *temp; \
|
||||
\
|
||||
|
||||
+485
-296
File diff suppressed because it is too large
Load Diff
@@ -51,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
|
||||
@@ -98,7 +98,7 @@ works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
@@ -145,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
|
||||
@@ -203,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.
|
||||
@@ -254,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
|
||||
@@ -308,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
|
||||
@@ -349,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
|
||||
@@ -401,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
|
||||
@@ -435,47 +435,3 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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!
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
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
|
||||
|
||||
@@ -29,16 +29,21 @@ struct ALeffectStateVtable {
|
||||
void (*const Destruct)(ALeffectState *state);
|
||||
|
||||
ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device);
|
||||
void (*const update)(ALeffectState *state, const ALCdevice *device, const struct ALeffectslot *slot, const union ALeffectProps *props);
|
||||
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_THUNK3(T, ALeffectState, void, update, const ALCdevice*, const ALeffectslot*, const ALeffectProps*) \
|
||||
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)); } \
|
||||
@@ -54,21 +59,22 @@ 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, \
|
||||
}
|
||||
|
||||
|
||||
@@ -110,17 +116,18 @@ typedef struct ALeffectslot {
|
||||
RefCount ref;
|
||||
|
||||
ATOMIC(struct ALeffectslotProps*) Update;
|
||||
ATOMIC(struct ALeffectslotProps*) FreeList;
|
||||
|
||||
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;
|
||||
@@ -133,9 +140,9 @@ typedef struct ALeffectslot {
|
||||
BFChannelConfig ChanMap[MAX_EFFECT_CHANNELS];
|
||||
/* Wet buffer configuration is ACN channel order with N3D scaling:
|
||||
* * Channel 0 is the unattenuated mono signal.
|
||||
* * Channel 1 is OpenAL -X
|
||||
* * Channel 2 is OpenAL Y
|
||||
* * Channel 3 is OpenAL -Z
|
||||
* * 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 pan (ComputeFirstOrderGains) for
|
||||
@@ -144,44 +151,32 @@ typedef struct ALeffectslot {
|
||||
alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
} ALeffectslot;
|
||||
|
||||
inline void LockEffectSlotsRead(ALCcontext *context)
|
||||
{ LockUIntMapRead(&context->EffectSlotMap); }
|
||||
inline void UnlockEffectSlotsRead(ALCcontext *context)
|
||||
{ UnlockUIntMapRead(&context->EffectSlotMap); }
|
||||
inline void LockEffectSlotsWrite(ALCcontext *context)
|
||||
{ LockUIntMapWrite(&context->EffectSlotMap); }
|
||||
inline void UnlockEffectSlotsWrite(ALCcontext *context)
|
||||
{ UnlockUIntMapWrite(&context->EffectSlotMap); }
|
||||
|
||||
inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALeffectslot*)LookupUIntMapKeyNoLock(&context->EffectSlotMap, id); }
|
||||
inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALeffectslot*)RemoveUIntMapKeyNoLock(&context->EffectSlotMap, id); }
|
||||
|
||||
ALenum InitEffectSlot(ALeffectslot *slot);
|
||||
void DeinitEffectSlot(ALeffectslot *slot);
|
||||
void UpdateEffectSlotProps(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 *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
|
||||
}
|
||||
|
||||
+39
-52
@@ -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,29 +15,25 @@ 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,
|
||||
UserFmtMulaw = AL_MULAW_SOFT,
|
||||
UserFmtAlaw = 0x10000000,
|
||||
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) */
|
||||
UserFmtBFormat2D = AL_BFORMAT2D_SOFT, /* WXY */
|
||||
UserFmtBFormat3D = AL_BFORMAT3D_SOFT, /* WXYZ */
|
||||
UserFmtMono,
|
||||
UserFmtStereo,
|
||||
UserFmtRear,
|
||||
UserFmtQuad,
|
||||
UserFmtX51, /* (WFX order) */
|
||||
UserFmtX61, /* (WFX order) */
|
||||
UserFmtX71, /* (WFX order) */
|
||||
UserFmtBFormat2D, /* WXY */
|
||||
UserFmtBFormat3D, /* WXYZ */
|
||||
};
|
||||
|
||||
ALsizei BytesFromUserFmt(enum UserFmtType type);
|
||||
@@ -44,9 +46,12 @@ inline ALsizei 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,
|
||||
@@ -72,18 +77,17 @@ inline ALsizei 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;
|
||||
ALuint BytesAlloc;
|
||||
ALsizei BytesAlloc;
|
||||
|
||||
enum UserFmtChannels OriginalChannels;
|
||||
enum UserFmtType OriginalType;
|
||||
ALsizei OriginalSize;
|
||||
ALsizei OriginalAlign;
|
||||
enum UserFmtType OriginalType;
|
||||
ALsizei OriginalSize;
|
||||
ALsizei OriginalAlign;
|
||||
|
||||
ALsizei LoopStart;
|
||||
ALsizei LoopEnd;
|
||||
@@ -91,34 +95,17 @@ typedef struct ALbuffer {
|
||||
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 void LockBuffersRead(ALCdevice *device)
|
||||
{ LockUIntMapRead(&device->BufferMap); }
|
||||
inline void UnlockBuffersRead(ALCdevice *device)
|
||||
{ UnlockUIntMapRead(&device->BufferMap); }
|
||||
inline void LockBuffersWrite(ALCdevice *device)
|
||||
{ LockUIntMapWrite(&device->BufferMap); }
|
||||
inline void UnlockBuffersWrite(ALCdevice *device)
|
||||
{ UnlockUIntMapWrite(&device->BufferMap); }
|
||||
|
||||
inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALbuffer*)LookupUIntMapKeyNoLock(&device->BufferMap, id); }
|
||||
inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALbuffer*)RemoveUIntMapKeyNoLock(&device->BufferMap, id); }
|
||||
|
||||
ALvoid ReleaseALBuffers(ALCdevice *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
+54
-37
@@ -10,23 +10,34 @@ extern "C" {
|
||||
struct ALeffect;
|
||||
|
||||
enum {
|
||||
AL__EAXREVERB = 0,
|
||||
AL__REVERB,
|
||||
AL__CHORUS,
|
||||
AL__COMPRESSOR,
|
||||
AL__DISTORTION,
|
||||
AL__ECHO,
|
||||
AL__EQUALIZER,
|
||||
AL__FLANGER,
|
||||
AL__MODULATOR,
|
||||
AL__DEDICATED,
|
||||
EAXREVERB_EFFECT = 0,
|
||||
REVERB_EFFECT,
|
||||
AUTOWAH_EFFECT,
|
||||
CHORUS_EFFECT,
|
||||
COMPRESSOR_EFFECT,
|
||||
DISTORTION_EFFECT,
|
||||
ECHO_EFFECT,
|
||||
EQUALIZER_EFFECT,
|
||||
FLANGER_EFFECT,
|
||||
FSHIFTER_EFFECT,
|
||||
MODULATOR_EFFECT,
|
||||
PSHIFTER_EFFECT,
|
||||
DEDICATED_EFFECT,
|
||||
|
||||
MAX_EFFECTS
|
||||
};
|
||||
extern ALboolean DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
extern ALfloat ReverbBoost;
|
||||
extern ALboolean EmulateEAXReverb;
|
||||
|
||||
struct EffectList {
|
||||
const char name[16];
|
||||
int type;
|
||||
ALenum val;
|
||||
};
|
||||
#define EFFECTLIST_SIZE 14
|
||||
extern const struct EffectList EffectList[EFFECTLIST_SIZE];
|
||||
|
||||
|
||||
struct ALeffectVtable {
|
||||
void (*const setParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val);
|
||||
@@ -50,14 +61,17 @@ const struct ALeffectVtable T##_vtable = { \
|
||||
|
||||
extern const struct ALeffectVtable ALeaxreverb_vtable;
|
||||
extern const struct ALeffectVtable ALreverb_vtable;
|
||||
extern const struct ALeffectVtable ALautowah_vtable;
|
||||
extern const struct ALeffectVtable ALchorus_vtable;
|
||||
extern const struct ALeffectVtable ALcompressor_vtable;
|
||||
extern const struct ALeffectVtable ALdistortion_vtable;
|
||||
extern const struct ALeffectVtable ALecho_vtable;
|
||||
extern const struct ALeffectVtable ALequalizer_vtable;
|
||||
extern const struct ALeffectVtable ALflanger_vtable;
|
||||
extern const struct ALeffectVtable ALfshifter_vtable;
|
||||
extern const struct ALeffectVtable ALmodulator_vtable;
|
||||
extern const struct ALeffectVtable ALnull_vtable;
|
||||
extern const struct ALeffectVtable ALpshifter_vtable;
|
||||
extern const struct ALeffectVtable ALdedicated_vtable;
|
||||
|
||||
|
||||
@@ -91,6 +105,13 @@ typedef union ALeffectProps {
|
||||
ALfloat LFReference;
|
||||
} Reverb;
|
||||
|
||||
struct {
|
||||
ALfloat AttackTime;
|
||||
ALfloat ReleaseTime;
|
||||
ALfloat Resonance;
|
||||
ALfloat PeakGain;
|
||||
} Autowah;
|
||||
|
||||
struct {
|
||||
ALint Waveform;
|
||||
ALint Phase;
|
||||
@@ -98,7 +119,7 @@ typedef union ALeffectProps {
|
||||
ALfloat Depth;
|
||||
ALfloat Feedback;
|
||||
ALfloat Delay;
|
||||
} Chorus;
|
||||
} Chorus; /* Also Flanger */
|
||||
|
||||
struct {
|
||||
ALboolean OnOff;
|
||||
@@ -136,13 +157,10 @@ typedef union ALeffectProps {
|
||||
} Equalizer;
|
||||
|
||||
struct {
|
||||
ALint Waveform;
|
||||
ALint Phase;
|
||||
ALfloat Rate;
|
||||
ALfloat Depth;
|
||||
ALfloat Feedback;
|
||||
ALfloat Delay;
|
||||
} Flanger;
|
||||
ALfloat Frequency;
|
||||
ALint LeftDirection;
|
||||
ALint RightDirection;
|
||||
} Fshifter;
|
||||
|
||||
struct {
|
||||
ALfloat Frequency;
|
||||
@@ -150,6 +168,11 @@ typedef union ALeffectProps {
|
||||
ALint Waveform;
|
||||
} Modulator;
|
||||
|
||||
struct {
|
||||
ALint CoarseTune;
|
||||
ALint FineTune;
|
||||
} Pshifter;
|
||||
|
||||
struct {
|
||||
ALfloat Gain;
|
||||
} Dedicated;
|
||||
@@ -161,33 +184,27 @@ typedef struct ALeffect {
|
||||
|
||||
ALeffectProps Props;
|
||||
|
||||
const struct ALeffectVtable *vtbl;
|
||||
const struct ALeffectVtable *vtab;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALeffect;
|
||||
|
||||
inline void LockEffectsRead(ALCdevice *device)
|
||||
{ LockUIntMapRead(&device->EffectMap); }
|
||||
inline void UnlockEffectsRead(ALCdevice *device)
|
||||
{ UnlockUIntMapRead(&device->EffectMap); }
|
||||
inline void LockEffectsWrite(ALCdevice *device)
|
||||
{ LockUIntMapWrite(&device->EffectMap); }
|
||||
inline void UnlockEffectsWrite(ALCdevice *device)
|
||||
{ UnlockUIntMapWrite(&device->EffectMap); }
|
||||
|
||||
inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALeffect*)LookupUIntMapKeyNoLock(&device->EffectMap, id); }
|
||||
inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALeffect*)RemoveUIntMapKeyNoLock(&device->EffectMap, id); }
|
||||
#define ALeffect_setParami(o, c, p, v) ((o)->vtab->setParami(o, c, p, v))
|
||||
#define ALeffect_setParamf(o, c, p, v) ((o)->vtab->setParamf(o, c, p, v))
|
||||
#define ALeffect_setParamiv(o, c, p, v) ((o)->vtab->setParamiv(o, c, p, v))
|
||||
#define ALeffect_setParamfv(o, c, p, v) ((o)->vtab->setParamfv(o, c, p, v))
|
||||
#define ALeffect_getParami(o, c, p, v) ((o)->vtab->getParami(o, c, p, v))
|
||||
#define ALeffect_getParamf(o, c, p, v) ((o)->vtab->getParamf(o, c, p, v))
|
||||
#define ALeffect_getParamiv(o, c, p, v) ((o)->vtab->getParamiv(o, c, p, v))
|
||||
#define ALeffect_getParamfv(o, c, p, v) ((o)->vtab->getParamfv(o, c, p, v))
|
||||
|
||||
inline ALboolean IsReverbEffect(ALenum type)
|
||||
{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; }
|
||||
|
||||
ALenum InitEffect(ALeffect *effect);
|
||||
ALvoid ReleaseALEffects(ALCdevice *device);
|
||||
void InitEffect(ALeffect *effect);
|
||||
void ReleaseALEffects(ALCdevice *device);
|
||||
|
||||
ALvoid LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _AL_ERROR_H_
|
||||
|
||||
#include "alMain.h"
|
||||
#include "logging.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -9,23 +10,18 @@ extern "C" {
|
||||
|
||||
extern ALboolean TrapALError;
|
||||
|
||||
ALvoid alSetError(ALCcontext *Context, ALenum errorCode);
|
||||
void alSetError(ALCcontext *context, ALenum errorCode, const char *msg, ...) DECL_FORMAT(printf, 3, 4);
|
||||
|
||||
#define SET_ERROR_AND_RETURN(ctx, err) do { \
|
||||
alSetError((ctx), (err)); \
|
||||
return; \
|
||||
} while(0)
|
||||
|
||||
#define SET_ERROR_AND_RETURN_VALUE(ctx, err, val) do { \
|
||||
alSetError((ctx), (err)); \
|
||||
return (val); \
|
||||
} while(0)
|
||||
|
||||
#define SET_ERROR_AND_GOTO(ctx, err, lbl) do { \
|
||||
alSetError((ctx), (err)); \
|
||||
#define SETERR_GOTO(ctx, err, lbl, ...) do { \
|
||||
alSetError((ctx), (err), __VA_ARGS__); \
|
||||
goto lbl; \
|
||||
} while(0)
|
||||
|
||||
#define SETERR_RETURN(ctx, err, retval, ...) do { \
|
||||
alSetError((ctx), (err), __VA_ARGS__); \
|
||||
return retval; \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+29
-126
@@ -1,9 +1,8 @@
|
||||
#ifndef _AL_FILTER_H_
|
||||
#define _AL_FILTER_H_
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#include "math_defs.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -13,99 +12,27 @@ extern "C" {
|
||||
#define HIGHPASSFREQREF (250.0f)
|
||||
|
||||
|
||||
/* Filters implementation is based on the "Cookbook formulae for audio
|
||||
* EQ biquad filter coefficients" by Robert Bristow-Johnson
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
*/
|
||||
/* 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).
|
||||
*/
|
||||
struct ALfilter;
|
||||
|
||||
typedef enum ALfilterType {
|
||||
/** EFX-style low-pass filter, specifying a gain and reference frequency. */
|
||||
ALfilterType_HighShelf,
|
||||
/** EFX-style high-pass filter, specifying a gain and reference frequency. */
|
||||
ALfilterType_LowShelf,
|
||||
/** Peaking filter, specifying a gain and reference frequency. */
|
||||
ALfilterType_Peaking,
|
||||
typedef struct ALfilterVtable {
|
||||
void (*const setParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*const setParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*const setParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*const setParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
|
||||
/** Low-pass cut-off filter, specifying a cut-off frequency. */
|
||||
ALfilterType_LowPass,
|
||||
/** High-pass cut-off filter, specifying a cut-off frequency. */
|
||||
ALfilterType_HighPass,
|
||||
/** Band-pass filter, specifying a center frequency. */
|
||||
ALfilterType_BandPass,
|
||||
} ALfilterType;
|
||||
void (*const getParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*const getParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*const getParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*const getParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
} ALfilterVtable;
|
||||
|
||||
typedef struct ALfilterState {
|
||||
ALfloat x[2]; /* History of two last input samples */
|
||||
ALfloat y[2]; /* History of two last output samples */
|
||||
ALfloat b0, b1, b2; /* Transfer function coefficients "b" */
|
||||
ALfloat a1, a2; /* Transfer function coefficients "a" (a0 is pre-applied) */
|
||||
} ALfilterState;
|
||||
/* Currently only a C-based filter process method is implemented. */
|
||||
#define ALfilterState_process ALfilterState_processC
|
||||
|
||||
/* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
|
||||
* reference gain and shelf slope parameter.
|
||||
* 0 < gain
|
||||
* 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);
|
||||
#define DEFINE_ALFILTER_VTABLE(T) \
|
||||
const struct ALfilterVtable T##_vtable = { \
|
||||
T##_setParami, T##_setParamiv, \
|
||||
T##_setParamf, T##_setParamfv, \
|
||||
T##_getParami, T##_getParamiv, \
|
||||
T##_getParamf, T##_getParamfv, \
|
||||
}
|
||||
/* Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the frequency
|
||||
* multiple (i.e. ref_freq / sampling_freq) and bandwidth.
|
||||
* 0 < freq_mult < 0.5.
|
||||
*/
|
||||
inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth)
|
||||
{
|
||||
ALfloat w0 = F_TAU * freq_mult;
|
||||
return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
|
||||
}
|
||||
|
||||
inline void ALfilterState_clear(ALfilterState *filter)
|
||||
{
|
||||
filter->x[0] = 0.0f;
|
||||
filter->x[1] = 0.0f;
|
||||
filter->y[0] = 0.0f;
|
||||
filter->y[1] = 0.0f;
|
||||
}
|
||||
|
||||
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat rcpQ);
|
||||
|
||||
inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src)
|
||||
{
|
||||
dst->b0 = src->b0;
|
||||
dst->b1 = src->b1;
|
||||
dst->b2 = src->b2;
|
||||
dst->a1 = src->a1;
|
||||
dst->a2 = src->a2;
|
||||
}
|
||||
|
||||
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
|
||||
|
||||
inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples)
|
||||
{
|
||||
if(numsamples >= 2)
|
||||
{
|
||||
filter->x[1] = src[numsamples-2];
|
||||
filter->x[0] = src[numsamples-1];
|
||||
filter->y[1] = src[numsamples-2];
|
||||
filter->y[0] = src[numsamples-1];
|
||||
}
|
||||
else if(numsamples == 1)
|
||||
{
|
||||
filter->x[1] = filter->x[0];
|
||||
filter->x[0] = src[0];
|
||||
filter->y[1] = filter->y[0];
|
||||
filter->y[0] = src[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALfilter {
|
||||
// Filter type (AL_FILTER_NULL, ...)
|
||||
@@ -117,45 +44,21 @@ typedef struct ALfilter {
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
|
||||
void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
|
||||
void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
const struct ALfilterVtable *vtab;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALfilter;
|
||||
#define ALfilter_setParami(o, c, p, v) ((o)->vtab->setParami(o, c, p, v))
|
||||
#define ALfilter_setParamf(o, c, p, v) ((o)->vtab->setParamf(o, c, p, v))
|
||||
#define ALfilter_setParamiv(o, c, p, v) ((o)->vtab->setParamiv(o, c, p, v))
|
||||
#define ALfilter_setParamfv(o, c, p, v) ((o)->vtab->setParamfv(o, c, p, v))
|
||||
#define ALfilter_getParami(o, c, p, v) ((o)->vtab->getParami(o, c, p, v))
|
||||
#define ALfilter_getParamf(o, c, p, v) ((o)->vtab->getParamf(o, c, p, v))
|
||||
#define ALfilter_getParamiv(o, c, p, v) ((o)->vtab->getParamiv(o, c, p, v))
|
||||
#define ALfilter_getParamfv(o, c, p, v) ((o)->vtab->getParamfv(o, c, p, v))
|
||||
|
||||
#define ALfilter_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v)))
|
||||
|
||||
#define ALfilter_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
|
||||
|
||||
inline void LockFiltersRead(ALCdevice *device)
|
||||
{ LockUIntMapRead(&device->FilterMap); }
|
||||
inline void UnlockFiltersRead(ALCdevice *device)
|
||||
{ UnlockUIntMapRead(&device->FilterMap); }
|
||||
inline void LockFiltersWrite(ALCdevice *device)
|
||||
{ LockUIntMapWrite(&device->FilterMap); }
|
||||
inline void UnlockFiltersWrite(ALCdevice *device)
|
||||
{ UnlockUIntMapWrite(&device->FilterMap); }
|
||||
|
||||
inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfilter*)LookupUIntMapKeyNoLock(&device->FilterMap, id); }
|
||||
inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfilter*)RemoveUIntMapKeyNoLock(&device->FilterMap, id); }
|
||||
|
||||
ALvoid ReleaseALFilters(ALCdevice *device);
|
||||
void ReleaseALFilters(ALCdevice *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -8,19 +8,23 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ALcontextProps {
|
||||
ALfloat DopplerFactor;
|
||||
ALfloat DopplerVelocity;
|
||||
ALfloat SpeedOfSound;
|
||||
ALboolean SourceDistanceModel;
|
||||
enum DistanceModel DistanceModel;
|
||||
ALfloat MetersPerUnit;
|
||||
|
||||
ATOMIC(struct ALcontextProps*) next;
|
||||
};
|
||||
|
||||
struct ALlistenerProps {
|
||||
ALfloat Position[3];
|
||||
ALfloat Velocity[3];
|
||||
ALfloat Forward[3];
|
||||
ALfloat Up[3];
|
||||
ALfloat Gain;
|
||||
ALfloat MetersPerUnit;
|
||||
|
||||
ALfloat DopplerFactor;
|
||||
ALfloat DopplerVelocity;
|
||||
ALfloat SpeedOfSound;
|
||||
ALboolean SourceDistanceModel;
|
||||
enum DistanceModel DistanceModel;
|
||||
|
||||
ATOMIC(struct ALlistenerProps*) next;
|
||||
};
|
||||
@@ -31,17 +35,13 @@ typedef struct ALlistener {
|
||||
ALfloat Forward[3];
|
||||
ALfloat Up[3];
|
||||
ALfloat Gain;
|
||||
ALfloat MetersPerUnit;
|
||||
|
||||
ATOMIC_FLAG PropsClean;
|
||||
|
||||
/* Pointer to the most recent property values that are awaiting an update.
|
||||
*/
|
||||
ATOMIC(struct ALlistenerProps*) Update;
|
||||
|
||||
/* A linked list of unused property containers, free to use for future
|
||||
* updates.
|
||||
*/
|
||||
ATOMIC(struct ALlistenerProps*) FreeList;
|
||||
|
||||
struct {
|
||||
aluMatrixf Matrix;
|
||||
aluVector Velocity;
|
||||
@@ -50,7 +50,8 @@ typedef struct ALlistener {
|
||||
ALfloat MetersPerUnit;
|
||||
|
||||
ALfloat DopplerFactor;
|
||||
ALfloat SpeedOfSound;
|
||||
ALfloat SpeedOfSound; /* in units per sec! */
|
||||
ALfloat ReverbSpeedOfSound; /* in meters per sec! */
|
||||
|
||||
ALboolean SourceDistanceModel;
|
||||
enum DistanceModel DistanceModel;
|
||||
|
||||
+395
-567
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,10 @@ struct ALsource;
|
||||
|
||||
|
||||
typedef struct ALbufferlistitem {
|
||||
struct ALbuffer *buffer;
|
||||
ATOMIC(struct ALbufferlistitem*) next;
|
||||
ALsizei max_samples;
|
||||
ALsizei num_buffers;
|
||||
struct ALbuffer *buffers[];
|
||||
} ALbufferlistitem;
|
||||
|
||||
|
||||
@@ -91,32 +93,22 @@ typedef struct ALsource {
|
||||
ALint SourceType;
|
||||
|
||||
/** Source state (initial, playing, paused, or stopped) */
|
||||
ATOMIC(ALenum) state;
|
||||
ALenum state;
|
||||
|
||||
/** Source Buffer Queue head. */
|
||||
RWLock queue_lock;
|
||||
ALbufferlistitem *queue;
|
||||
|
||||
ATOMIC_FLAG PropsClean;
|
||||
|
||||
/* Index into the context's Voices array. Lazily updated, only checked and
|
||||
* reset when looking up the voice.
|
||||
*/
|
||||
ALint VoiceIdx;
|
||||
|
||||
/** Self ID */
|
||||
ALuint id;
|
||||
} ALsource;
|
||||
|
||||
inline void LockSourcesRead(ALCcontext *context)
|
||||
{ LockUIntMapRead(&context->SourceMap); }
|
||||
inline void UnlockSourcesRead(ALCcontext *context)
|
||||
{ UnlockUIntMapRead(&context->SourceMap); }
|
||||
inline void LockSourcesWrite(ALCcontext *context)
|
||||
{ LockUIntMapWrite(&context->SourceMap); }
|
||||
inline void UnlockSourcesWrite(ALCcontext *context)
|
||||
{ UnlockUIntMapWrite(&context->SourceMap); }
|
||||
|
||||
inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)LookupUIntMapKeyNoLock(&context->SourceMap, id); }
|
||||
inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)RemoveUIntMapKeyNoLock(&context->SourceMap, id); }
|
||||
|
||||
void UpdateAllSourceProps(ALCcontext *context);
|
||||
|
||||
ALvoid ReleaseALSources(ALCcontext *Context);
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef ALTHUNK_H
|
||||
#define ALTHUNK_H
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void ThunkInit(void);
|
||||
void ThunkExit(void);
|
||||
ALenum NewThunkEntry(ALuint *index);
|
||||
void FreeThunkEntry(ALuint index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //ALTHUNK_H
|
||||
|
||||
+120
-88
@@ -12,28 +12,27 @@
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alBuffer.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
|
||||
#include "hrtf.h"
|
||||
#include "align.h"
|
||||
#include "nfcfilter.h"
|
||||
#include "math_defs.h"
|
||||
#include "filters/defs.h"
|
||||
#include "filters/nfc.h"
|
||||
|
||||
|
||||
#define MAX_PITCH (255)
|
||||
|
||||
/* Maximum number of buffer samples before the current pos needed for resampling. */
|
||||
#define MAX_PRE_SAMPLES 12
|
||||
|
||||
/* Maximum number of buffer samples after the current pos needed for resampling. */
|
||||
#define MAX_POST_SAMPLES 12
|
||||
/* Maximum number of samples to pad on either end of a buffer for resampling.
|
||||
* Note that both the beginning and end need padding!
|
||||
*/
|
||||
#define MAX_RESAMPLE_PADDING 24
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct BSincTable;
|
||||
struct ALsource;
|
||||
struct ALbufferlistitem;
|
||||
struct ALvoice;
|
||||
@@ -53,13 +52,16 @@ enum Resampler {
|
||||
PointResampler,
|
||||
LinearResampler,
|
||||
FIR4Resampler,
|
||||
BSincResampler,
|
||||
BSinc12Resampler,
|
||||
BSinc24Resampler,
|
||||
|
||||
ResamplerMax = BSincResampler
|
||||
ResamplerMax = BSinc24Resampler
|
||||
};
|
||||
extern enum Resampler ResamplerDefault;
|
||||
|
||||
/* The number of distinct scale and phase intervals within the filter table. */
|
||||
/* The number of distinct scale and phase intervals within the bsinc filter
|
||||
* table.
|
||||
*/
|
||||
#define BSINC_SCALE_BITS 4
|
||||
#define BSINC_SCALE_COUNT (1<<BSINC_SCALE_BITS)
|
||||
#define BSINC_PHASE_BITS 4
|
||||
@@ -71,27 +73,29 @@ extern enum Resampler ResamplerDefault;
|
||||
*/
|
||||
typedef struct BsincState {
|
||||
ALfloat sf; /* Scale interpolation factor. */
|
||||
ALuint m; /* Coefficient count. */
|
||||
ALsizei m; /* Coefficient count. */
|
||||
ALint l; /* Left coefficient offset. */
|
||||
struct {
|
||||
const ALfloat *filter; /* Filter coefficients. */
|
||||
const ALfloat *scDelta; /* Scale deltas. */
|
||||
const ALfloat *phDelta; /* Phase deltas. */
|
||||
const ALfloat *spDelta; /* Scale-phase deltas. */
|
||||
} coeffs[BSINC_PHASE_COUNT];
|
||||
/* Filter coefficients, followed by the scale, phase, and scale-phase
|
||||
* delta coefficients. Starting at phase index 0, each subsequent phase
|
||||
* index follows contiguously.
|
||||
*/
|
||||
const ALfloat *filter;
|
||||
} BsincState;
|
||||
|
||||
typedef union InterpState {
|
||||
BsincState bsinc;
|
||||
} InterpState;
|
||||
|
||||
ALboolean BsincPrepare(const ALuint increment, BsincState *state);
|
||||
|
||||
typedef const ALfloat* (*ResamplerFunc)(const InterpState *state,
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei dstlen
|
||||
);
|
||||
|
||||
void BsincPrepare(const ALuint increment, BsincState *state, const struct BSincTable *table);
|
||||
|
||||
extern const struct BSincTable bsinc12;
|
||||
extern const struct BSincTable bsinc24;
|
||||
|
||||
|
||||
typedef union aluVector {
|
||||
alignas(16) ALfloat v[4];
|
||||
@@ -149,10 +153,10 @@ typedef struct MixHrtfParams {
|
||||
|
||||
|
||||
typedef struct DirectParams {
|
||||
ALfilterState LowPass;
|
||||
ALfilterState HighPass;
|
||||
BiquadFilter LowPass;
|
||||
BiquadFilter HighPass;
|
||||
|
||||
NfcFilter NFCtrlFilter[MAX_AMBI_ORDER];
|
||||
NfcFilter NFCtrlFilter;
|
||||
|
||||
struct {
|
||||
HrtfParams Old;
|
||||
@@ -167,8 +171,8 @@ typedef struct DirectParams {
|
||||
} DirectParams;
|
||||
|
||||
typedef struct SendParams {
|
||||
ALfilterState LowPass;
|
||||
ALfilterState HighPass;
|
||||
BiquadFilter LowPass;
|
||||
BiquadFilter HighPass;
|
||||
|
||||
struct {
|
||||
ALfloat Current[MAX_OUTPUT_CHANNELS];
|
||||
@@ -231,16 +235,15 @@ struct ALvoiceProps {
|
||||
} Send[];
|
||||
};
|
||||
|
||||
/* If not 'fading', gain targets are used directly without fading. */
|
||||
#define VOICE_IS_FADING (1<<0)
|
||||
#define VOICE_HAS_HRTF (1<<1)
|
||||
#define VOICE_HAS_NFC (1<<2)
|
||||
#define VOICE_IS_STATIC (1<<0)
|
||||
#define VOICE_IS_FADING (1<<1) /* Fading sources use gain stepping for smooth transitions. */
|
||||
#define VOICE_HAS_HRTF (1<<2)
|
||||
#define VOICE_HAS_NFC (1<<3)
|
||||
|
||||
typedef struct ALvoice {
|
||||
struct ALvoiceProps *Props;
|
||||
|
||||
ATOMIC(struct ALvoiceProps*) Update;
|
||||
ATOMIC(struct ALvoiceProps*) FreeList;
|
||||
|
||||
ATOMIC(struct ALsource*) Source;
|
||||
ATOMIC(bool) Playing;
|
||||
@@ -277,7 +280,7 @@ typedef struct ALvoice {
|
||||
|
||||
ALuint Offset; /* Number of output samples mixed since starting. */
|
||||
|
||||
alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PRE_SAMPLES];
|
||||
alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_RESAMPLE_PADDING];
|
||||
|
||||
InterpState ResampleState;
|
||||
|
||||
@@ -381,19 +384,26 @@ inline ALuint64 maxu64(ALuint64 a, ALuint64 b)
|
||||
inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
|
||||
{ return minu64(max, maxu64(min, val)); }
|
||||
|
||||
|
||||
extern alignas(16) const ALfloat bsincTab[18840];
|
||||
extern alignas(16) const ALfloat sinc4Tab[FRACTIONONE][4];
|
||||
inline size_t minz(size_t a, size_t b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline size_t maxz(size_t a, size_t b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline size_t clampz(size_t val, size_t min, size_t max)
|
||||
{ return minz(max, maxz(min, val)); }
|
||||
|
||||
|
||||
inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
|
||||
{
|
||||
return val1 + (val2-val1)*mu;
|
||||
}
|
||||
inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALsizei frac)
|
||||
inline ALfloat cubic(ALfloat val1, ALfloat val2, ALfloat val3, ALfloat val4, ALfloat mu)
|
||||
{
|
||||
return sinc4Tab[frac][0]*val0 + sinc4Tab[frac][1]*val1 +
|
||||
sinc4Tab[frac][2]*val2 + sinc4Tab[frac][3]*val3;
|
||||
ALfloat mu2 = mu*mu, mu3 = mu2*mu;
|
||||
ALfloat a0 = -0.5f*mu3 + mu2 + -0.5f*mu;
|
||||
ALfloat a1 = 1.5f*mu3 + -2.5f*mu2 + 1.0f;
|
||||
ALfloat a2 = -1.5f*mu3 + 2.0f*mu2 + 0.5f*mu;
|
||||
ALfloat a3 = 0.5f*mu3 + -0.5f*mu2;
|
||||
return val1*a0 + val2*a1 + val3*a2 + val4*a3;
|
||||
}
|
||||
|
||||
|
||||
@@ -403,10 +413,10 @@ enum HrtfRequestMode {
|
||||
Hrtf_Disable = 2,
|
||||
};
|
||||
|
||||
void aluInit(void);
|
||||
|
||||
void aluInitMixer(void);
|
||||
|
||||
MixerFunc SelectMixer(void);
|
||||
RowMixerFunc SelectRowMixer(void);
|
||||
ResamplerFunc SelectResampler(enum Resampler resampler);
|
||||
|
||||
/* aluInitRenderer
|
||||
@@ -418,14 +428,37 @@ void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf
|
||||
|
||||
void aluInitEffectPanning(struct ALeffectslot *slot);
|
||||
|
||||
void aluSelectPostProcess(ALCdevice *device);
|
||||
|
||||
/**
|
||||
* Calculates ambisonic encoder coefficients using the X, Y, and Z direction
|
||||
* components, which must represent a normalized (unit length) vector, and the
|
||||
* spread is the angular width of the sound (0...tau).
|
||||
*
|
||||
* NOTE: The components use ambisonic coordinates. As a result:
|
||||
*
|
||||
* Ambisonic Y = OpenAL -X
|
||||
* Ambisonic Z = OpenAL Y
|
||||
* Ambisonic X = OpenAL -Z
|
||||
*
|
||||
* The components are ordered such that OpenAL's X, Y, and Z are the first,
|
||||
* second, and third parameters respectively -- simply negate X and Z.
|
||||
*/
|
||||
void CalcAmbiCoeffs(const ALfloat y, const ALfloat z, const ALfloat x, const ALfloat spread,
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
|
||||
/**
|
||||
* CalcDirectionCoeffs
|
||||
*
|
||||
* Calculates ambisonic coefficients based on a direction vector. The vector
|
||||
* must be normalized (unit length), and the spread is the angular width of the
|
||||
* sound (0...tau).
|
||||
* Calculates ambisonic coefficients based on an OpenAL direction vector. The
|
||||
* vector must be normalized (unit length), and the spread is the angular width
|
||||
* of the sound (0...tau).
|
||||
*/
|
||||
void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
inline void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
/* Convert from OpenAL coords to Ambisonics. */
|
||||
CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread, coeffs);
|
||||
}
|
||||
|
||||
/**
|
||||
* CalcAngleCoeffs
|
||||
@@ -436,53 +469,47 @@ void CalcDirectionCoeffs(const ALfloat dir[3], ALfloat spread, ALfloat coeffs[MA
|
||||
*/
|
||||
inline void CalcAngleCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS])
|
||||
{
|
||||
ALfloat dir[3] = {
|
||||
sinf(azimuth) * cosf(elevation),
|
||||
sinf(elevation),
|
||||
-cosf(azimuth) * cosf(elevation)
|
||||
};
|
||||
CalcDirectionCoeffs(dir, spread, coeffs);
|
||||
ALfloat x = -sinf(azimuth) * cosf(elevation);
|
||||
ALfloat y = sinf(elevation);
|
||||
ALfloat z = cosf(azimuth) * cosf(elevation);
|
||||
|
||||
CalcAmbiCoeffs(x, y, z, spread, coeffs);
|
||||
}
|
||||
|
||||
/**
|
||||
* CalcAnglePairwiseCoeffs
|
||||
* ScaleAzimuthFront
|
||||
*
|
||||
* Calculates ambisonic coefficients based on azimuth and elevation. The
|
||||
* azimuth and elevation parameters are in radians, going right and up
|
||||
* respectively. This pairwise variant warps the result such that +30 azimuth
|
||||
* is full right, and -30 azimuth is full left.
|
||||
* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in
|
||||
* front.
|
||||
*/
|
||||
void CalcAnglePairwiseCoeffs(ALfloat azimuth, ALfloat elevation, ALfloat spread, ALfloat coeffs[MAX_AMBI_COEFFS]);
|
||||
inline float ScaleAzimuthFront(float azimuth, float scale)
|
||||
{
|
||||
ALfloat sign = copysignf(1.0f, azimuth);
|
||||
if(!(fabsf(azimuth) > F_PI_2))
|
||||
return minf(fabsf(azimuth) * scale, F_PI_2) * sign;
|
||||
return azimuth;
|
||||
}
|
||||
|
||||
/**
|
||||
* ComputeAmbientGains
|
||||
*
|
||||
* Computes channel gains for ambient, omni-directional sounds.
|
||||
*/
|
||||
#define ComputeAmbientGains(b, g, o) do { \
|
||||
if((b).CoeffCount > 0) \
|
||||
ComputeAmbientGainsMC((b).Ambi.Coeffs, (b).NumChannels, g, o); \
|
||||
else \
|
||||
ComputeAmbientGainsBF((b).Ambi.Map, (b).NumChannels, g, o); \
|
||||
} while (0)
|
||||
void ComputeAmbientGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputeAmbientGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
/**
|
||||
* ComputePanningGains
|
||||
* ComputeDryPanGains
|
||||
*
|
||||
* Computes panning gains using the given channel decoder coefficients and the
|
||||
* pre-calculated direction or angle coefficients.
|
||||
*/
|
||||
#define ComputePanningGains(b, c, g, o) do { \
|
||||
if((b).CoeffCount > 0) \
|
||||
ComputePanningGainsMC((b).Ambi.Coeffs, (b).NumChannels, (b).CoeffCount, c, g, o);\
|
||||
else \
|
||||
ComputePanningGainsBF((b).Ambi.Map, (b).NumChannels, c, g, o); \
|
||||
} while (0)
|
||||
void ComputePanningGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, ALsizei numcoeffs, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
inline void ComputeDryPanGains(const DryMixParams *dry, const ALfloat coeffs[MAX_AMBI_COEFFS], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
if(dry->CoeffCount > 0)
|
||||
ComputePanningGainsMC(dry->Ambi.Coeffs, dry->NumChannels, dry->CoeffCount,
|
||||
coeffs, ingain, gains);
|
||||
else
|
||||
ComputePanningGainsBF(dry->Ambi.Map, dry->NumChannels, coeffs, ingain, gains);
|
||||
}
|
||||
|
||||
void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
/**
|
||||
* ComputeFirstOrderGains
|
||||
*
|
||||
@@ -490,24 +517,29 @@ void ComputePanningGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, con
|
||||
* a 1x4 'slice' of a transform matrix for the input channel, used to scale and
|
||||
* orient the sound samples.
|
||||
*/
|
||||
#define ComputeFirstOrderGains(b, m, g, o) do { \
|
||||
if((b).CoeffCount > 0) \
|
||||
ComputeFirstOrderGainsMC((b).Ambi.Coeffs, (b).NumChannels, m, g, o); \
|
||||
else \
|
||||
ComputeFirstOrderGainsBF((b).Ambi.Map, (b).NumChannels, m, g, o); \
|
||||
} while (0)
|
||||
void ComputeFirstOrderGainsMC(const ChannelConfig *chancoeffs, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
void ComputeFirstOrderGainsBF(const BFChannelConfig *chanmap, ALsizei numchans, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]);
|
||||
inline void ComputeFirstOrderGains(const BFMixParams *foa, const ALfloat mtx[4], ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
if(foa->CoeffCount > 0)
|
||||
ComputeFirstOrderGainsMC(foa->Ambi.Coeffs, foa->NumChannels, mtx, ingain, gains);
|
||||
else
|
||||
ComputeFirstOrderGainsBF(foa->Ambi.Map, foa->NumChannels, mtx, ingain, gains);
|
||||
}
|
||||
|
||||
|
||||
ALboolean MixSource(struct ALvoice *voice, struct ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo);
|
||||
ALboolean MixSource(struct ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo);
|
||||
|
||||
void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples);
|
||||
/* Caller must lock the device. */
|
||||
void aluHandleDisconnect(ALCdevice *device);
|
||||
/* Caller must lock the device, and the mixer must not be running. */
|
||||
void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) DECL_FORMAT(printf, 2, 3);
|
||||
|
||||
void UpdateContextProps(ALCcontext *context);
|
||||
|
||||
extern MixerFunc MixSamples;
|
||||
extern RowMixerFunc MixRowSamples;
|
||||
|
||||
extern ALfloat ConeScale;
|
||||
extern ALfloat ZScale;
|
||||
extern ALboolean OverrideReverbSpeedOfSound;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
#include "AL/al.h"
|
||||
#include "alBuffer.h"
|
||||
|
||||
void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align);
|
||||
extern const ALshort muLawDecompressionTable[256];
|
||||
extern const ALshort aLawDecompressionTable[256];
|
||||
|
||||
void Convert_ALshort_ALima4(ALshort *dst, const ALubyte *src, ALsizei numchans, ALsizei len,
|
||||
ALsizei align);
|
||||
void Convert_ALshort_ALmsadpcm(ALshort *dst, const ALubyte *src, ALsizei numchans, ALsizei len,
|
||||
ALsizei align);
|
||||
|
||||
#endif /* SAMPLE_CVT_H */
|
||||
|
||||
+278
-207
@@ -27,36 +27,81 @@
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alThunk.h"
|
||||
#include "alError.h"
|
||||
#include "alListener.h"
|
||||
#include "alSource.h"
|
||||
|
||||
#include "fpu_modes.h"
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
extern inline void LockEffectSlotsRead(ALCcontext *context);
|
||||
extern inline void UnlockEffectSlotsRead(ALCcontext *context);
|
||||
extern inline void LockEffectSlotsWrite(ALCcontext *context);
|
||||
extern inline void UnlockEffectSlotsWrite(ALCcontext *context);
|
||||
extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id);
|
||||
extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id);
|
||||
extern inline void LockEffectSlotList(ALCcontext *context);
|
||||
extern inline void UnlockEffectSlotList(ALCcontext *context);
|
||||
|
||||
static UIntMap EffectStateFactoryMap;
|
||||
static inline ALeffectStateFactory *getFactoryByType(ALenum type)
|
||||
static void AddActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context);
|
||||
static void RemoveActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context);
|
||||
|
||||
static const struct {
|
||||
ALenum Type;
|
||||
EffectStateFactory* (*GetFactory)(void);
|
||||
} FactoryList[] = {
|
||||
{ AL_EFFECT_NULL, NullStateFactory_getFactory },
|
||||
{ AL_EFFECT_EAXREVERB, ReverbStateFactory_getFactory },
|
||||
{ AL_EFFECT_REVERB, ReverbStateFactory_getFactory },
|
||||
{ AL_EFFECT_AUTOWAH, AutowahStateFactory_getFactory },
|
||||
{ AL_EFFECT_CHORUS, ChorusStateFactory_getFactory },
|
||||
{ AL_EFFECT_COMPRESSOR, CompressorStateFactory_getFactory },
|
||||
{ AL_EFFECT_DISTORTION, DistortionStateFactory_getFactory },
|
||||
{ AL_EFFECT_ECHO, EchoStateFactory_getFactory },
|
||||
{ AL_EFFECT_EQUALIZER, EqualizerStateFactory_getFactory },
|
||||
{ AL_EFFECT_FLANGER, FlangerStateFactory_getFactory },
|
||||
{ AL_EFFECT_FREQUENCY_SHIFTER, FshifterStateFactory_getFactory },
|
||||
{ AL_EFFECT_RING_MODULATOR, ModulatorStateFactory_getFactory },
|
||||
{ AL_EFFECT_PITCH_SHIFTER, PshifterStateFactory_getFactory},
|
||||
{ AL_EFFECT_DEDICATED_DIALOGUE, DedicatedStateFactory_getFactory },
|
||||
{ AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedStateFactory_getFactory }
|
||||
};
|
||||
|
||||
static inline EffectStateFactory *getFactoryByType(ALenum type)
|
||||
{
|
||||
ALeffectStateFactory* (*getFactory)(void) = LookupUIntMapKey(&EffectStateFactoryMap, type);
|
||||
if(getFactory != NULL)
|
||||
return getFactory();
|
||||
size_t i;
|
||||
for(i = 0;i < COUNTOF(FactoryList);i++)
|
||||
{
|
||||
if(FactoryList[i].Type == type)
|
||||
return FactoryList[i].GetFactory();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ALeffectState_IncRef(ALeffectState *state);
|
||||
static void ALeffectState_DecRef(ALeffectState *state);
|
||||
|
||||
|
||||
static inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
|
||||
{
|
||||
id--;
|
||||
if(UNLIKELY(id >= VECTOR_SIZE(context->EffectSlotList)))
|
||||
return NULL;
|
||||
return VECTOR_ELEM(context->EffectSlotList, id);
|
||||
}
|
||||
|
||||
static inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
||||
{
|
||||
EffectSubList *sublist;
|
||||
ALuint lidx = (id-1) >> 6;
|
||||
ALsizei slidx = (id-1) & 0x3f;
|
||||
|
||||
if(UNLIKELY(lidx >= VECTOR_SIZE(device->EffectList)))
|
||||
return NULL;
|
||||
sublist = &VECTOR_ELEM(device->EffectList, lidx);
|
||||
if(UNLIKELY(sublist->FreeMask & (U64(1)<<slidx)))
|
||||
return NULL;
|
||||
return sublist->Effects + slidx;
|
||||
}
|
||||
|
||||
|
||||
#define DO_UPDATEPROPS() do { \
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
|
||||
UpdateEffectSlotProps(slot); \
|
||||
UpdateEffectSlotProps(slot, context); \
|
||||
else \
|
||||
ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release); \
|
||||
} while(0)
|
||||
@@ -64,77 +109,62 @@ static void ALeffectState_DecRef(ALeffectState *state);
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALeffectslot **tmpslots = NULL;
|
||||
ALsizei cur;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
tmpslots = al_malloc(DEF_ALIGN, sizeof(ALeffectslot*)*n);
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Generating %d effect slots", n);
|
||||
if(n == 0) goto done;
|
||||
|
||||
LockEffectSlotsWrite(context);
|
||||
LockEffectSlotList(context);
|
||||
device = context->Device;
|
||||
if(device->AuxiliaryEffectSlotMax - VECTOR_SIZE(context->EffectSlotList) < (ALuint)n)
|
||||
{
|
||||
UnlockEffectSlotList(context);
|
||||
SETERR_GOTO(context, AL_OUT_OF_MEMORY, done, "Exceeding %u auxiliary effect slot limit",
|
||||
device->AuxiliaryEffectSlotMax);
|
||||
}
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot));
|
||||
err = AL_OUT_OF_MEMORY;
|
||||
ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList);
|
||||
ALeffectslotPtr *end = VECTOR_END(context->EffectSlotList);
|
||||
ALeffectslot *slot = NULL;
|
||||
ALenum err = AL_OUT_OF_MEMORY;
|
||||
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(!*iter)
|
||||
break;
|
||||
}
|
||||
if(iter == end)
|
||||
{
|
||||
VECTOR_PUSH_BACK(context->EffectSlotList, NULL);
|
||||
iter = &VECTOR_BACK(context->EffectSlotList);
|
||||
}
|
||||
slot = al_calloc(16, sizeof(ALeffectslot));
|
||||
if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
|
||||
{
|
||||
al_free(slot);
|
||||
UnlockEffectSlotsWrite(context);
|
||||
UnlockEffectSlotList(context);
|
||||
|
||||
alDeleteAuxiliaryEffectSlots(cur, effectslots);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
SETERR_GOTO(context, err, done, "Effect slot object allocation failed");
|
||||
}
|
||||
|
||||
err = NewThunkEntry(&slot->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntryNoLock(&context->EffectSlotMap, slot->id, slot);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
FreeThunkEntry(slot->id);
|
||||
ALeffectState_DecRef(slot->Effect.State);
|
||||
if(slot->Params.EffectState)
|
||||
ALeffectState_DecRef(slot->Params.EffectState);
|
||||
al_free(slot);
|
||||
UnlockEffectSlotsWrite(context);
|
||||
|
||||
alDeleteAuxiliaryEffectSlots(cur, effectslots);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
}
|
||||
|
||||
aluInitEffectPanning(slot);
|
||||
|
||||
tmpslots[cur] = slot;
|
||||
slot->id = (iter - VECTOR_BEGIN(context->EffectSlotList)) + 1;
|
||||
*iter = slot;
|
||||
|
||||
effectslots[cur] = slot->id;
|
||||
}
|
||||
if(n > 0)
|
||||
{
|
||||
struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
|
||||
struct ALeffectslotArray *newarray = NULL;
|
||||
ALsizei newcount = curarray->count + n;
|
||||
ALCdevice *device;
|
||||
|
||||
newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount));
|
||||
newarray->count = newcount;
|
||||
memcpy(newarray->slot, tmpslots, sizeof(ALeffectslot*)*n);
|
||||
if(curarray)
|
||||
memcpy(newarray->slot+n, curarray->slot, sizeof(ALeffectslot*)*curarray->count);
|
||||
|
||||
newarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray,
|
||||
almemory_order_acq_rel);
|
||||
device = context->Device;
|
||||
while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
|
||||
althrd_yield();
|
||||
al_free(newarray);
|
||||
}
|
||||
UnlockEffectSlotsWrite(context);
|
||||
AddActiveEffectSlots(effectslots, n, context);
|
||||
UnlockEffectSlotList(context);
|
||||
|
||||
done:
|
||||
al_free(tmpslots);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -147,54 +177,28 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
LockEffectSlotsWrite(context);
|
||||
LockEffectSlotList(context);
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d effect slots", n);
|
||||
if(n == 0) goto done;
|
||||
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if((slot=LookupEffectSlot(context, effectslots[i])) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u",
|
||||
effectslots[i]);
|
||||
if(ReadRef(&slot->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Deleting in-use effect slot %u",
|
||||
effectslots[i]);
|
||||
}
|
||||
|
||||
// All effectslots are valid
|
||||
if(n > 0)
|
||||
{
|
||||
struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
|
||||
struct ALeffectslotArray *newarray = NULL;
|
||||
ALsizei newcount = curarray->count - n;
|
||||
ALCdevice *device;
|
||||
ALsizei j, k;
|
||||
|
||||
assert(newcount >= 0);
|
||||
newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount));
|
||||
newarray->count = newcount;
|
||||
for(i = j = 0;i < newarray->count;)
|
||||
{
|
||||
slot = curarray->slot[j++];
|
||||
for(k = 0;k < n;k++)
|
||||
{
|
||||
if(slot->id == effectslots[k])
|
||||
break;
|
||||
}
|
||||
if(k == n)
|
||||
newarray->slot[i++] = slot;
|
||||
}
|
||||
|
||||
newarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray,
|
||||
almemory_order_acq_rel);
|
||||
device = context->Device;
|
||||
while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
|
||||
althrd_yield();
|
||||
al_free(newarray);
|
||||
}
|
||||
|
||||
RemoveActiveEffectSlots(effectslots, n, context);
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL)
|
||||
if((slot=LookupEffectSlot(context, effectslots[i])) == NULL)
|
||||
continue;
|
||||
FreeThunkEntry(slot->id);
|
||||
VECTOR_ELEM(context->EffectSlotList, effectslots[i]-1) = NULL;
|
||||
|
||||
DeinitEffectSlot(slot);
|
||||
|
||||
@@ -203,7 +207,7 @@ AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsWrite(context);
|
||||
UnlockEffectSlotList(context);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -215,9 +219,9 @@ AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
|
||||
context = GetContextRef();
|
||||
if(!context) return AL_FALSE;
|
||||
|
||||
LockEffectSlotsRead(context);
|
||||
LockEffectSlotList(context);
|
||||
ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE);
|
||||
UnlockEffectSlotsRead(context);
|
||||
UnlockEffectSlotList(context);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
|
||||
@@ -235,43 +239,45 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
LockEffectSlotsRead(context);
|
||||
almtx_lock(&context->PropLock);
|
||||
LockEffectSlotList(context);
|
||||
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot);
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
device = context->Device;
|
||||
|
||||
LockEffectsRead(device);
|
||||
LockEffectList(device);
|
||||
effect = (value ? LookupEffect(device, value) : NULL);
|
||||
if(!(value == 0 || effect != NULL))
|
||||
{
|
||||
UnlockEffectsRead(device);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
UnlockEffectList(device);
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Invalid effect ID %u", value);
|
||||
}
|
||||
err = InitializeEffect(device, slot, effect);
|
||||
UnlockEffectsRead(device);
|
||||
err = InitializeEffect(context, slot, effect);
|
||||
UnlockEffectList(device);
|
||||
|
||||
if(err != AL_NO_ERROR)
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
SETERR_GOTO(context, err, done, "Effect initialization failed");
|
||||
break;
|
||||
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
if(!(value == AL_TRUE || value == AL_FALSE))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done,
|
||||
"Effect slot auxiliary send auto out of range");
|
||||
slot->AuxSendAuto = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
SETERR_GOTO(context, AL_INVALID_ENUM, done, "Invalid effect slot integer property 0x%04x",
|
||||
param);
|
||||
}
|
||||
DO_UPDATEPROPS();
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsRead(context);
|
||||
WriteUnlock(&context->PropLock);
|
||||
UnlockEffectSlotList(context);
|
||||
almtx_unlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -290,17 +296,18 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum para
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
LockEffectSlotsRead(context);
|
||||
LockEffectSlotList(context);
|
||||
if(LookupEffectSlot(context, effectslot) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
|
||||
param);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsRead(context);
|
||||
UnlockEffectSlotList(context);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -312,26 +319,27 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
LockEffectSlotsRead(context);
|
||||
almtx_lock(&context->PropLock);
|
||||
LockEffectSlotList(context);
|
||||
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot);
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
if(!(value >= 0.0f && value <= 1.0f))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Effect slot gain out of range");
|
||||
slot->Gain = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
SETERR_GOTO(context, AL_INVALID_ENUM, done, "Invalid effect slot float property 0x%04x",
|
||||
param);
|
||||
}
|
||||
DO_UPDATEPROPS();
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsRead(context);
|
||||
WriteUnlock(&context->PropLock);
|
||||
UnlockEffectSlotList(context);
|
||||
almtx_unlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -349,17 +357,18 @@ AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum para
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
LockEffectSlotsRead(context);
|
||||
LockEffectSlotList(context);
|
||||
if(LookupEffectSlot(context, effectslot) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
|
||||
param);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsRead(context);
|
||||
UnlockEffectSlotList(context);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -371,9 +380,9 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
LockEffectSlotsRead(context);
|
||||
LockEffectSlotList(context);
|
||||
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot);
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
@@ -381,11 +390,11 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum pa
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsRead(context);
|
||||
UnlockEffectSlotList(context);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -404,17 +413,18 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum p
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
LockEffectSlotsRead(context);
|
||||
LockEffectSlotList(context);
|
||||
if(LookupEffectSlot(context, effectslot) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x",
|
||||
param);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsRead(context);
|
||||
UnlockEffectSlotList(context);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -426,9 +436,9 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
LockEffectSlotsRead(context);
|
||||
LockEffectSlotList(context);
|
||||
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot);
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
@@ -436,11 +446,11 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum pa
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsRead(context);
|
||||
UnlockEffectSlotList(context);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -458,54 +468,32 @@ AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum p
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
LockEffectSlotsRead(context);
|
||||
LockEffectSlotList(context);
|
||||
if(LookupEffectSlot(context, effectslot) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect slot ID %u", effectslot);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x",
|
||||
param);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockEffectSlotsRead(context);
|
||||
UnlockEffectSlotList(context);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
void InitEffectFactoryMap(void)
|
||||
{
|
||||
InitUIntMap(&EffectStateFactoryMap, INT_MAX);
|
||||
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory);
|
||||
}
|
||||
|
||||
void DeinitEffectFactoryMap(void)
|
||||
{
|
||||
ResetUIntMap(&EffectStateFactoryMap);
|
||||
}
|
||||
|
||||
|
||||
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
|
||||
ALenum InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, ALeffect *effect)
|
||||
{
|
||||
ALCdevice *Device = Context->Device;
|
||||
ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
|
||||
struct ALeffectslotProps *props;
|
||||
ALeffectState *State;
|
||||
|
||||
if(newtype != EffectSlot->Effect.Type)
|
||||
{
|
||||
ALeffectStateFactory *factory;
|
||||
EffectStateFactory *factory;
|
||||
|
||||
factory = getFactoryByType(newtype);
|
||||
if(!factory)
|
||||
@@ -513,7 +501,7 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e
|
||||
ERR("Failed to find factory for effect type 0x%04x\n", newtype);
|
||||
return AL_INVALID_ENUM;
|
||||
}
|
||||
State = V0(factory,create)();
|
||||
State = EffectStateFactory_create(factory);
|
||||
if(!State) return AL_OUT_OF_MEMORY;
|
||||
|
||||
START_MIXER_MODE();
|
||||
@@ -548,7 +536,7 @@ ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *e
|
||||
EffectSlot->Effect.Props = effect->Props;
|
||||
|
||||
/* Remove state references from old effect slot property updates. */
|
||||
props = ATOMIC_LOAD_SEQ(&EffectSlot->FreeList);
|
||||
props = ATOMIC_LOAD_SEQ(&Context->FreeEffectslotProps);
|
||||
while(props)
|
||||
{
|
||||
if(props->State)
|
||||
@@ -568,7 +556,7 @@ static void ALeffectState_IncRef(ALeffectState *state)
|
||||
TRACEREF("%p increasing refcount to %u\n", state, ref);
|
||||
}
|
||||
|
||||
static void ALeffectState_DecRef(ALeffectState *state)
|
||||
void ALeffectState_DecRef(ALeffectState *state)
|
||||
{
|
||||
uint ref;
|
||||
ref = DecrementRef(&state->Ref);
|
||||
@@ -590,15 +578,103 @@ void ALeffectState_Destruct(ALeffectState *UNUSED(state))
|
||||
}
|
||||
|
||||
|
||||
static void AddActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context)
|
||||
{
|
||||
struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots,
|
||||
almemory_order_acquire);
|
||||
struct ALeffectslotArray *newarray = NULL;
|
||||
ALsizei newcount = curarray->count + count;
|
||||
ALCdevice *device = context->Device;
|
||||
ALsizei i, j;
|
||||
|
||||
/* Insert the new effect slots into the head of the array, followed by the
|
||||
* existing ones.
|
||||
*/
|
||||
newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, newcount));
|
||||
newarray->count = newcount;
|
||||
for(i = 0;i < count;i++)
|
||||
newarray->slot[i] = LookupEffectSlot(context, slotids[i]);
|
||||
for(j = 0;i < newcount;)
|
||||
newarray->slot[i++] = curarray->slot[j++];
|
||||
/* Remove any duplicates (first instance of each will be kept). */
|
||||
for(i = 1;i < newcount;i++)
|
||||
{
|
||||
for(j = i;j != 0;)
|
||||
{
|
||||
if(UNLIKELY(newarray->slot[i] == newarray->slot[--j]))
|
||||
{
|
||||
newcount--;
|
||||
for(j = i;j < newcount;j++)
|
||||
newarray->slot[j] = newarray->slot[j+1];
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reallocate newarray if the new size ended up smaller from duplicate
|
||||
* removal.
|
||||
*/
|
||||
if(UNLIKELY(newcount < newarray->count))
|
||||
{
|
||||
struct ALeffectslotArray *tmpnewarray = al_calloc(DEF_ALIGN,
|
||||
FAM_SIZE(struct ALeffectslotArray, slot, newcount));
|
||||
memcpy(tmpnewarray, newarray, FAM_SIZE(struct ALeffectslotArray, slot, newcount));
|
||||
al_free(newarray);
|
||||
newarray = tmpnewarray;
|
||||
newarray->count = newcount;
|
||||
}
|
||||
|
||||
curarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray, almemory_order_acq_rel);
|
||||
while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
|
||||
althrd_yield();
|
||||
al_free(curarray);
|
||||
}
|
||||
|
||||
static void RemoveActiveEffectSlots(const ALuint *slotids, ALsizei count, ALCcontext *context)
|
||||
{
|
||||
struct ALeffectslotArray *curarray = ATOMIC_LOAD(&context->ActiveAuxSlots,
|
||||
almemory_order_acquire);
|
||||
struct ALeffectslotArray *newarray = NULL;
|
||||
ALCdevice *device = context->Device;
|
||||
ALsizei i, j;
|
||||
|
||||
/* Don't shrink the allocated array size since we don't know how many (if
|
||||
* any) of the effect slots to remove are in the array.
|
||||
*/
|
||||
newarray = al_calloc(DEF_ALIGN, FAM_SIZE(struct ALeffectslotArray, slot, curarray->count));
|
||||
newarray->count = 0;
|
||||
for(i = 0;i < curarray->count;i++)
|
||||
{
|
||||
/* Insert this slot into the new array only if it's not one to remove. */
|
||||
ALeffectslot *slot = curarray->slot[i];
|
||||
for(j = count;j != 0;)
|
||||
{
|
||||
if(slot->id == slotids[--j])
|
||||
goto skip_ins;
|
||||
}
|
||||
newarray->slot[newarray->count++] = slot;
|
||||
skip_ins: ;
|
||||
}
|
||||
|
||||
/* TODO: Could reallocate newarray now that we know it's needed size. */
|
||||
|
||||
curarray = ATOMIC_EXCHANGE_PTR(&context->ActiveAuxSlots, newarray, almemory_order_acq_rel);
|
||||
while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
|
||||
althrd_yield();
|
||||
al_free(curarray);
|
||||
}
|
||||
|
||||
|
||||
ALenum InitEffectSlot(ALeffectslot *slot)
|
||||
{
|
||||
ALeffectStateFactory *factory;
|
||||
EffectStateFactory *factory;
|
||||
|
||||
slot->Effect.Type = AL_EFFECT_NULL;
|
||||
|
||||
factory = getFactoryByType(AL_EFFECT_NULL);
|
||||
if(!(slot->Effect.State=V0(factory,create)()))
|
||||
return AL_OUT_OF_MEMORY;
|
||||
slot->Effect.State = EffectStateFactory_create(factory);
|
||||
if(!slot->Effect.State) return AL_OUT_OF_MEMORY;
|
||||
|
||||
slot->Gain = 1.0;
|
||||
slot->AuxSendAuto = AL_TRUE;
|
||||
@@ -606,7 +682,6 @@ ALenum InitEffectSlot(ALeffectslot *slot)
|
||||
InitRef(&slot->ref, 0);
|
||||
|
||||
ATOMIC_INIT(&slot->Update, NULL);
|
||||
ATOMIC_INIT(&slot->FreeList, NULL);
|
||||
|
||||
slot->Params.Gain = 1.0f;
|
||||
slot->Params.AuxSendAuto = AL_TRUE;
|
||||
@@ -614,6 +689,7 @@ ALenum InitEffectSlot(ALeffectslot *slot)
|
||||
slot->Params.EffectState = slot->Effect.State;
|
||||
slot->Params.RoomRolloff = 0.0f;
|
||||
slot->Params.DecayTime = 0.0f;
|
||||
slot->Params.DecayLFRatio = 0.0f;
|
||||
slot->Params.DecayHFRatio = 0.0f;
|
||||
slot->Params.DecayHFLimit = AL_FALSE;
|
||||
slot->Params.AirAbsorptionGainHF = 1.0f;
|
||||
@@ -624,7 +700,6 @@ ALenum InitEffectSlot(ALeffectslot *slot)
|
||||
void DeinitEffectSlot(ALeffectslot *slot)
|
||||
{
|
||||
struct ALeffectslotProps *props;
|
||||
size_t count = 0;
|
||||
|
||||
props = ATOMIC_LOAD_SEQ(&slot->Update);
|
||||
if(props)
|
||||
@@ -633,29 +708,19 @@ void DeinitEffectSlot(ALeffectslot *slot)
|
||||
TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props);
|
||||
al_free(props);
|
||||
}
|
||||
props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
|
||||
while(props)
|
||||
{
|
||||
struct ALeffectslotProps *next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
|
||||
if(props->State) ALeffectState_DecRef(props->State);
|
||||
al_free(props);
|
||||
props = next;
|
||||
++count;
|
||||
}
|
||||
TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
|
||||
|
||||
ALeffectState_DecRef(slot->Effect.State);
|
||||
if(slot->Params.EffectState)
|
||||
ALeffectState_DecRef(slot->Params.EffectState);
|
||||
}
|
||||
|
||||
void UpdateEffectSlotProps(ALeffectslot *slot)
|
||||
void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context)
|
||||
{
|
||||
struct ALeffectslotProps *props;
|
||||
ALeffectState *oldstate;
|
||||
|
||||
/* Get an unused property container, or allocate a new one as needed. */
|
||||
props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
|
||||
props = ATOMIC_LOAD(&context->FreeEffectslotProps, almemory_order_relaxed);
|
||||
if(!props)
|
||||
props = al_calloc(16, sizeof(*props));
|
||||
else
|
||||
@@ -663,7 +728,7 @@ void UpdateEffectSlotProps(ALeffectslot *slot)
|
||||
struct ALeffectslotProps *next;
|
||||
do {
|
||||
next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
|
||||
} while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&slot->FreeList, &props, next,
|
||||
} while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context->FreeEffectslotProps, &props, next,
|
||||
almemory_order_seq_cst, almemory_order_acquire) == 0);
|
||||
}
|
||||
|
||||
@@ -687,7 +752,7 @@ void UpdateEffectSlotProps(ALeffectslot *slot)
|
||||
/* If there was an unused update container, put it back in the
|
||||
* freelist.
|
||||
*/
|
||||
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &slot->FreeList, props);
|
||||
ATOMIC_REPLACE_HEAD(struct ALeffectslotProps*, &context->FreeEffectslotProps, props);
|
||||
}
|
||||
|
||||
if(oldstate)
|
||||
@@ -699,29 +764,35 @@ void UpdateAllEffectSlotProps(ALCcontext *context)
|
||||
struct ALeffectslotArray *auxslots;
|
||||
ALsizei i;
|
||||
|
||||
LockEffectSlotsRead(context);
|
||||
LockEffectSlotList(context);
|
||||
auxslots = ATOMIC_LOAD(&context->ActiveAuxSlots, almemory_order_acquire);
|
||||
for(i = 0;i < auxslots->count;i++)
|
||||
{
|
||||
ALeffectslot *slot = auxslots->slot[i];
|
||||
if(!ATOMIC_FLAG_TEST_AND_SET(&slot->PropsClean, almemory_order_acq_rel))
|
||||
UpdateEffectSlotProps(slot);
|
||||
UpdateEffectSlotProps(slot, context);
|
||||
}
|
||||
UnlockEffectSlotsRead(context);
|
||||
UnlockEffectSlotList(context);
|
||||
}
|
||||
|
||||
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
|
||||
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *context)
|
||||
{
|
||||
ALsizei pos;
|
||||
for(pos = 0;pos < Context->EffectSlotMap.size;pos++)
|
||||
ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList);
|
||||
ALeffectslotPtr *end = VECTOR_END(context->EffectSlotList);
|
||||
size_t leftover = 0;
|
||||
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
ALeffectslot *temp = Context->EffectSlotMap.values[pos];
|
||||
Context->EffectSlotMap.values[pos] = NULL;
|
||||
ALeffectslot *slot = *iter;
|
||||
if(!slot) continue;
|
||||
*iter = NULL;
|
||||
|
||||
DeinitEffectSlot(temp);
|
||||
DeinitEffectSlot(slot);
|
||||
|
||||
FreeThunkEntry(temp->id);
|
||||
memset(temp, 0, sizeof(ALeffectslot));
|
||||
al_free(temp);
|
||||
memset(slot, 0, sizeof(*slot));
|
||||
al_free(slot);
|
||||
++leftover;
|
||||
}
|
||||
if(leftover > 0)
|
||||
WARN("(%p) Deleted "SZFMT" AuxiliaryEffectSlot%s\n", context, leftover, (leftover==1)?"":"s");
|
||||
}
|
||||
|
||||
+604
-708
File diff suppressed because it is too large
Load Diff
+215
-112
@@ -28,26 +28,53 @@
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alEffect.h"
|
||||
#include "alThunk.h"
|
||||
#include "alError.h"
|
||||
|
||||
|
||||
ALboolean DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
extern inline void LockEffectsRead(ALCdevice *device);
|
||||
extern inline void UnlockEffectsRead(ALCdevice *device);
|
||||
extern inline void LockEffectsWrite(ALCdevice *device);
|
||||
extern inline void UnlockEffectsWrite(ALCdevice *device);
|
||||
extern inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id);
|
||||
extern inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id);
|
||||
extern inline void LockEffectList(ALCdevice *device);
|
||||
extern inline void UnlockEffectList(ALCdevice *device);
|
||||
extern inline ALboolean IsReverbEffect(ALenum type);
|
||||
|
||||
const struct EffectList EffectList[EFFECTLIST_SIZE] = {
|
||||
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
||||
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
|
||||
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
|
||||
{ "chorus", CHORUS_EFFECT, AL_EFFECT_CHORUS },
|
||||
{ "compressor", COMPRESSOR_EFFECT, AL_EFFECT_COMPRESSOR },
|
||||
{ "distortion", DISTORTION_EFFECT, AL_EFFECT_DISTORTION },
|
||||
{ "echo", ECHO_EFFECT, AL_EFFECT_ECHO },
|
||||
{ "equalizer", EQUALIZER_EFFECT, AL_EFFECT_EQUALIZER },
|
||||
{ "flanger", FLANGER_EFFECT, AL_EFFECT_FLANGER },
|
||||
{ "fshifter", FSHIFTER_EFFECT, AL_EFFECT_FREQUENCY_SHIFTER },
|
||||
{ "modulator", MODULATOR_EFFECT, AL_EFFECT_RING_MODULATOR },
|
||||
{ "pshifter", PSHIFTER_EFFECT, AL_EFFECT_PITCH_SHIFTER },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE },
|
||||
};
|
||||
|
||||
ALboolean DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
static ALeffect *AllocEffect(ALCcontext *context);
|
||||
static void FreeEffect(ALCdevice *device, ALeffect *effect);
|
||||
static void InitEffectParams(ALeffect *effect, ALenum type);
|
||||
|
||||
static inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
||||
{
|
||||
EffectSubList *sublist;
|
||||
ALuint lidx = (id-1) >> 6;
|
||||
ALsizei slidx = (id-1) & 0x3f;
|
||||
|
||||
if(UNLIKELY(lidx >= VECTOR_SIZE(device->EffectList)))
|
||||
return NULL;
|
||||
sublist = &VECTOR_ELEM(device->EffectList, lidx);
|
||||
if(UNLIKELY(sublist->FreeMask & (U64(1)<<slidx)))
|
||||
return NULL;
|
||||
return sublist->Effects + slidx;
|
||||
}
|
||||
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsizei cur;
|
||||
|
||||
@@ -55,37 +82,18 @@ AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(cur = 0;cur < n;cur++)
|
||||
alSetError(context, AL_INVALID_VALUE, "Generating %d effects", n);
|
||||
else for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALeffect *effect = al_calloc(16, sizeof(ALeffect));
|
||||
ALenum err = AL_OUT_OF_MEMORY;
|
||||
if(!effect || (err=InitEffect(effect)) != AL_NO_ERROR)
|
||||
ALeffect *effect = AllocEffect(context);
|
||||
if(!effect)
|
||||
{
|
||||
al_free(effect);
|
||||
alDeleteEffects(cur, effects);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
break;
|
||||
}
|
||||
|
||||
err = NewThunkEntry(&effect->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntry(&device->EffectMap, effect->id, effect);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
FreeThunkEntry(effect->id);
|
||||
memset(effect, 0, sizeof(ALeffect));
|
||||
al_free(effect);
|
||||
|
||||
alDeleteEffects(cur, effects);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
}
|
||||
|
||||
effects[cur] = effect->id;
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -100,26 +108,22 @@ AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
LockEffectsWrite(device);
|
||||
LockEffectList(device);
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d effects", n);
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if(effects[i] && LookupEffect(device, effects[i]) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid effect ID %u", effects[i]);
|
||||
}
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if((effect=RemoveEffect(device, effects[i])) == NULL)
|
||||
continue;
|
||||
FreeThunkEntry(effect->id);
|
||||
|
||||
memset(effect, 0, sizeof(*effect));
|
||||
al_free(effect);
|
||||
if((effect=LookupEffect(device, effects[i])) != NULL)
|
||||
FreeEffect(device, effect);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockEffectsWrite(device);
|
||||
UnlockEffectList(device);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -131,10 +135,10 @@ AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
|
||||
Context = GetContextRef();
|
||||
if(!Context) return AL_FALSE;
|
||||
|
||||
LockEffectsRead(Context->Device);
|
||||
LockEffectList(Context->Device);
|
||||
result = ((!effect || LookupEffect(Context->Device, effect)) ?
|
||||
AL_TRUE : AL_FALSE);
|
||||
UnlockEffectsRead(Context->Device);
|
||||
UnlockEffectList(Context->Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
|
||||
@@ -151,16 +155,16 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockEffectsWrite(Device);
|
||||
LockEffectList(Device);
|
||||
if((ALEffect=LookupEffect(Device, effect)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else
|
||||
{
|
||||
if(param == AL_EFFECT_TYPE)
|
||||
{
|
||||
ALboolean isOk = (value == AL_EFFECT_NULL);
|
||||
ALint i;
|
||||
for(i = 0;!isOk && EffectList[i].val;i++)
|
||||
for(i = 0;!isOk && i < EFFECTLIST_SIZE;i++)
|
||||
{
|
||||
if(value == EffectList[i].val &&
|
||||
!DisabledEffects[EffectList[i].type])
|
||||
@@ -170,15 +174,15 @@ AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
|
||||
if(isOk)
|
||||
InitEffectParams(ALEffect, value);
|
||||
else
|
||||
alSetError(Context, AL_INVALID_VALUE);
|
||||
alSetError(Context, AL_INVALID_VALUE, "Effect type 0x%04x not supported", value);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
V(ALEffect,setParami)(Context, param, value);
|
||||
ALeffect_setParami(ALEffect, Context, param, value);
|
||||
}
|
||||
}
|
||||
UnlockEffectsWrite(Device);
|
||||
UnlockEffectList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -200,15 +204,15 @@ AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *v
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockEffectsWrite(Device);
|
||||
LockEffectList(Device);
|
||||
if((ALEffect=LookupEffect(Device, effect)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
V(ALEffect,setParamiv)(Context, param, values);
|
||||
ALeffect_setParamiv(ALEffect, Context, param, values);
|
||||
}
|
||||
UnlockEffectsWrite(Device);
|
||||
UnlockEffectList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -223,15 +227,15 @@ AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockEffectsWrite(Device);
|
||||
LockEffectList(Device);
|
||||
if((ALEffect=LookupEffect(Device, effect)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
V(ALEffect,setParamf)(Context, param, value);
|
||||
ALeffect_setParamf(ALEffect, Context, param, value);
|
||||
}
|
||||
UnlockEffectsWrite(Device);
|
||||
UnlockEffectList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -246,15 +250,15 @@ AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockEffectsWrite(Device);
|
||||
LockEffectList(Device);
|
||||
if((ALEffect=LookupEffect(Device, effect)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
V(ALEffect,setParamfv)(Context, param, values);
|
||||
ALeffect_setParamfv(ALEffect, Context, param, values);
|
||||
}
|
||||
UnlockEffectsWrite(Device);
|
||||
UnlockEffectList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -269,9 +273,9 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockEffectsRead(Device);
|
||||
LockEffectList(Device);
|
||||
if((ALEffect=LookupEffect(Device, effect)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else
|
||||
{
|
||||
if(param == AL_EFFECT_TYPE)
|
||||
@@ -279,10 +283,10 @@ AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
V(ALEffect,getParami)(Context, param, value);
|
||||
ALeffect_getParami(ALEffect, Context, param, value);
|
||||
}
|
||||
}
|
||||
UnlockEffectsRead(Device);
|
||||
UnlockEffectList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -304,15 +308,15 @@ AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *valu
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockEffectsRead(Device);
|
||||
LockEffectList(Device);
|
||||
if((ALEffect=LookupEffect(Device, effect)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
V(ALEffect,getParamiv)(Context, param, values);
|
||||
ALeffect_getParamiv(ALEffect, Context, param, values);
|
||||
}
|
||||
UnlockEffectsRead(Device);
|
||||
UnlockEffectList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -327,15 +331,15 @@ AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *val
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockEffectsRead(Device);
|
||||
LockEffectList(Device);
|
||||
if((ALEffect=LookupEffect(Device, effect)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
V(ALEffect,getParamf)(Context, param, value);
|
||||
ALeffect_getParamf(ALEffect, Context, param, value);
|
||||
}
|
||||
UnlockEffectsRead(Device);
|
||||
UnlockEffectList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -350,39 +354,120 @@ AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *va
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockEffectsRead(Device);
|
||||
LockEffectList(Device);
|
||||
if((ALEffect=LookupEffect(Device, effect)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
V(ALEffect,getParamfv)(Context, param, values);
|
||||
ALeffect_getParamfv(ALEffect, Context, param, values);
|
||||
}
|
||||
UnlockEffectsRead(Device);
|
||||
UnlockEffectList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
|
||||
ALenum InitEffect(ALeffect *effect)
|
||||
void InitEffect(ALeffect *effect)
|
||||
{
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
ALvoid ReleaseALEffects(ALCdevice *device)
|
||||
static ALeffect *AllocEffect(ALCcontext *context)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < device->EffectMap.size;i++)
|
||||
{
|
||||
ALeffect *temp = device->EffectMap.values[i];
|
||||
device->EffectMap.values[i] = NULL;
|
||||
ALCdevice *device = context->Device;
|
||||
EffectSubList *sublist, *subend;
|
||||
ALeffect *effect = NULL;
|
||||
ALsizei lidx = 0;
|
||||
ALsizei slidx;
|
||||
|
||||
// Release effect structure
|
||||
FreeThunkEntry(temp->id);
|
||||
memset(temp, 0, sizeof(ALeffect));
|
||||
al_free(temp);
|
||||
almtx_lock(&device->EffectLock);
|
||||
sublist = VECTOR_BEGIN(device->EffectList);
|
||||
subend = VECTOR_END(device->EffectList);
|
||||
for(;sublist != subend;++sublist)
|
||||
{
|
||||
if(sublist->FreeMask)
|
||||
{
|
||||
slidx = CTZ64(sublist->FreeMask);
|
||||
effect = sublist->Effects + slidx;
|
||||
break;
|
||||
}
|
||||
++lidx;
|
||||
}
|
||||
if(UNLIKELY(!effect))
|
||||
{
|
||||
const EffectSubList empty_sublist = { 0, NULL };
|
||||
/* Don't allocate so many list entries that the 32-bit ID could
|
||||
* overflow...
|
||||
*/
|
||||
if(UNLIKELY(VECTOR_SIZE(device->EffectList) >= 1<<25))
|
||||
{
|
||||
almtx_unlock(&device->EffectLock);
|
||||
alSetError(context, AL_OUT_OF_MEMORY, "Too many effects allocated");
|
||||
return NULL;
|
||||
}
|
||||
lidx = (ALsizei)VECTOR_SIZE(device->EffectList);
|
||||
VECTOR_PUSH_BACK(device->EffectList, empty_sublist);
|
||||
sublist = &VECTOR_BACK(device->EffectList);
|
||||
sublist->FreeMask = ~U64(0);
|
||||
sublist->Effects = al_calloc(16, sizeof(ALeffect)*64);
|
||||
if(UNLIKELY(!sublist->Effects))
|
||||
{
|
||||
VECTOR_POP_BACK(device->EffectList);
|
||||
almtx_unlock(&device->EffectLock);
|
||||
alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate effect batch");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slidx = 0;
|
||||
effect = sublist->Effects + slidx;
|
||||
}
|
||||
|
||||
memset(effect, 0, sizeof(*effect));
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
|
||||
/* Add 1 to avoid effect ID 0. */
|
||||
effect->id = ((lidx<<6) | slidx) + 1;
|
||||
|
||||
sublist->FreeMask &= ~(U64(1)<<slidx);
|
||||
almtx_unlock(&device->EffectLock);
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
static void FreeEffect(ALCdevice *device, ALeffect *effect)
|
||||
{
|
||||
ALuint id = effect->id - 1;
|
||||
ALsizei lidx = id >> 6;
|
||||
ALsizei slidx = id & 0x3f;
|
||||
|
||||
memset(effect, 0, sizeof(*effect));
|
||||
|
||||
VECTOR_ELEM(device->EffectList, lidx).FreeMask |= U64(1) << slidx;
|
||||
}
|
||||
|
||||
void ReleaseALEffects(ALCdevice *device)
|
||||
{
|
||||
EffectSubList *sublist = VECTOR_BEGIN(device->EffectList);
|
||||
EffectSubList *subend = VECTOR_END(device->EffectList);
|
||||
size_t leftover = 0;
|
||||
for(;sublist != subend;++sublist)
|
||||
{
|
||||
ALuint64 usemask = ~sublist->FreeMask;
|
||||
while(usemask)
|
||||
{
|
||||
ALsizei idx = CTZ64(usemask);
|
||||
ALeffect *effect = sublist->Effects + idx;
|
||||
|
||||
memset(effect, 0, sizeof(*effect));
|
||||
++leftover;
|
||||
|
||||
usemask &= ~(U64(1) << idx);
|
||||
}
|
||||
sublist->FreeMask = ~usemask;
|
||||
}
|
||||
if(leftover > 0)
|
||||
WARN("(%p) Deleted "SZFMT" Effect%s\n", device, leftover, (leftover==1)?"":"s");
|
||||
}
|
||||
|
||||
|
||||
@@ -418,7 +503,7 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
effect->Props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
|
||||
effect->Props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
|
||||
effect->Props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT;
|
||||
SET_VTABLE1(ALeaxreverb, effect);
|
||||
effect->vtab = &ALeaxreverb_vtable;
|
||||
break;
|
||||
case AL_EFFECT_REVERB:
|
||||
effect->Props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY;
|
||||
@@ -448,7 +533,14 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
effect->Props.Reverb.LFReference = 250.0f;
|
||||
effect->Props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
|
||||
effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
|
||||
SET_VTABLE1(ALreverb, effect);
|
||||
effect->vtab = &ALreverb_vtable;
|
||||
break;
|
||||
case AL_EFFECT_AUTOWAH:
|
||||
effect->Props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
||||
effect->Props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
||||
effect->Props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
||||
effect->Props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
||||
effect->vtab = &ALautowah_vtable;
|
||||
break;
|
||||
case AL_EFFECT_CHORUS:
|
||||
effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM;
|
||||
@@ -457,11 +549,11 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
effect->Props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
|
||||
effect->Props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
|
||||
effect->Props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
|
||||
SET_VTABLE1(ALchorus, effect);
|
||||
effect->vtab = &ALchorus_vtable;
|
||||
break;
|
||||
case AL_EFFECT_COMPRESSOR:
|
||||
effect->Props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
||||
SET_VTABLE1(ALcompressor, effect);
|
||||
effect->vtab = &ALcompressor_vtable;
|
||||
break;
|
||||
case AL_EFFECT_DISTORTION:
|
||||
effect->Props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
|
||||
@@ -469,7 +561,7 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
effect->Props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
|
||||
effect->Props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
|
||||
effect->Props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
|
||||
SET_VTABLE1(ALdistortion, effect);
|
||||
effect->vtab = &ALdistortion_vtable;
|
||||
break;
|
||||
case AL_EFFECT_ECHO:
|
||||
effect->Props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
|
||||
@@ -477,7 +569,7 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
effect->Props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
|
||||
effect->Props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
|
||||
effect->Props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
|
||||
SET_VTABLE1(ALecho, effect);
|
||||
effect->vtab = &ALecho_vtable;
|
||||
break;
|
||||
case AL_EFFECT_EQUALIZER:
|
||||
effect->Props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
|
||||
@@ -490,30 +582,41 @@ static void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
effect->Props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
|
||||
effect->Props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
|
||||
effect->Props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
|
||||
SET_VTABLE1(ALequalizer, effect);
|
||||
effect->vtab = &ALequalizer_vtable;
|
||||
break;
|
||||
case AL_EFFECT_FLANGER:
|
||||
effect->Props.Flanger.Waveform = AL_FLANGER_DEFAULT_WAVEFORM;
|
||||
effect->Props.Flanger.Phase = AL_FLANGER_DEFAULT_PHASE;
|
||||
effect->Props.Flanger.Rate = AL_FLANGER_DEFAULT_RATE;
|
||||
effect->Props.Flanger.Depth = AL_FLANGER_DEFAULT_DEPTH;
|
||||
effect->Props.Flanger.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
|
||||
effect->Props.Flanger.Delay = AL_FLANGER_DEFAULT_DELAY;
|
||||
SET_VTABLE1(ALflanger, effect);
|
||||
effect->Props.Chorus.Waveform = AL_FLANGER_DEFAULT_WAVEFORM;
|
||||
effect->Props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
|
||||
effect->Props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
|
||||
effect->Props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
|
||||
effect->Props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
|
||||
effect->Props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
|
||||
effect->vtab = &ALflanger_vtable;
|
||||
break;
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER:
|
||||
effect->Props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||
effect->Props.Fshifter.LeftDirection = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
|
||||
effect->Props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
|
||||
effect->vtab = &ALfshifter_vtable;
|
||||
break;
|
||||
case AL_EFFECT_RING_MODULATOR:
|
||||
effect->Props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||
effect->Props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||
effect->Props.Modulator.Waveform = AL_RING_MODULATOR_DEFAULT_WAVEFORM;
|
||||
SET_VTABLE1(ALmodulator, effect);
|
||||
effect->vtab = &ALmodulator_vtable;
|
||||
break;
|
||||
case AL_EFFECT_PITCH_SHIFTER:
|
||||
effect->Props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
||||
effect->Props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
||||
effect->vtab = &ALpshifter_vtable;
|
||||
break;
|
||||
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
|
||||
case AL_EFFECT_DEDICATED_DIALOGUE:
|
||||
effect->Props.Dedicated.Gain = 1.0f;
|
||||
SET_VTABLE1(ALdedicated, effect);
|
||||
effect->vtab = &ALdedicated_vtable;
|
||||
break;
|
||||
default:
|
||||
SET_VTABLE1(ALnull, effect);
|
||||
effect->vtab = &ALnull_vtable;
|
||||
break;
|
||||
}
|
||||
effect->type = type;
|
||||
@@ -656,7 +759,7 @@ static const struct {
|
||||
};
|
||||
#undef DECL
|
||||
|
||||
ALvoid LoadReverbPreset(const char *name, ALeffect *effect)
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
@@ -667,9 +770,9 @@ ALvoid LoadReverbPreset(const char *name, ALeffect *effect)
|
||||
return;
|
||||
}
|
||||
|
||||
if(!DisabledEffects[AL__EAXREVERB])
|
||||
if(!DisabledEffects[EAXREVERB_EFFECT])
|
||||
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
|
||||
else if(!DisabledEffects[AL__REVERB])
|
||||
else if(!DisabledEffects[REVERB_EFFECT])
|
||||
InitEffectParams(effect, AL_EFFECT_REVERB);
|
||||
else
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
|
||||
+43
-12
@@ -21,6 +21,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
@@ -33,11 +34,32 @@
|
||||
|
||||
ALboolean TrapALError = AL_FALSE;
|
||||
|
||||
ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
|
||||
void alSetError(ALCcontext *context, ALenum errorCode, const char *msg, ...)
|
||||
{
|
||||
ALenum curerr = AL_NO_ERROR;
|
||||
char message[1024] = { 0 };
|
||||
va_list args;
|
||||
int msglen;
|
||||
|
||||
WARN("Error generated on context %p, code 0x%04x\n", Context, errorCode);
|
||||
va_start(args, msg);
|
||||
msglen = vsnprintf(message, sizeof(message), msg, args);
|
||||
va_end(args);
|
||||
|
||||
if(msglen < 0 || (size_t)msglen >= sizeof(message))
|
||||
{
|
||||
message[sizeof(message)-1] = 0;
|
||||
msglen = (int)strlen(message);
|
||||
}
|
||||
if(msglen > 0)
|
||||
msg = message;
|
||||
else
|
||||
{
|
||||
msg = "<internal error constructing message>";
|
||||
msglen = (int)strlen(msg);
|
||||
}
|
||||
|
||||
WARN("Error generated on context %p, code 0x%04x, \"%s\"\n",
|
||||
context, errorCode, message);
|
||||
if(TrapALError)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -49,19 +71,29 @@ ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
|
||||
#endif
|
||||
}
|
||||
|
||||
(void)(ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&Context->LastError, &curerr, errorCode));
|
||||
ATOMIC_COMPARE_EXCHANGE_STRONG_SEQ(&context->LastError, &curerr, errorCode);
|
||||
if((ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed)&EventType_Error))
|
||||
{
|
||||
ALbitfieldSOFT enabledevts;
|
||||
almtx_lock(&context->EventCbLock);
|
||||
enabledevts = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_relaxed);
|
||||
if((enabledevts&EventType_Error) && context->EventCb)
|
||||
(*context->EventCb)(AL_EVENT_TYPE_ERROR_SOFT, 0, errorCode, msglen, msg,
|
||||
context->EventParam);
|
||||
almtx_unlock(&context->EventCbLock);
|
||||
}
|
||||
}
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetError(void)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCcontext *context;
|
||||
ALenum errorCode;
|
||||
|
||||
Context = GetContextRef();
|
||||
if(!Context)
|
||||
context = GetContextRef();
|
||||
if(!context)
|
||||
{
|
||||
WARN("Querying error state on null context (implicitly 0x%04x)\n",
|
||||
AL_INVALID_OPERATION);
|
||||
const ALenum deferror = AL_INVALID_OPERATION;
|
||||
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
|
||||
if(TrapALError)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -71,12 +103,11 @@ AL_API ALenum AL_APIENTRY alGetError(void)
|
||||
raise(SIGTRAP);
|
||||
#endif
|
||||
}
|
||||
return AL_INVALID_OPERATION;
|
||||
return deferror;
|
||||
}
|
||||
|
||||
errorCode = ATOMIC_EXCHANGE_SEQ(&Context->LastError, AL_NO_ERROR);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
errorCode = ATOMIC_EXCHANGE_SEQ(&context->LastError, AL_NO_ERROR);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
+2
-18
@@ -35,22 +35,6 @@
|
||||
#include "AL/alc.h"
|
||||
|
||||
|
||||
const struct EffectList EffectList[] = {
|
||||
{ "eaxreverb", AL__EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB },
|
||||
{ "reverb", AL__REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB },
|
||||
{ "chorus", AL__CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS },
|
||||
{ "compressor", AL__COMPRESSOR, "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR },
|
||||
{ "distortion", AL__DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION },
|
||||
{ "echo", AL__ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO },
|
||||
{ "equalizer", AL__EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER },
|
||||
{ "flanger", AL__FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER },
|
||||
{ "modulator", AL__MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
|
||||
{ "dedicated", AL__DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
||||
{ "dedicated", AL__DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE },
|
||||
{ NULL, 0, NULL, (ALenum)0 }
|
||||
};
|
||||
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
|
||||
{
|
||||
ALboolean ret = AL_FALSE;
|
||||
@@ -61,8 +45,8 @@ AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
|
||||
context = GetContextRef();
|
||||
if(!context) return AL_FALSE;
|
||||
|
||||
if(!(extName))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
if(!extName)
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "NULL pointer");
|
||||
|
||||
len = strlen(extName);
|
||||
ptr = context->ExtensionList;
|
||||
|
||||
+248
-296
@@ -25,66 +25,56 @@
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alFilter.h"
|
||||
#include "alThunk.h"
|
||||
#include "alError.h"
|
||||
|
||||
|
||||
extern inline void LockFiltersRead(ALCdevice *device);
|
||||
extern inline void UnlockFiltersRead(ALCdevice *device);
|
||||
extern inline void LockFiltersWrite(ALCdevice *device);
|
||||
extern inline void UnlockFiltersWrite(ALCdevice *device);
|
||||
extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id);
|
||||
extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id);
|
||||
extern inline void ALfilterState_clear(ALfilterState *filter);
|
||||
extern inline void ALfilterState_copyParams(ALfilterState *restrict dst, const ALfilterState *restrict src);
|
||||
extern inline void ALfilterState_processPassthru(ALfilterState *filter, const ALfloat *restrict src, ALsizei numsamples);
|
||||
extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope);
|
||||
extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat freq_mult, ALfloat bandwidth);
|
||||
#define FILTER_MIN_GAIN 0.0f
|
||||
#define FILTER_MAX_GAIN 4.0f /* +12dB */
|
||||
|
||||
extern inline void LockFilterList(ALCdevice *device);
|
||||
extern inline void UnlockFilterList(ALCdevice *device);
|
||||
|
||||
static ALfilter *AllocFilter(ALCcontext *context);
|
||||
static void FreeFilter(ALCdevice *device, ALfilter *filter);
|
||||
static void InitFilterParams(ALfilter *filter, ALenum type);
|
||||
|
||||
static inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
|
||||
{
|
||||
FilterSubList *sublist;
|
||||
ALuint lidx = (id-1) >> 6;
|
||||
ALsizei slidx = (id-1) & 0x3f;
|
||||
|
||||
if(UNLIKELY(lidx >= VECTOR_SIZE(device->FilterList)))
|
||||
return NULL;
|
||||
sublist = &VECTOR_ELEM(device->FilterList, lidx);
|
||||
if(UNLIKELY(sublist->FreeMask & (U64(1)<<slidx)))
|
||||
return NULL;
|
||||
return sublist->Filters + slidx;
|
||||
}
|
||||
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsizei cur = 0;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(cur = 0;cur < n;cur++)
|
||||
alSetError(context, AL_INVALID_VALUE, "Generating %d filters", n);
|
||||
else for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALfilter *filter = al_calloc(16, sizeof(ALfilter));
|
||||
ALfilter *filter = AllocFilter(context);
|
||||
if(!filter)
|
||||
{
|
||||
alDeleteFilters(cur, filters);
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
}
|
||||
InitFilterParams(filter, AL_FILTER_NULL);
|
||||
|
||||
err = NewThunkEntry(&filter->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntry(&device->FilterMap, filter->id, filter);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
FreeThunkEntry(filter->id);
|
||||
memset(filter, 0, sizeof(ALfilter));
|
||||
al_free(filter);
|
||||
|
||||
alDeleteFilters(cur, filters);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
break;
|
||||
}
|
||||
|
||||
filters[cur] = filter->id;
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -99,26 +89,22 @@ AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
LockFiltersWrite(device);
|
||||
LockFilterList(device);
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d filters", n);
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if(filters[i] && LookupFilter(device, filters[i]) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid filter ID %u", filters[i]);
|
||||
}
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if((filter=RemoveFilter(device, filters[i])) == NULL)
|
||||
continue;
|
||||
FreeThunkEntry(filter->id);
|
||||
|
||||
memset(filter, 0, sizeof(*filter));
|
||||
al_free(filter);
|
||||
if((filter=LookupFilter(device, filters[i])) != NULL)
|
||||
FreeFilter(device, filter);
|
||||
}
|
||||
|
||||
done:
|
||||
UnlockFiltersWrite(device);
|
||||
UnlockFilterList(device);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -130,10 +116,10 @@ AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
|
||||
Context = GetContextRef();
|
||||
if(!Context) return AL_FALSE;
|
||||
|
||||
LockFiltersRead(Context->Device);
|
||||
LockFilterList(Context->Device);
|
||||
result = ((!filter || LookupFilter(Context->Device, filter)) ?
|
||||
AL_TRUE : AL_FALSE);
|
||||
UnlockFiltersRead(Context->Device);
|
||||
UnlockFilterList(Context->Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
|
||||
@@ -150,9 +136,9 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockFiltersWrite(Device);
|
||||
LockFilterList(Device);
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter);
|
||||
else
|
||||
{
|
||||
if(param == AL_FILTER_TYPE)
|
||||
@@ -161,15 +147,15 @@ AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
|
||||
value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
|
||||
InitFilterParams(ALFilter, value);
|
||||
else
|
||||
alSetError(Context, AL_INVALID_VALUE);
|
||||
alSetError(Context, AL_INVALID_VALUE, "Invalid filter type 0x%04x", value);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_SetParami(ALFilter, Context, param, value);
|
||||
ALfilter_setParami(ALFilter, Context, param, value);
|
||||
}
|
||||
}
|
||||
UnlockFiltersWrite(Device);
|
||||
UnlockFilterList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -191,15 +177,15 @@ AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *v
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockFiltersWrite(Device);
|
||||
LockFilterList(Device);
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_SetParamiv(ALFilter, Context, param, values);
|
||||
ALfilter_setParamiv(ALFilter, Context, param, values);
|
||||
}
|
||||
UnlockFiltersWrite(Device);
|
||||
UnlockFilterList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -214,15 +200,15 @@ AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockFiltersWrite(Device);
|
||||
LockFilterList(Device);
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_SetParamf(ALFilter, Context, param, value);
|
||||
ALfilter_setParamf(ALFilter, Context, param, value);
|
||||
}
|
||||
UnlockFiltersWrite(Device);
|
||||
UnlockFilterList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -237,15 +223,15 @@ AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockFiltersWrite(Device);
|
||||
LockFilterList(Device);
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_SetParamfv(ALFilter, Context, param, values);
|
||||
ALfilter_setParamfv(ALFilter, Context, param, values);
|
||||
}
|
||||
UnlockFiltersWrite(Device);
|
||||
UnlockFilterList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -260,9 +246,9 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockFiltersRead(Device);
|
||||
LockFilterList(Device);
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter);
|
||||
else
|
||||
{
|
||||
if(param == AL_FILTER_TYPE)
|
||||
@@ -270,10 +256,10 @@ AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_GetParami(ALFilter, Context, param, value);
|
||||
ALfilter_getParami(ALFilter, Context, param, value);
|
||||
}
|
||||
}
|
||||
UnlockFiltersRead(Device);
|
||||
UnlockFilterList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -295,15 +281,15 @@ AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *valu
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockFiltersRead(Device);
|
||||
LockFilterList(Device);
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_GetParamiv(ALFilter, Context, param, values);
|
||||
ALfilter_getParamiv(ALFilter, Context, param, values);
|
||||
}
|
||||
UnlockFiltersRead(Device);
|
||||
UnlockFilterList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -318,15 +304,15 @@ AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *val
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockFiltersRead(Device);
|
||||
LockFilterList(Device);
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_GetParamf(ALFilter, Context, param, value);
|
||||
ALfilter_getParamf(ALFilter, Context, param, value);
|
||||
}
|
||||
UnlockFiltersRead(Device);
|
||||
UnlockFilterList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
@@ -341,134 +327,52 @@ AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *va
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
LockFiltersRead(Device);
|
||||
LockFilterList(Device);
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
alSetError(Context, AL_INVALID_NAME, "Invalid filter ID %u", filter);
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_GetParamfv(ALFilter, Context, param, values);
|
||||
ALfilter_getParamfv(ALFilter, Context, param, values);
|
||||
}
|
||||
UnlockFiltersRead(Device);
|
||||
UnlockFilterList(Device);
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
|
||||
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, 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 * freq_mult;
|
||||
sin_w0 = sinf(w0);
|
||||
cos_w0 = cosf(w0);
|
||||
alpha = sin_w0/2.0f * rcpQ;
|
||||
|
||||
/* Calculate filter coefficients depending on filter type */
|
||||
switch(type)
|
||||
{
|
||||
case ALfilterType_HighShelf:
|
||||
sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
|
||||
b[0] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
|
||||
b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0 );
|
||||
b[2] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
|
||||
a[0] = (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
|
||||
a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cos_w0 );
|
||||
a[2] = (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
|
||||
break;
|
||||
case ALfilterType_LowShelf:
|
||||
sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
|
||||
b[0] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
|
||||
b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0 );
|
||||
b[2] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
|
||||
a[0] = (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
|
||||
a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cos_w0 );
|
||||
a[2] = (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
|
||||
break;
|
||||
case ALfilterType_Peaking:
|
||||
gain = sqrtf(gain);
|
||||
b[0] = 1.0f + alpha * gain;
|
||||
b[1] = -2.0f * cos_w0;
|
||||
b[2] = 1.0f - alpha * gain;
|
||||
a[0] = 1.0f + alpha / gain;
|
||||
a[1] = -2.0f * cos_w0;
|
||||
a[2] = 1.0f - alpha / gain;
|
||||
break;
|
||||
|
||||
case ALfilterType_LowPass:
|
||||
b[0] = (1.0f - cos_w0) / 2.0f;
|
||||
b[1] = 1.0f - cos_w0;
|
||||
b[2] = (1.0f - cos_w0) / 2.0f;
|
||||
a[0] = 1.0f + alpha;
|
||||
a[1] = -2.0f * cos_w0;
|
||||
a[2] = 1.0f - alpha;
|
||||
break;
|
||||
case ALfilterType_HighPass:
|
||||
b[0] = (1.0f + cos_w0) / 2.0f;
|
||||
b[1] = -(1.0f + cos_w0);
|
||||
b[2] = (1.0f + cos_w0) / 2.0f;
|
||||
a[0] = 1.0f + alpha;
|
||||
a[1] = -2.0f * cos_w0;
|
||||
a[2] = 1.0f - alpha;
|
||||
break;
|
||||
case ALfilterType_BandPass:
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
static void lp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void lp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void lp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
static void ALlowpass_setParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param); }
|
||||
static void ALlowpass_setParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x", param); }
|
||||
static void ALlowpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_LOWPASS_GAIN:
|
||||
if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Low-pass gain %f out of range", val);
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
||||
case AL_LOWPASS_GAINHF:
|
||||
if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Low-pass gainhf %f out of range", val);
|
||||
filter->GainHF = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
static void lp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
lp_SetParamf(filter, context, param, vals[0]);
|
||||
}
|
||||
static void ALlowpass_setParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALlowpass_setParamf(filter, context, param, vals[0]); }
|
||||
|
||||
static void lp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void lp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
static void ALlowpass_getParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param); }
|
||||
static void ALlowpass_getParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x", param); }
|
||||
static void ALlowpass_getParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
@@ -481,49 +385,47 @@ static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, AL
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
static void lp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
lp_GetParamf(filter, context, param, vals);
|
||||
}
|
||||
static void ALlowpass_getParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALlowpass_getParamf(filter, context, param, vals); }
|
||||
|
||||
DEFINE_ALFILTER_VTABLE(ALlowpass);
|
||||
|
||||
|
||||
static void hp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void hp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void hp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
static void ALhighpass_setParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param); }
|
||||
static void ALhighpass_setParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x", param); }
|
||||
static void ALhighpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_HIGHPASS_GAIN:
|
||||
if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "High-pass gain out of range");
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
||||
case AL_HIGHPASS_GAINLF:
|
||||
if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "High-pass gainlf out of range");
|
||||
filter->GainLF = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
static void hp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
hp_SetParamf(filter, context, param, vals[0]);
|
||||
}
|
||||
static void ALhighpass_setParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALhighpass_setParamf(filter, context, param, vals[0]); }
|
||||
|
||||
static void hp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void hp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
static void ALhighpass_getParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param); }
|
||||
static void ALhighpass_getParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x", param); }
|
||||
static void ALhighpass_getParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
@@ -536,55 +438,53 @@ static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, AL
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
static void hp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
hp_GetParamf(filter, context, param, vals);
|
||||
}
|
||||
static void ALhighpass_getParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALhighpass_getParamf(filter, context, param, vals); }
|
||||
|
||||
DEFINE_ALFILTER_VTABLE(ALhighpass);
|
||||
|
||||
|
||||
static void bp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void bp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void bp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
static void ALbandpass_setParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param); }
|
||||
static void ALbandpass_setParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x", param); }
|
||||
static void ALbandpass_setParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_BANDPASS_GAIN:
|
||||
if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gain out of range");
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
||||
case AL_BANDPASS_GAINHF:
|
||||
if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gainhf out of range");
|
||||
filter->GainHF = val;
|
||||
break;
|
||||
|
||||
case AL_BANDPASS_GAINLF:
|
||||
if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Band-pass gainlf out of range");
|
||||
filter->GainLF = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
static void bp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
bp_SetParamf(filter, context, param, vals[0]);
|
||||
}
|
||||
static void ALbandpass_setParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALbandpass_setParamf(filter, context, param, vals[0]); }
|
||||
|
||||
static void bp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void bp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
static void ALbandpass_getParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param); }
|
||||
static void ALbandpass_getParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x", param); }
|
||||
static void ALbandpass_getParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
@@ -601,47 +501,131 @@ static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, AL
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
static void bp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
static void ALbandpass_getParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALbandpass_getParamf(filter, context, param, vals); }
|
||||
|
||||
DEFINE_ALFILTER_VTABLE(ALbandpass);
|
||||
|
||||
|
||||
static void ALnullfilter_setParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); }
|
||||
static void ALnullfilter_setParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); }
|
||||
static void ALnullfilter_setParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); }
|
||||
static void ALnullfilter_setParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); }
|
||||
|
||||
static void ALnullfilter_getParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); }
|
||||
static void ALnullfilter_getParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); }
|
||||
static void ALnullfilter_getParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); }
|
||||
static void ALnullfilter_getParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param); }
|
||||
|
||||
DEFINE_ALFILTER_VTABLE(ALnullfilter);
|
||||
|
||||
|
||||
static ALfilter *AllocFilter(ALCcontext *context)
|
||||
{
|
||||
bp_GetParamf(filter, context, param, vals);
|
||||
}
|
||||
ALCdevice *device = context->Device;
|
||||
FilterSubList *sublist, *subend;
|
||||
ALfilter *filter = NULL;
|
||||
ALsizei lidx = 0;
|
||||
ALsizei slidx;
|
||||
|
||||
|
||||
static void null_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_SetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_SetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALfloat *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
|
||||
static void null_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_GetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_GetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
|
||||
|
||||
ALvoid ReleaseALFilters(ALCdevice *device)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < device->FilterMap.size;i++)
|
||||
almtx_lock(&device->FilterLock);
|
||||
sublist = VECTOR_BEGIN(device->FilterList);
|
||||
subend = VECTOR_END(device->FilterList);
|
||||
for(;sublist != subend;++sublist)
|
||||
{
|
||||
ALfilter *temp = device->FilterMap.values[i];
|
||||
device->FilterMap.values[i] = NULL;
|
||||
|
||||
// Release filter structure
|
||||
FreeThunkEntry(temp->id);
|
||||
memset(temp, 0, sizeof(ALfilter));
|
||||
al_free(temp);
|
||||
if(sublist->FreeMask)
|
||||
{
|
||||
slidx = CTZ64(sublist->FreeMask);
|
||||
filter = sublist->Filters + slidx;
|
||||
break;
|
||||
}
|
||||
++lidx;
|
||||
}
|
||||
if(UNLIKELY(!filter))
|
||||
{
|
||||
const FilterSubList empty_sublist = { 0, NULL };
|
||||
/* Don't allocate so many list entries that the 32-bit ID could
|
||||
* overflow...
|
||||
*/
|
||||
if(UNLIKELY(VECTOR_SIZE(device->FilterList) >= 1<<25))
|
||||
{
|
||||
almtx_unlock(&device->FilterLock);
|
||||
alSetError(context, AL_OUT_OF_MEMORY, "Too many filters allocated");
|
||||
return NULL;
|
||||
}
|
||||
lidx = (ALsizei)VECTOR_SIZE(device->FilterList);
|
||||
VECTOR_PUSH_BACK(device->FilterList, empty_sublist);
|
||||
sublist = &VECTOR_BACK(device->FilterList);
|
||||
sublist->FreeMask = ~U64(0);
|
||||
sublist->Filters = al_calloc(16, sizeof(ALfilter)*64);
|
||||
if(UNLIKELY(!sublist->Filters))
|
||||
{
|
||||
VECTOR_POP_BACK(device->FilterList);
|
||||
almtx_unlock(&device->FilterLock);
|
||||
alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate filter batch");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slidx = 0;
|
||||
filter = sublist->Filters + slidx;
|
||||
}
|
||||
|
||||
memset(filter, 0, sizeof(*filter));
|
||||
InitFilterParams(filter, AL_FILTER_NULL);
|
||||
|
||||
/* Add 1 to avoid filter ID 0. */
|
||||
filter->id = ((lidx<<6) | slidx) + 1;
|
||||
|
||||
sublist->FreeMask &= ~(U64(1)<<slidx);
|
||||
almtx_unlock(&device->FilterLock);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
static void FreeFilter(ALCdevice *device, ALfilter *filter)
|
||||
{
|
||||
ALuint id = filter->id - 1;
|
||||
ALsizei lidx = id >> 6;
|
||||
ALsizei slidx = id & 0x3f;
|
||||
|
||||
memset(filter, 0, sizeof(*filter));
|
||||
|
||||
VECTOR_ELEM(device->FilterList, lidx).FreeMask |= U64(1) << slidx;
|
||||
}
|
||||
|
||||
void ReleaseALFilters(ALCdevice *device)
|
||||
{
|
||||
FilterSubList *sublist = VECTOR_BEGIN(device->FilterList);
|
||||
FilterSubList *subend = VECTOR_END(device->FilterList);
|
||||
size_t leftover = 0;
|
||||
for(;sublist != subend;++sublist)
|
||||
{
|
||||
ALuint64 usemask = ~sublist->FreeMask;
|
||||
while(usemask)
|
||||
{
|
||||
ALsizei idx = CTZ64(usemask);
|
||||
ALfilter *filter = sublist->Filters + idx;
|
||||
|
||||
memset(filter, 0, sizeof(*filter));
|
||||
++leftover;
|
||||
|
||||
usemask &= ~(U64(1) << idx);
|
||||
}
|
||||
sublist->FreeMask = ~usemask;
|
||||
}
|
||||
if(leftover > 0)
|
||||
WARN("(%p) Deleted "SZFMT" Filter%s\n", device, leftover, (leftover==1)?"":"s");
|
||||
}
|
||||
|
||||
|
||||
@@ -654,15 +638,7 @@ static void InitFilterParams(ALfilter *filter, ALenum type)
|
||||
filter->HFReference = LOWPASSFREQREF;
|
||||
filter->GainLF = 1.0f;
|
||||
filter->LFReference = HIGHPASSFREQREF;
|
||||
|
||||
filter->SetParami = lp_SetParami;
|
||||
filter->SetParamiv = lp_SetParamiv;
|
||||
filter->SetParamf = lp_SetParamf;
|
||||
filter->SetParamfv = lp_SetParamfv;
|
||||
filter->GetParami = lp_GetParami;
|
||||
filter->GetParamiv = lp_GetParamiv;
|
||||
filter->GetParamf = lp_GetParamf;
|
||||
filter->GetParamfv = lp_GetParamfv;
|
||||
filter->vtab = &ALlowpass_vtable;
|
||||
}
|
||||
else if(type == AL_FILTER_HIGHPASS)
|
||||
{
|
||||
@@ -671,15 +647,7 @@ static void InitFilterParams(ALfilter *filter, ALenum type)
|
||||
filter->HFReference = LOWPASSFREQREF;
|
||||
filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
|
||||
filter->LFReference = HIGHPASSFREQREF;
|
||||
|
||||
filter->SetParami = hp_SetParami;
|
||||
filter->SetParamiv = hp_SetParamiv;
|
||||
filter->SetParamf = hp_SetParamf;
|
||||
filter->SetParamfv = hp_SetParamfv;
|
||||
filter->GetParami = hp_GetParami;
|
||||
filter->GetParamiv = hp_GetParamiv;
|
||||
filter->GetParamf = hp_GetParamf;
|
||||
filter->GetParamfv = hp_GetParamfv;
|
||||
filter->vtab = &ALhighpass_vtable;
|
||||
}
|
||||
else if(type == AL_FILTER_BANDPASS)
|
||||
{
|
||||
@@ -688,15 +656,7 @@ static void InitFilterParams(ALfilter *filter, ALenum type)
|
||||
filter->HFReference = LOWPASSFREQREF;
|
||||
filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
|
||||
filter->LFReference = HIGHPASSFREQREF;
|
||||
|
||||
filter->SetParami = bp_SetParami;
|
||||
filter->SetParamiv = bp_SetParamiv;
|
||||
filter->SetParamf = bp_SetParamf;
|
||||
filter->SetParamfv = bp_SetParamfv;
|
||||
filter->GetParami = bp_GetParami;
|
||||
filter->GetParamiv = bp_GetParamiv;
|
||||
filter->GetParamf = bp_GetParamf;
|
||||
filter->GetParamfv = bp_GetParamfv;
|
||||
filter->vtab = &ALbandpass_vtable;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -705,15 +665,7 @@ static void InitFilterParams(ALfilter *filter, ALenum type)
|
||||
filter->HFReference = LOWPASSFREQREF;
|
||||
filter->GainLF = 1.0f;
|
||||
filter->LFReference = HIGHPASSFREQREF;
|
||||
|
||||
filter->SetParami = null_SetParami;
|
||||
filter->SetParamiv = null_SetParamiv;
|
||||
filter->SetParamf = null_SetParamf;
|
||||
filter->SetParamfv = null_SetParamfv;
|
||||
filter->GetParami = null_GetParami;
|
||||
filter->GetParamiv = null_GetParamiv;
|
||||
filter->GetParamf = null_GetParamf;
|
||||
filter->GetParamfv = null_GetParamfv;
|
||||
filter->vtab = &ALnullfilter_vtable;
|
||||
}
|
||||
filter->type = type;
|
||||
}
|
||||
|
||||
+105
-113
@@ -21,85 +21,101 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
#include "alError.h"
|
||||
#include "alListener.h"
|
||||
#include "alSource.h"
|
||||
|
||||
#define DO_UPDATEPROPS() do { \
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
|
||||
UpdateListenerProps(context); \
|
||||
else \
|
||||
ATOMIC_FLAG_CLEAR(&listener->PropsClean, almemory_order_release); \
|
||||
} while(0)
|
||||
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value)
|
||||
{
|
||||
ALlistener *listener;
|
||||
ALCcontext *context;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
listener = context->Listener;
|
||||
almtx_lock(&context->PropLock);
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
if(!(value >= 0.0f && isfinite(value)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
context->Listener->Gain = value;
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener gain out of range");
|
||||
listener->Gain = value;
|
||||
DO_UPDATEPROPS();
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
if(!(value >= 0.0f && isfinite(value)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
context->Listener->MetersPerUnit = value;
|
||||
if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener meters per unit out of range");
|
||||
context->MetersPerUnit = value;
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
|
||||
UpdateContextProps(context);
|
||||
else
|
||||
ATOMIC_FLAG_CLEAR(&context->PropsClean, almemory_order_release);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener float property");
|
||||
}
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
|
||||
UpdateListenerProps(context);
|
||||
|
||||
done:
|
||||
WriteUnlock(&context->PropLock);
|
||||
almtx_unlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
|
||||
{
|
||||
ALlistener *listener;
|
||||
ALCcontext *context;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
listener = context->Listener;
|
||||
almtx_lock(&context->PropLock);
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
context->Listener->Position[0] = value1;
|
||||
context->Listener->Position[1] = value2;
|
||||
context->Listener->Position[2] = value3;
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener position out of range");
|
||||
listener->Position[0] = value1;
|
||||
listener->Position[1] = value2;
|
||||
listener->Position[2] = value3;
|
||||
DO_UPDATEPROPS();
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
context->Listener->Velocity[0] = value1;
|
||||
context->Listener->Velocity[1] = value2;
|
||||
context->Listener->Velocity[2] = value3;
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener velocity out of range");
|
||||
listener->Velocity[0] = value1;
|
||||
listener->Velocity[1] = value2;
|
||||
listener->Velocity[2] = value3;
|
||||
DO_UPDATEPROPS();
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener 3-float property");
|
||||
}
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
|
||||
UpdateListenerProps(context);
|
||||
|
||||
done:
|
||||
WriteUnlock(&context->PropLock);
|
||||
almtx_unlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
|
||||
{
|
||||
ALlistener *listener;
|
||||
ALCcontext *context;
|
||||
|
||||
if(values)
|
||||
@@ -121,32 +137,31 @@ AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
if(!(values))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
listener = context->Listener;
|
||||
almtx_lock(&context->PropLock);
|
||||
if(!values) SETERR_GOTO(context, AL_INVALID_VALUE, done, "NULL pointer");
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
|
||||
isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5])))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
SETERR_GOTO(context, AL_INVALID_VALUE, done, "Listener orientation out of range");
|
||||
/* AT then UP */
|
||||
context->Listener->Forward[0] = values[0];
|
||||
context->Listener->Forward[1] = values[1];
|
||||
context->Listener->Forward[2] = values[2];
|
||||
context->Listener->Up[0] = values[3];
|
||||
context->Listener->Up[1] = values[4];
|
||||
context->Listener->Up[2] = values[5];
|
||||
listener->Forward[0] = values[0];
|
||||
listener->Forward[1] = values[1];
|
||||
listener->Forward[2] = values[2];
|
||||
listener->Up[0] = values[3];
|
||||
listener->Up[1] = values[4];
|
||||
listener->Up[2] = values[5];
|
||||
DO_UPDATEPROPS();
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener float-vector property");
|
||||
}
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
|
||||
UpdateListenerProps(context);
|
||||
|
||||
done:
|
||||
WriteUnlock(&context->PropLock);
|
||||
almtx_unlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -158,17 +173,14 @@ AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value))
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
almtx_lock(&context->PropLock);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener integer property");
|
||||
}
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
|
||||
UpdateListenerProps(context);
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
WriteUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -188,17 +200,14 @@ AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, A
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
almtx_lock(&context->PropLock);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
||||
}
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
|
||||
UpdateListenerProps(context);
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
WriteUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -232,19 +241,16 @@ AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
WriteLock(&context->PropLock);
|
||||
if(!(values))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
almtx_lock(&context->PropLock);
|
||||
if(!values)
|
||||
alSetError(context, AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
||||
}
|
||||
if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
|
||||
UpdateListenerProps(context);
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
WriteUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -256,25 +262,24 @@ AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
ReadLock(&context->PropLock);
|
||||
if(!(value))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
almtx_lock(&context->PropLock);
|
||||
if(!value)
|
||||
alSetError(context, AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
*value = context->Listener->Gain;
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
*value = context->Listener->MetersPerUnit;
|
||||
*value = context->MetersPerUnit;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener float property");
|
||||
}
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
ReadUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -286,10 +291,10 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
ReadLock(&context->PropLock);
|
||||
if(!(value1 && value2 && value3))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
almtx_lock(&context->PropLock);
|
||||
if(!value1 || !value2 || !value3)
|
||||
alSetError(context, AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*value1 = context->Listener->Position[0];
|
||||
@@ -304,11 +309,10 @@ AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener 3-float property");
|
||||
}
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
ReadUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -333,10 +337,10 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
ReadLock(&context->PropLock);
|
||||
if(!(values))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
almtx_lock(&context->PropLock);
|
||||
if(!values)
|
||||
alSetError(context, AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
// AT then UP
|
||||
@@ -349,11 +353,10 @@ AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener float-vector property");
|
||||
}
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
ReadUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -365,17 +368,16 @@ AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
ReadLock(&context->PropLock);
|
||||
if(!(value))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
almtx_lock(&context->PropLock);
|
||||
if(!value)
|
||||
alSetError(context, AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener integer property");
|
||||
}
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
ReadUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -387,10 +389,10 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
ReadLock(&context->PropLock);
|
||||
if(!(value1 && value2 && value3))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch (param)
|
||||
almtx_lock(&context->PropLock);
|
||||
if(!value1 || !value2 || !value3)
|
||||
alSetError(context, AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*value1 = (ALint)context->Listener->Position[0];
|
||||
@@ -405,11 +407,10 @@ AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *valu
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
||||
}
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
ReadUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -429,10 +430,10 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
ReadLock(&context->PropLock);
|
||||
if(!(values))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
almtx_lock(&context->PropLock);
|
||||
if(!values)
|
||||
alSetError(context, AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
// AT then UP
|
||||
@@ -445,11 +446,10 @@ AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
||||
}
|
||||
almtx_unlock(&context->PropLock);
|
||||
|
||||
done:
|
||||
ReadUnlock(&context->PropLock);
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ void UpdateListenerProps(ALCcontext *context)
|
||||
struct ALlistenerProps *props;
|
||||
|
||||
/* Get an unused proprty container, or allocate a new one as needed. */
|
||||
props = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire);
|
||||
props = ATOMIC_LOAD(&context->FreeListenerProps, almemory_order_acquire);
|
||||
if(!props)
|
||||
props = al_calloc(16, sizeof(*props));
|
||||
else
|
||||
@@ -468,7 +468,7 @@ void UpdateListenerProps(ALCcontext *context)
|
||||
struct ALlistenerProps *next;
|
||||
do {
|
||||
next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
|
||||
} while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&listener->FreeList, &props, next,
|
||||
} while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context->FreeListenerProps, &props, next,
|
||||
almemory_order_seq_cst, almemory_order_acquire) == 0);
|
||||
}
|
||||
|
||||
@@ -489,14 +489,6 @@ void UpdateListenerProps(ALCcontext *context)
|
||||
props->Up[2] = listener->Up[2];
|
||||
|
||||
props->Gain = listener->Gain;
|
||||
props->MetersPerUnit = listener->MetersPerUnit;
|
||||
|
||||
props->DopplerFactor = context->DopplerFactor;
|
||||
props->DopplerVelocity = context->DopplerVelocity;
|
||||
props->SpeedOfSound = context->SpeedOfSound;
|
||||
|
||||
props->SourceDistanceModel = context->SourceDistanceModel;
|
||||
props->DistanceModel = context->DistanceModel;;
|
||||
|
||||
/* Set the new container for updating internal parameters. */
|
||||
props = ATOMIC_EXCHANGE_PTR(&listener->Update, props, almemory_order_acq_rel);
|
||||
@@ -505,6 +497,6 @@ void UpdateListenerProps(ALCcontext *context)
|
||||
/* If there was an unused update container, put it back in the
|
||||
* freelist.
|
||||
*/
|
||||
ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &listener->FreeList, props);
|
||||
ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &context->FreeListenerProps, props);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user