Compare commits
2659 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b907c21f48 | |||
| 8364aa5f6f | |||
| ba3f1e7468 | |||
| 74c8de6372 | |||
| e3ee534c39 | |||
| 8ecbeb4945 | |||
| 960cdec0b5 | |||
| 4a9545affa | |||
| 6273a18de5 | |||
| a3c236598a | |||
| 936a79da7d | |||
| 94013216b6 | |||
| 22856844c6 | |||
| 357beb0570 | |||
| f173a67870 | |||
| 1692dda4b9 | |||
| 10c01110f3 | |||
| 6048d70232 | |||
| e64d0f83cd | |||
| 23441be474 | |||
| 951870b189 | |||
| bda39c5038 | |||
| 066a48c5fb | |||
| 7b4a2335eb | |||
| 451b780e08 | |||
| f5194a9d8e | |||
| 659b340716 | |||
| 1c1e878be7 | |||
| 87423f046e | |||
| 15a58eb383 | |||
| cdfc5a4d31 | |||
| ce046d2f03 | |||
| 6ce464aed8 | |||
| 7c847e62c3 | |||
| 38e8dc8540 | |||
| 6864053eee | |||
| 0ad3f61351 | |||
| d3980bdebc | |||
| 531c0d8e6b | |||
| a3dbe08c8b | |||
| e4b779c492 | |||
| a3b1d4a5e2 | |||
| 5a339a2a5b | |||
| 7b41ed7ec4 | |||
| 0ff4a91442 | |||
| 8592b9371a | |||
| 0c31ccd456 | |||
| 9370fea359 | |||
| 36381f3164 | |||
| 994c3f00e5 | |||
| 85e135beea | |||
| 17682cb0ce | |||
| 23cae7783d | |||
| 8635733d59 | |||
| 15d7ed2f0d | |||
| 0d3b18b17d | |||
| f4cdecebcf | |||
| c5af088b5f | |||
| f4f1c1eeed | |||
| 8dec92531b | |||
| dc76013126 | |||
| 23979ac648 | |||
| f809d3c81a | |||
| d0a64fe191 | |||
| 5de7271bcd | |||
| ad92181a26 | |||
| ff915534cd | |||
| b88aaaf1bc | |||
| 47f5e710c2 | |||
| 5afc29f65d | |||
| f667782df2 | |||
| 3f45c00779 | |||
| 17f79ea43e | |||
| fda5bc2bd2 | |||
| 5303ee2588 | |||
| cb726d87ce | |||
| fec1fec373 | |||
| 347d8f94e8 | |||
| 8b1b52a687 | |||
| 56bb8689d4 | |||
| 3c6752e765 | |||
| c06bb88756 | |||
| 8577df76c4 | |||
| 22982948cf | |||
| 8f8898b7b0 | |||
| 8176d2c058 | |||
| 389a2f7b4b | |||
| d19e42a723 | |||
| b9462bbe3c | |||
| 81b3395b41 | |||
| be903d67b8 | |||
| fb25a70f95 | |||
| 0fd215cb84 | |||
| 57c683f822 | |||
| a8deaf12f4 | |||
| c29eb63489 | |||
| 19ec7b2ad2 | |||
| 4c706f59d9 | |||
| dfded9595c | |||
| d0fac3fe1d | |||
| c07fb7b45c | |||
| 8ca1f4f371 | |||
| be66692f94 | |||
| 09d5475aa6 | |||
| 2ff266ca83 | |||
| 6d1622dc68 | |||
| fc0be88c0f | |||
| 3b2fcb3ef6 | |||
| d2c0a43498 | |||
| 933f51a8bf | |||
| 1d45c439b8 | |||
| 73614f228e | |||
| fd62868c17 | |||
| f0b65aa6b7 | |||
| 2d2bc25fd0 | |||
| b6e1042e8c | |||
| fd5e7f1466 | |||
| 0ec0f7561c | |||
| 3d6ffa282d | |||
| 4ca561acd3 | |||
| 13eac2552e | |||
| 5ef5d218c4 | |||
| 3fc9791747 | |||
| 7d997277a0 | |||
| a48f5d26fd | |||
| 9008bcbe54 | |||
| 2729a1549b | |||
| e32e3df3aa | |||
| 85e990e915 | |||
| 2b3550b61c | |||
| 100c059157 | |||
| ecdfcdbfa5 | |||
| 2e38de349c | |||
| 6ea050cc6c | |||
| 4b433c900e | |||
| dd2e6b7902 | |||
| e650db98f7 | |||
| 4a8d38c19a | |||
| f313ce2a96 | |||
| cd983245f1 | |||
| 8e04a8a022 | |||
| 7b782f6afa | |||
| ef9dfe3772 | |||
| 9317ec59b9 | |||
| c9083d04fa | |||
| 5a1abf6918 | |||
| 29b5dae6aa | |||
| bd2721dc7a | |||
| a5631e05cc | |||
| cd53a4b74c | |||
| 190ab90c0d | |||
| 82dd2d875e | |||
| 3a26ebef0b | |||
| 0b5b39d5e6 | |||
| 70f1e54068 | |||
| 1efddac3db | |||
| c5b25ba2a7 | |||
| a11e1c0858 | |||
| fdcdda2ed3 | |||
| 028bd9767c | |||
| 042003896f | |||
| 6dcd4fec7d | |||
| ef1b34dcd2 | |||
| f0797e27f3 | |||
| 16d3316f70 | |||
| a6974a6c83 | |||
| a1fbb434e8 | |||
| debd5346fb | |||
| f311f74001 | |||
| 1d2504d12e | |||
| 4454ae25c7 | |||
| 3582a44dff | |||
| 343200d229 | |||
| 58235bec17 | |||
| b89cc3417b | |||
| 9f8615c670 | |||
| 851a917b03 | |||
| e78acce652 | |||
| ddd1550a8b | |||
| e9297d9744 | |||
| 3e28fb33a3 | |||
| 9e25ea8666 | |||
| 67b4bd1031 | |||
| 9642bf637d | |||
| a455704148 | |||
| eebde08e65 | |||
| c4383b65e2 | |||
| eea46f268d | |||
| bf97f58edb | |||
| 8b7c71e20e | |||
| bd88e1743a | |||
| a2bddb7b40 | |||
| 3e274c3a57 | |||
| 1c01e94237 | |||
| d8d609b940 | |||
| 38b98de78e | |||
| 1aff37114a | |||
| fa12855d19 | |||
| 0ea979a262 | |||
| 1a76a3238c | |||
| cbb94405db | |||
| 2edd047178 | |||
| 2bc16e9947 | |||
| a48d235ef4 | |||
| bca7c682f1 | |||
| 2a7f3c015e | |||
| db90985f6a | |||
| a2bb4c3f13 | |||
| d066c7b124 | |||
| b2e533fbfc | |||
| 34e96aef60 | |||
| 2660ecfd4a | |||
| 3bdb3f9db7 | |||
| d90f684f16 | |||
| 804b08e0ca | |||
| f150676873 | |||
| f9d70aa9ef | |||
| 149df6d4db | |||
| 28e9f0db8c | |||
| 83970369e0 | |||
| 7dd4a2348f | |||
| a4cf378af8 | |||
| 9af8e10c20 | |||
| 0b527621a5 | |||
| 8fd224f841 | |||
| 420599f8e3 | |||
| 7fed6383c0 | |||
| ada4868d86 | |||
| a4fbe158d4 | |||
| 84afe4fb40 | |||
| d5ac11c87f | |||
| f6ad3ef221 | |||
| e9aee578a7 | |||
| 0d2521e30e | |||
| df3c54a4bf | |||
| ad71565e45 | |||
| ca1008571e | |||
| d856183c4b | |||
| dbe0897c7f | |||
| 5215b9cad2 | |||
| b435a8e01b | |||
| 32a2f95885 | |||
| bb969c3ccc | |||
| 043e25b602 | |||
| 96c559fbef | |||
| 887763e41c | |||
| 1d266aa834 | |||
| 59fc9aac0e | |||
| 8badd3740e | |||
| 1d69ec9ce1 | |||
| 9b685bace5 | |||
| 2eda15fc77 | |||
| c041a99f72 | |||
| b8d56190d1 | |||
| cb3315f9d3 | |||
| b979a8005f | |||
| 1353cb0d88 | |||
| 579e7c10c2 | |||
| 6c8bf9ec42 | |||
| 20e5ec18e1 | |||
| 5abefaed0a | |||
| 36df67f546 | |||
| 528c8c5e77 | |||
| 8a00c24012 | |||
| 4b5e8b8c40 | |||
| d1f1a1d056 | |||
| 47f5c436c8 | |||
| fbb4cbbe01 | |||
| e5d39a5f4c | |||
| c3b1c31d9b | |||
| 8cc3d05949 | |||
| 2149ccd985 | |||
| c6dd479229 | |||
| 8a51995cfa | |||
| 959d75edc8 | |||
| b020dd13fd | |||
| 184cf30cf7 | |||
| d124aee4d7 | |||
| 505ef82246 | |||
| e8517d8600 | |||
| 18ab9cbbdd | |||
| 29cb5058c0 | |||
| 9c70ca9da6 | |||
| 7bc08bb08c | |||
| e0d24f9da4 | |||
| e615ea3cf9 | |||
| 219a0e6352 | |||
| 0ef87227c1 | |||
| cce9609b9e | |||
| e76bc53d37 | |||
| 90ae4b7c0f | |||
| 023981acb9 | |||
| 1290f37750 | |||
| add3013234 | |||
| 4f1104125b | |||
| c6821e5dd1 | |||
| f1a4b95b8c | |||
| 5e0fc84f49 | |||
| 569374da2c | |||
| 482fb37b68 | |||
| 570eb4e1a7 | |||
| 48b472abb7 | |||
| 7b93e10f7a | |||
| 7570195b09 | |||
| d997be67fd | |||
| af8fda8a4a | |||
| 2ca673cca2 | |||
| 20886059fd | |||
| 18620ab5e0 | |||
| d11d061b2c | |||
| 4f3e71e5b7 | |||
| 045959e9c0 | |||
| 03fd2b8266 | |||
| de5c54db09 | |||
| cbb82647fb | |||
| e2cb41dc61 | |||
| 72986882f5 | |||
| a25260ea8a | |||
| f5e10b9945 | |||
| 52deb557d5 | |||
| 83038c0dab | |||
| 55f851093f | |||
| 92433532fe | |||
| 81e049bd47 | |||
| 0ce0a88fd6 | |||
| b0d511a860 | |||
| cc599333a8 | |||
| 9959972c7e | |||
| 842aeb888b | |||
| 22f6eb18a2 | |||
| d8bfd12d31 | |||
| 0a030c2bd9 | |||
| f5ce73646c | |||
| 8c33b4d742 | |||
| 983fa4630a | |||
| 40a0692a1c | |||
| ff63188cc2 | |||
| e6e3937fa9 | |||
| 168149ce9d | |||
| 0c5cbafcd8 | |||
| 213e3ba4af | |||
| 6ee54fb1f3 | |||
| d6f7aac1bb | |||
| 71b177918d | |||
| be2aeeb2fb | |||
| dc74473025 | |||
| d346038102 | |||
| cf066fd306 | |||
| bb787ba8ee | |||
| 308d87b12a | |||
| 372f3178f8 | |||
| 74f2864c74 | |||
| cde2f825d5 | |||
| 14a04020ff | |||
| 2e279684d7 | |||
| 7657fbb296 | |||
| 143f786d1a | |||
| bf4ddfef6d | |||
| 61569e09ed | |||
| a4126b66d4 | |||
| f82f3f10ab | |||
| 15b68fe694 | |||
| fb1f9aad6e | |||
| a1c440bd09 | |||
| f3a87d72b2 | |||
| b0a6b6427c | |||
| a6eb38ea7f | |||
| a2d9133ffc | |||
| c442c93a93 | |||
| ea8a85b19c | |||
| 3867e3857d | |||
| 6e7c0e15eb | |||
| fd3acf19fe | |||
| a8249c3469 | |||
| 3145bac8c6 | |||
| 99d82690ef | |||
| 1b9a722601 | |||
| 89506435fa | |||
| 0ddb9888fe | |||
| 9dd16f8b9c | |||
| 9a4ded2491 | |||
| c68ce288d0 | |||
| 5731534e8d | |||
| 9f43de62ab | |||
| ef8002a832 | |||
| e6e3bdee83 | |||
| 0e51becd58 | |||
| 46b2115bfb | |||
| 0626a44eb1 | |||
| 29e0af5ef0 | |||
| 09d1319e82 | |||
| ea0aea6508 | |||
| 6f711c32ba | |||
| 926ecc2dbe | |||
| 7c4339c195 | |||
| 755f161fc5 | |||
| 7bfea9087e | |||
| 11a7b3e8f8 | |||
| 112b0f166f | |||
| 69f3a7da6d | |||
| 9e2a8843a8 | |||
| 68a32a3486 | |||
| a4bc0a46e9 | |||
| 49baa9128d | |||
| f193b539ce | |||
| 35fd4dcf77 | |||
| 748be9a97d | |||
| 1a90c7f498 | |||
| be883131a7 | |||
| 658d35a1b0 | |||
| d387deb3e8 | |||
| 7206f5873c | |||
| 05e9c824d5 | |||
| 65a5311449 | |||
| d65279ac2a | |||
| 50c8d9181a | |||
| 08ba5d9dba | |||
| e118ac93a0 | |||
| 9ebeae6228 | |||
| 50b74629dd | |||
| 8d7559d9d0 | |||
| 56899716d6 | |||
| 443e6b73b2 | |||
| 5fdc3c093e | |||
| 1739998045 | |||
| bdc5850120 | |||
| 24ffb5b464 | |||
| 90fd92628d | |||
| 0c80f62de7 | |||
| 6f3dac6f58 | |||
| e252a100f7 | |||
| a5bfc1e30b | |||
| 9a57aca8d4 | |||
| 2aa95db5b9 | |||
| da2c9732da | |||
| cf66516a27 | |||
| 4079ee2200 | |||
| 5f961a392e | |||
| 540a99e71f | |||
| bb54743425 | |||
| 86f250a3f5 | |||
| 4834f56779 | |||
| 2dd87946fd | |||
| 355b8db915 | |||
| 13066bbf87 | |||
| 42a67731c4 | |||
| 3a31402b1e | |||
| d9846f3219 | |||
| e6e7e461c0 | |||
| 5dc4ea15bb | |||
| f790d0e91f | |||
| 49c17ad520 | |||
| fcf9e72202 | |||
| 5f85a1dcf0 | |||
| 01f547b93e | |||
| 3ff33e2f79 | |||
| e74e3e2b23 | |||
| 7d94e9c52f | |||
| 0a5a08c0fb | |||
| e699ab8614 | |||
| 6b2d3c3cfa | |||
| 396a2f1595 | |||
| 7640c658ee | |||
| 37d183efc0 | |||
| 06dd9f9b8c | |||
| 4e73b95fc0 | |||
| 4c35f7c632 | |||
| 15aa12847d | |||
| 699d84491f | |||
| d030e07879 | |||
| 1d41122372 | |||
| deecbbc55d | |||
| d9aedc1416 | |||
| fc928bb75a | |||
| 4659669a74 | |||
| 5aa274999c | |||
| 09665715ed | |||
| 0d7996409e | |||
| fe55cd6de8 | |||
| 7156f4439c | |||
| 0678e3fc2b | |||
| f85d733f9d | |||
| 2b772a5607 | |||
| 307ac564ca | |||
| cc30c5754f | |||
| d5f687b2fa | |||
| 35f9e35aa3 | |||
| b5ae424dbd | |||
| 677b864565 | |||
| b650ecbc23 | |||
| 94ddc5cb18 | |||
| cb3f82a4bf | |||
| f7556b827a | |||
| b0878fe609 | |||
| c911ca89a3 | |||
| a43b296c60 | |||
| d7de86966d | |||
| 0df5eb90ea | |||
| ede3c20880 | |||
| 5dc3f3626d | |||
| d596cf9cf3 | |||
| 1bd828603e | |||
| 03a6bf22a5 | |||
| 99933fac49 | |||
| 57491bf14c | |||
| 666acb314b | |||
| 4288d3cd01 | |||
| 1b5c3495c9 | |||
| 1e536cf7ca | |||
| 73fc4592ab | |||
| 8083fb5be7 | |||
| b9468dc917 | |||
| cd90bdd9bd | |||
| 0653680690 | |||
| 0095a59fc9 | |||
| ee92b3142d | |||
| ef0341c180 | |||
| 7f51569186 | |||
| 189d73dd87 | |||
| f244ef6f82 | |||
| bcc2b9a490 | |||
| 1dd05bac5c | |||
| a343d78724 | |||
| 57eef756c3 | |||
| 5349b90a48 | |||
| ac525db845 | |||
| a846418964 | |||
| 3f5914e094 | |||
| 517be463ae | |||
| dc13ce7a0a | |||
| c834713526 | |||
| 2633fbfc4b | |||
| e0babed29b | |||
| be446366fb | |||
| ea1d7d56d3 | |||
| 82d4e69da1 | |||
| f4cf84e72d | |||
| 8615a109ce | |||
| f8c68291d3 | |||
| 29a4ac329a | |||
| 5dad1688d4 | |||
| 98b0a3187e | |||
| 7794e53667 | |||
| bb0207d784 | |||
| a3c76c3274 | |||
| a48f362d28 | |||
| 489723723c | |||
| 182b08d8bc | |||
| 5554b8cfc9 | |||
| 3a1613253b | |||
| 6a76f50fa3 | |||
| 37fa82f138 | |||
| d315feae40 | |||
| 08dfbcfd5c | |||
| d339120145 | |||
| 80ea801204 | |||
| 191e8172f0 | |||
| b029b77784 | |||
| 4830b1afe7 | |||
| 25b9c3d0c1 | |||
| 4386ee32ce | |||
| a603dd55ff | |||
| 9228d13333 | |||
| bca959f372 | |||
| d6eff654be | |||
| e0fecdcaaa | |||
| 414a9edc6e | |||
| b84f6d100f | |||
| 801b7cb059 | |||
| 10dbb1bc9b | |||
| ca83629e4e | |||
| 5874e387a8 | |||
| d3c70e63b4 | |||
| 551f893ae9 | |||
| 692ab1085d | |||
| 94aeb5f52e | |||
| 9760a592b2 | |||
| f868d881d9 | |||
| 3d921e0e6f | |||
| a1a3f51be2 | |||
| c851e2c611 | |||
| ddf521eddc | |||
| 7142e3828f | |||
| 426429279b | |||
| 7460dd1b08 | |||
| 90a51d7897 | |||
| bc9c3de969 | |||
| 9f0e49917d | |||
| 779eb4b82b | |||
| 6e43757fed | |||
| a507d0aa75 | |||
| d4aa4e16a0 | |||
| d2b3615943 | |||
| 3437769ba0 | |||
| 3c65c946d4 | |||
| 16d5d5760c | |||
| 7a424ab4bf | |||
| 823782a33f | |||
| 321644de7c | |||
| a407d57639 | |||
| 0a6eff6a89 | |||
| 6fd857739c | |||
| 21f1e54cb9 | |||
| f8c95f1e90 | |||
| 8d9fb5109b | |||
| 20bcb68ad6 | |||
| 0617df9b5f | |||
| f24cb44781 | |||
| 2912d130c2 | |||
| 034935b2e1 | |||
| c1cdd3095b | |||
| dc7ed39fa7 | |||
| c8603092d3 | |||
| f065700ef9 | |||
| e54983694b | |||
| 3ed425d7ef | |||
| af8be56f17 | |||
| bf465eb2eb | |||
| 8ceb800def | |||
| b9e30f7604 | |||
| f93bfab824 | |||
| ff5277f4d7 | |||
| 6617985a4e | |||
| 54b8690781 | |||
| 959b768911 | |||
| a421e515be | |||
| 11365b42c7 | |||
| b42fcce014 | |||
| fc31a41473 | |||
| b3841653c6 | |||
| 32d3bde261 | |||
| c787a9bf74 | |||
| 4f72da0005 | |||
| 69d373db78 | |||
| 0803f1b2f1 | |||
| c080dcae8d | |||
| 997f6228de | |||
| 32e85d469b | |||
| 1518895e15 | |||
| 44172f701c | |||
| e1d5be570e | |||
| b5fece0381 | |||
| 1461903e0d | |||
| 8aa237ac23 | |||
| 5d014ba85f | |||
| 79edd2cf3b | |||
| 6425480d40 | |||
| 8448b94c0b | |||
| 9ee3d01f6e | |||
| 69e64e1ed2 | |||
| ba52ac9bcd | |||
| 43c375716a | |||
| ead8573aaf | |||
| 41175ec84c | |||
| b9fd3f9eb4 | |||
| 01a5946a2e | |||
| 2e605590a2 | |||
| 764ea95781 | |||
| 99fa5911bc | |||
| 2f676832bc | |||
| 94884ed04b | |||
| 2219ce2475 | |||
| 393d0eb138 | |||
| 51791e6a54 | |||
| d85f0c0bb2 | |||
| 0a95c470b0 | |||
| 7ebc1bb69e | |||
| 52096234e1 | |||
| 61c6a38f04 | |||
| f23a28e8f3 | |||
| 314f9e2686 | |||
| 2c7f94e285 | |||
| a375104826 | |||
| 62dd5f33b2 | |||
| 77a2c8bcf0 | |||
| d984cec3a1 | |||
| 5c7680ec84 | |||
| 18157bc331 | |||
| 27b66b2008 | |||
| f48c0c9e0a | |||
| b3f54124bd | |||
| eebc6c0419 | |||
| 144059b062 | |||
| 4017e4a96a | |||
| ed0568bb24 | |||
| 160e901381 | |||
| a9fac12969 | |||
| 3c90ed95af | |||
| 48d5002190 | |||
| 784e396cc7 | |||
| 8bd77be522 | |||
| ff44ebfd5b | |||
| a5471ce0a4 | |||
| 817ca82e4b | |||
| 68898dad1e | |||
| 024374e0ce | |||
| 647398d7c6 | |||
| dcefeac6e6 | |||
| 9fd8731480 | |||
| 056fa2a474 | |||
| fe5428dc49 | |||
| a371de080b | |||
| 0e0f70b288 | |||
| 7257aa3ed2 | |||
| 69e0c19767 | |||
| 8f14902290 | |||
| 2290e08053 | |||
| a7a3d1e194 | |||
| 9c3f1d11f0 | |||
| 764e3aa496 | |||
| e96cc656e9 | |||
| 48aa1e10d6 | |||
| 6556626055 | |||
| 59a38442ac | |||
| 0c7c5327c9 | |||
| 3e663d7307 | |||
| feeec34333 | |||
| bcf4842eb3 | |||
| b1ae44206f | |||
| 0ad71a67d4 | |||
| 334a7c1d59 | |||
| 8d874512b6 | |||
| 9e84f38a46 | |||
| 06c8442af6 | |||
| 0a4be3470e | |||
| 4bf5466f64 | |||
| 82caba6bbe | |||
| 8808159d55 | |||
| 2759239754 | |||
| d1c5599c8e | |||
| 6571d80540 | |||
| 2da6caeaa6 | |||
| e157238ce7 | |||
| 49446c7b32 | |||
| f105a8bb4b | |||
| 18807e06fe | |||
| 357cf72ab3 | |||
| 2376683128 | |||
| f667c028e6 | |||
| dbb6da8c47 | |||
| 4f068b51e5 | |||
| be4a4d7468 | |||
| eb15b13ca1 | |||
| 38a9e642df | |||
| b80efeb87d | |||
| 52efb8d7f4 | |||
| a6fa4a8bc2 | |||
| 1bed3cff21 | |||
| cdbb0722f6 | |||
| 2eb8a520d4 | |||
| 0a07ed14c2 | |||
| 604726c639 | |||
| eaccaa5028 | |||
| a5d94f5d09 | |||
| af1936be5d | |||
| e4186f4903 | |||
| 3ee0f8feb9 | |||
| 5c8c40afef | |||
| 5516d8df0b | |||
| fba9ac6db1 | |||
| de387c853f | |||
| 44da54ec7f | |||
| b6da74b6e2 | |||
| 7e9960f7f1 | |||
| c6872d4d86 | |||
| 5b706f3bdc | |||
| 80459b13e4 | |||
| 012fbc36b9 | |||
| 9dae98071f | |||
| 4c436b106d | |||
| 1c523df160 | |||
| 6aa722808e | |||
| c693e649e6 | |||
| 1976d64814 | |||
| 78e7c1c27b | |||
| a7ad6080f0 | |||
| 563f16dc2a | |||
| 43b406ad9b | |||
| dd48375bd6 | |||
| 678ce4109c | |||
| b313c881c0 | |||
| 2c501f3b28 | |||
| e725db4480 | |||
| 23703ff40f | |||
| fb11eb1190 | |||
| 061b75b343 | |||
| 6bf8f01af5 | |||
| bf54de4984 | |||
| eb1080910e | |||
| d237410d1d | |||
| 2486f13dae | |||
| 3fd0f23e48 | |||
| 991aba286f | |||
| a3846ba53b | |||
| 0423caeb8c | |||
| f1b0f67553 | |||
| 89fae876a4 | |||
| ac4930e233 | |||
| e358882f14 | |||
| 564a182259 | |||
| 9cd086a707 | |||
| c2adea1999 | |||
| 7d81d6b24e | |||
| 84944af142 | |||
| be25e6802d | |||
| 9b6a226da5 | |||
| eb5073a8a7 | |||
| beb84bdc21 | |||
| a20f1fa779 | |||
| 7445ffe1a6 | |||
| c225c59b0a | |||
| 37a6705556 | |||
| ed5738bfa4 | |||
| 5e14a83030 | |||
| 3b9b176f14 | |||
| 838f0fd71b | |||
| fd83e006ec | |||
| e51574c764 | |||
| 893a1bedbc | |||
| 4621a7b426 | |||
| 4808313e70 | |||
| 1fb9311d82 | |||
| dd34daed42 | |||
| 830428fb06 | |||
| 0ca7ab2cf8 | |||
| a0c39989f5 | |||
| 209d09ae13 | |||
| 0c030f7d9c | |||
| 2d800e99e2 | |||
| 0518ecca14 | |||
| 43b9027611 | |||
| 40ea4f4b46 | |||
| 4f624cb734 | |||
| b03013d6a7 | |||
| 9e79d144de | |||
| 40b3f60cde | |||
| 2b27d243ca | |||
| 2f07c46e20 | |||
| 3da1b540fd | |||
| 92dde81296 | |||
| e2368eb960 | |||
| 3e71a7cb94 | |||
| b92ad830ef | |||
| fc7adccd6f | |||
| 97bf718d0e | |||
| eb6360e1f8 | |||
| 41b1ba146a | |||
| 6450d0cbca | |||
| 4e281350a0 | |||
| e868dad61c | |||
| 4a3d36a176 | |||
| f3dddb6e63 | |||
| 3c3e3b123b | |||
| f02a9935c6 | |||
| 3711735ed9 | |||
| 2c802cc10a | |||
| e2e502970c | |||
| 404d601666 | |||
| 252fc40f78 | |||
| fb21a73b4e | |||
| 138a4c6d92 | |||
| c802bfccef | |||
| fae4959db3 | |||
| 8a67e5cfa2 | |||
| 617def3ef0 | |||
| 4f5aafe59f | |||
| a14383d6cc | |||
| fe1903fb56 | |||
| f0d27e5f91 | |||
| a852669dce | |||
| 3240894cd5 | |||
| 0b679167c9 | |||
| 5ac0a93fba | |||
| f7655d44a2 | |||
| 38e6bfb702 | |||
| 20ede6de59 | |||
| 5c70e6b9ab | |||
| 9bd3b88a58 | |||
| e60281c927 | |||
| 01136ecd51 | |||
| 1212523470 | |||
| 73105b2d4b | |||
| 06ab9cf70e | |||
| a3d4868ef4 | |||
| 815b03855b | |||
| 6750bbb1d7 | |||
| 43c8c4fa29 | |||
| 25b8a95987 | |||
| 99fcd6f816 | |||
| 513c6db0d5 | |||
| 1b7f01b7cc | |||
| a4f21aaa64 | |||
| 39bc2ba65c | |||
| d598f82722 | |||
| c22408cb2a | |||
| 8c3419ed48 | |||
| a98361ebbe | |||
| 999f0783c5 | |||
| 2a6dd2f243 | |||
| f3349221c6 | |||
| 65c7c468cc | |||
| ebdf636759 | |||
| 6b870714a9 | |||
| 708f3ccc3d | |||
| 84f4afa5f2 | |||
| 991e21a712 | |||
| 1a9cf7ab80 | |||
| 9501f1cb9e | |||
| d52cb3708b | |||
| fa8d374613 | |||
| 0d26b0dcc2 | |||
| a6287fd407 | |||
| 16bdf79d4c | |||
| 0b47844606 | |||
| 468759e6ab | |||
| f138774d78 | |||
| 068da3fe83 | |||
| 29b11db13f | |||
| dca6254b35 | |||
| cccec40e87 | |||
| b5d9d8e005 | |||
| 825c5b5282 | |||
| 25ca179389 | |||
| 8bd0592e3f | |||
| 06a0d1b86e | |||
| 41b2c2f3bb | |||
| 68faef5b84 | |||
| abf1309fb6 | |||
| d3891af36e | |||
| ed82194260 | |||
| 012e61b105 | |||
| d19d7321d9 | |||
| d4e82d441e | |||
| 2ceea21f76 | |||
| 12327da4fe | |||
| 52d403b0d1 | |||
| ccd35bffe7 | |||
| d1c93525ed | |||
| f0732426f2 | |||
| eedbd1e81c | |||
| 6005e7f27f | |||
| e779e64c95 | |||
| 3baaf99a6e | |||
| 4a86983a6f | |||
| c19d9d3452 | |||
| 79b6d89b3f | |||
| 535e8fd0f7 | |||
| a240abb6c8 | |||
| dd9d30e248 | |||
| 95535ce99e | |||
| df2e82da78 | |||
| a1daec66e2 | |||
| 657ee85136 | |||
| 965306b296 | |||
| 28086f6cb7 | |||
| 45bb010b28 | |||
| 90e0c74721 | |||
| a20341ac2a | |||
| 7635afcb52 | |||
| 74aee374a6 | |||
| 4f70766b95 | |||
| 9f58edd7a8 | |||
| 0f3a575a09 | |||
| ec74fb9ba2 | |||
| ff809f2271 | |||
| 41f52fdd6a | |||
| aa827790bd | |||
| 38e7bd5359 | |||
| 613ce3f976 | |||
| ce8a0a9fd5 | |||
| f59e4ba7c2 | |||
| df469bfdea | |||
| f4642953d4 | |||
| 88b0c30152 | |||
| 01183ad93b | |||
| 3f97af4c8c | |||
| 4c5cb2189b | |||
| 98ff6f990a | |||
| aaa81e00b7 | |||
| 1c44a96768 | |||
| 34fef456c8 | |||
| 7e81918f7b | |||
| 1b840a3db8 | |||
| 4e440fff62 | |||
| 81208acd6e | |||
| af3b5c23c8 | |||
| 9f208417df | |||
| f1ce139997 | |||
| f56dddfa73 | |||
| 2bf1979d4a | |||
| 89cab3cd39 | |||
| 6723d27e06 | |||
| 1c03025090 | |||
| 7a37a63b8e | |||
| 2b5fdad2c8 | |||
| a6f9ede849 | |||
| 78dac1bf4a | |||
| 795909c39e | |||
| 3c042a054d | |||
| 332d66c183 | |||
| de006947ac | |||
| 8a128e5d25 | |||
| 34cbacf424 | |||
| f2fa5e7c88 | |||
| 3b735cc34a | |||
| 238caa1492 | |||
| e5ebe345ad | |||
| 965608356f | |||
| bc1ce7b3ac | |||
| 6a3619c40f | |||
| 50fa9a44be | |||
| 4937a48bd9 | |||
| 987a81c4de | |||
| f5e0500df4 | |||
| 37924887e8 | |||
| 0db7c4131b | |||
| a56a974149 | |||
| af7f3768fa | |||
| 0865db564f | |||
| 2b020040b4 | |||
| 3ae5fcbd7e | |||
| ac4fc4026c | |||
| 9737f68315 | |||
| 2a8c26e390 | |||
| 3c395a82db | |||
| 15c6b9b165 | |||
| 147fd9de4e | |||
| 0bca771a88 | |||
| f4ff63e271 | |||
| bbcf4e8c1d | |||
| 2859357939 | |||
| 2cbb565d09 | |||
| 4d157d2849 | |||
| 64cd763e19 | |||
| c3f387da70 | |||
| 6ebeb3b4f7 | |||
| 2e18baa3c8 | |||
| c9e87a19a9 | |||
| 3b87e29e63 | |||
| e9a20cb985 | |||
| cfc5205569 | |||
| 17dfaa3aae | |||
| 0a3eba08d8 | |||
| add53e28c2 | |||
| f42f655ea9 | |||
| 8304fc1bef | |||
| ea84fee952 | |||
| d6fd52a0e5 | |||
| 0dde7dd88a | |||
| 1323e11701 | |||
| 7ac9944bb3 | |||
| e32e2fd6b8 | |||
| 6bd535bed0 | |||
| 524c88c402 | |||
| 1c096b101d | |||
| 583dc8dbca | |||
| befa4e7528 | |||
| bc6751237c | |||
| 6f3be31b60 | |||
| 8001d86751 | |||
| acca3e5a21 | |||
| d17f835e74 | |||
| ef1f8aa9a0 | |||
| a5ac6eb524 | |||
| ac84d95c6e | |||
| 785f52aa29 | |||
| 051195a991 | |||
| 90ca6675c2 | |||
| 0db9730a50 | |||
| ad2643d8af | |||
| 46840ee787 | |||
| 1e0282a238 | |||
| 259e4f5b38 | |||
| e44229c74e | |||
| 92827cc467 | |||
| dd3d5bc2d4 | |||
| b4c7b3309a | |||
| 010a66487d | |||
| aed35fd811 | |||
| 8d9838012d | |||
| 57d36ec582 | |||
| febc2a50cb | |||
| 74e128bd0f | |||
| c34d78f417 | |||
| 611bd0b2d3 | |||
| 267d38cf20 | |||
| 26456f13e1 | |||
| 2484c44db0 | |||
| a8a13fcbd8 | |||
| 0a193c5645 | |||
| 2450ee1723 | |||
| 67f1cdfcbe | |||
| 549d542a31 | |||
| 2cdb58fb85 | |||
| 68ab232364 | |||
| 871d61f3f9 | |||
| b08bd3746e | |||
| 39ab0b0aa3 | |||
| 5c70cacc13 | |||
| 8c2cd057b2 | |||
| fdc4133ac9 | |||
| 3833c021c0 | |||
| cafbd9461e | |||
| 6995ac1ed2 | |||
| 58e3dc3d52 | |||
| 3e49e79140 | |||
| dec8e81211 | |||
| 4188a5f3f3 | |||
| 31d124582b | |||
| 248e079301 | |||
| 5ce850570f | |||
| 648464a2da | |||
| 29f77003c0 | |||
| dfa38ed7a5 | |||
| 66c918c84b | |||
| 77f55da6c7 | |||
| d6af7cdcb2 | |||
| 0ad8047820 | |||
| 2115f578e9 | |||
| 511b4790d0 | |||
| d6491e21f9 | |||
| cff0f938e5 | |||
| 5418519637 | |||
| b023dbe7b8 | |||
| 8092cd9205 | |||
| 60da5bd338 | |||
| c9e64596a4 | |||
| 08bd5e8b37 | |||
| 125b743e5c | |||
| 9a3eb397f4 | |||
| 84c1f2bfd0 | |||
| d7ba49ea62 | |||
| e47557630e | |||
| f6a1e53f19 | |||
| a0a015f19f | |||
| e3bea63f94 | |||
| 6b154025ab | |||
| b9b2bdb37a | |||
| 7b35c77062 | |||
| c23cfdcfa1 | |||
| 929e9bfac1 | |||
| b51afb74da | |||
| 5a43cd3b64 | |||
| 5d08563975 | |||
| adb83ad3c0 | |||
| 2d0e028993 | |||
| 703d72f20e | |||
| b7d67d8bcc | |||
| 7e5b13aabd | |||
| 7fbcb4b9e6 | |||
| 767a5b3c73 | |||
| a08080bd82 | |||
| 415bf58a06 | |||
| 3b517bfa59 | |||
| e356527a88 | |||
| 07e34cc3cf | |||
| f7f1dd1346 | |||
| 92ccacdc77 | |||
| b5ed2a5351 | |||
| 55c5e4fe92 | |||
| 3b165809cc | |||
| 5c59e6e704 | |||
| ef2f237276 | |||
| 0af14c7a11 | |||
| 8ef777ebbe | |||
| 305d790a19 | |||
| 1f3c1091d3 | |||
| 663b43f4f1 | |||
| 3e0cf0f239 | |||
| 560f34e3df | |||
| 335232207d | |||
| 93e71f0554 | |||
| 0efbf7bc9e | |||
| 3daaa078fb | |||
| 647448a32b | |||
| f7a8e3db35 | |||
| 7747c320a8 | |||
| ebc69bc870 | |||
| 10e2b7ecbf | |||
| 842a698f2b | |||
| f5f519aa80 | |||
| 7db29a2332 | |||
| 6e70ae9958 | |||
| e190fc9416 | |||
| 1a4a0abd1a | |||
| dddacc25a5 | |||
| 2ba2da467c | |||
| f2b64fd111 | |||
| 6623f69476 | |||
| db36574667 | |||
| 3131104595 | |||
| f64bdc4bd4 | |||
| fe6e532c87 | |||
| 5cdeeb47f3 | |||
| bc1bb7f90f | |||
| 0f0141b9dd | |||
| 302c08a14b | |||
| b643f13a6e | |||
| 487d05a73e | |||
| 91ed077eba | |||
| 49433c2074 | |||
| a93ff6ef3c | |||
| 44e10c4f69 | |||
| 7a832c4522 | |||
| 7d0faba424 | |||
| 01d942628b | |||
| 2a9012c94d | |||
| b571926a6d | |||
| f784aa704d | |||
| 153affac12 | |||
| e5d365a3d9 | |||
| 6ffaabeb75 | |||
| 06debb153d | |||
| 51e6786f31 | |||
| 50ef19720d | |||
| db4d62d17c | |||
| 8d24bd720f | |||
| 55556f0e6c | |||
| 7d9cabebff | |||
| 9602c46809 | |||
| 85daab0d5e | |||
| 105faa1935 | |||
| b299b90964 | |||
| 346f1ac293 | |||
| 6e113eea89 | |||
| 54f906b347 | |||
| 4b252bc494 | |||
| b192165fb4 | |||
| f887f4efdd | |||
| b37e54eead | |||
| ea4f458c1d | |||
| 394c76d64f | |||
| d34fc21a50 | |||
| f5435aa2de | |||
| e88f954cbf | |||
| fe897f291b | |||
| fbb6239299 | |||
| ae1633497c | |||
| 00b0e64bd2 | |||
| 2747c87893 | |||
| 2285c79ded | |||
| 230e784b9e | |||
| 4b88168edb | |||
| f29fbdf088 | |||
| 233acff3c4 | |||
| 5e625c9a68 | |||
| da4408835d | |||
| b78b8f0c82 | |||
| 484e8f07a0 | |||
| 504cdadd60 | |||
| 45c10229d9 | |||
| 973b92b8fd | |||
| 6ba0fd25a2 | |||
| 1dde8ad60b | |||
| 45b9cbe2b3 | |||
| 14912229b1 | |||
| 2b64df05e6 | |||
| 01d8d956da | |||
| 4f0d862cbe | |||
| e2ccc6f98e | |||
| d24ada7ab7 | |||
| 54e2316b65 | |||
| 58316a97d5 | |||
| 771ef3e8d5 | |||
| 65ed18960e | |||
| 799f8da333 | |||
| 40bb8bd9dc | |||
| 426f0273cb | |||
| 03ec2d6fc7 | |||
| e4f14fdc61 | |||
| 418c766493 | |||
| 4a65747a4b | |||
| 6dc194ed1d | |||
| 1aa69e77e9 | |||
| 6ac16867b4 | |||
| bbc679a5db | |||
| 592fb2b913 | |||
| f68f3f5f86 | |||
| 5f23860880 | |||
| faf59a7cba | |||
| cc2cb1142b | |||
| eb8be88866 | |||
| e43e1bd7f5 | |||
| eb586b0ed7 | |||
| 75aa96a4a6 | |||
| 10257f485a | |||
| deee6a73f0 | |||
| 84ba0a94c0 | |||
| 5d17cef55f | |||
| 70f1a7e3ca | |||
| 60785eab8e | |||
| 7e9af6f406 | |||
| 6b272ff585 | |||
| fecd679f33 | |||
| 2ab0e3521c | |||
| 9afd6b2858 | |||
| 670c88a64c | |||
| 425ff7cb00 | |||
| 1140b3ae83 | |||
| 5e1d1a52ba | |||
| e394d14cda | |||
| 9f073b6f1b | |||
| b375b130ba | |||
| 3fb6351212 | |||
| 61be01efc1 | |||
| 23bda38be0 | |||
| e6a0dd10a9 | |||
| ef68aa39f8 | |||
| b4b492c417 | |||
| a98e381c8e | |||
| 18f87b97d5 | |||
| c211f1d3fe | |||
| deb84cfdac | |||
| 7ad1b45d11 | |||
| 47161150d7 | |||
| 8e6832407a | |||
| 9166432ccf | |||
| 0107e74986 | |||
| c605e06e07 | |||
| 15a8e58225 | |||
| 4393b6b05f | |||
| ed037698fd | |||
| e870c22f87 | |||
| fd6f03cf74 | |||
| 706f407ed5 | |||
| dd3abd0f16 | |||
| 3fda804217 | |||
| 1d1440ac64 | |||
| 119768001c | |||
| 3ee92a2b33 | |||
| f87b607a53 | |||
| b4ba83ec34 | |||
| d807c4bf87 | |||
| 423f89c908 | |||
| 8d07bc41c9 | |||
| 86ef115e21 | |||
| 1dd8aa9b6f | |||
| f520257463 | |||
| 00dc3088c8 | |||
| e52e4b1a8c | |||
| 0ef3a30b84 | |||
| b375897374 | |||
| 2b2ce8bf3b | |||
| 47c8c15878 | |||
| fa36435290 | |||
| 4b83123cb8 | |||
| 37b17c8c78 | |||
| 51e81f4867 | |||
| ae7b61b040 | |||
| 5a2a534240 | |||
| 09258ca5c1 | |||
| e0966d99a1 | |||
| 66a700729b | |||
| a7266442c2 | |||
| cf34b9c04e | |||
| 530597347c | |||
| c543611897 | |||
| d88596e13c | |||
| c59bbe7c22 | |||
| d4670faafd | |||
| e772fb7d0e | |||
| 29ba8f9f1a | |||
| 61af4d715c | |||
| 535091e36c | |||
| 5b7f8c1816 | |||
| 18216ab1a9 | |||
| 5500ed9209 | |||
| 886f874ff3 | |||
| 466cac328f | |||
| 06caac2619 | |||
| 2691c33b55 | |||
| 7f6844e48d | |||
| a5a929ff72 | |||
| 84b5311257 | |||
| 6a97f6a327 | |||
| dfd02817fa | |||
| 222d2363cd | |||
| 14c117ffd3 | |||
| d5df2ad3ce | |||
| c56b2734fd | |||
| 5eebe73f79 | |||
| 21d400e3c6 | |||
| d270188578 | |||
| fcf9034c2b | |||
| 956e6f95ec | |||
| 50913948a0 | |||
| a5c02556e6 | |||
| 319f40462f | |||
| e5374f60c9 | |||
| 75679c959a | |||
| 628356a294 | |||
| 0fba3b0e1e | |||
| 92a650c827 | |||
| 3bbde05e0e | |||
| 3324bab81e | |||
| 81133769de | |||
| a7d3779dfa | |||
| 75fee8c11f | |||
| 9e11b0a8c6 | |||
| 5861920453 | |||
| eb277cc578 | |||
| 20fd648729 | |||
| 11caba9807 | |||
| 35b4b31d57 | |||
| a5c74e324c | |||
| e1cbf06653 | |||
| 5a1959f0cf | |||
| 7f5074949e | |||
| cf10a8d321 | |||
| a05006b06a | |||
| eb0b96752a | |||
| 2f453eba35 | |||
| 8b2e1fdd9a | |||
| c99b32a8ec | |||
| 92f95e4d94 | |||
| d1011f8d37 | |||
| a486045f7c | |||
| 0e7dc93681 | |||
| a7bbf7a741 | |||
| bfa782f329 | |||
| bed37a148c | |||
| bf19186223 | |||
| 087e75d47f | |||
| 9e2600cac4 | |||
| 3c27c24dec | |||
| 1b81642db5 | |||
| d3a6de3f86 | |||
| f4925a0e6a | |||
| b6b3ca6e6f | |||
| 53572da7de | |||
| 8a0e0265bc | |||
| 0ada7b2da5 | |||
| 1dee1245ff | |||
| e9da6950ee | |||
| 12abd75aed | |||
| 044774ed72 | |||
| cf56b0733b | |||
| 2b0a63003f | |||
| deef98658b | |||
| d9544d006b | |||
| 6a2b2a0aed | |||
| 9060d0cc36 | |||
| b4f9f89480 | |||
| a9b6b284c0 | |||
| da62f50528 | |||
| e84e38cc19 | |||
| 9266a37276 | |||
| ff8ee68852 | |||
| 470b506952 | |||
| de8a447ea5 | |||
| b2f4520ba1 | |||
| a4b1239f45 | |||
| 43350f9066 | |||
| 0988023d30 | |||
| e81e20446f | |||
| 64080250ff | |||
| 4e19f82a2b | |||
| 97743e32c2 | |||
| b8d53fb7e9 | |||
| 390d94ec97 | |||
| e01092a0da | |||
| 312e9a436d | |||
| e43470da7a | |||
| d3e7a26db6 | |||
| 8c2a2a5c9d | |||
| bf2ac6625c | |||
| edb386eb1e | |||
| cc3dd648ac | |||
| 98b2fa6cb5 | |||
| b615c7d0a2 | |||
| 7e06a10f73 | |||
| d46db2f648 | |||
| 6d77aabb23 | |||
| a7b9ee6bcb | |||
| 0e5c221a3c | |||
| 25f7c27043 | |||
| ec3b78e52d | |||
| 4e903c84cf | |||
| ae3ec4e767 | |||
| fd22226242 | |||
| fe1e046ea6 | |||
| 2171aaa35c | |||
| 97024151e4 | |||
| 62dfea43c6 | |||
| a4f8408150 | |||
| 80f2b787ef | |||
| 404cfde33e | |||
| 145bc6da6b | |||
| 69b10e2d7c | |||
| 25e86e8e91 | |||
| 780417ba39 | |||
| 9f159bfb33 | |||
| c2e9cda2d1 | |||
| 1d6ecee64f | |||
| 3ee2826565 | |||
| 0ceea27b44 | |||
| 0dc5837b82 | |||
| b4cbb3380a | |||
| d9cb2baf4a | |||
| 8f456f68ff | |||
| 54a421ec58 | |||
| 6ce420afe3 | |||
| 8028770901 | |||
| d51b93f869 | |||
| ab2b62c980 | |||
| 97fa7cc9ac | |||
| 2648a9b011 | |||
| 1fdc25df75 | |||
| 4db24af59a | |||
| d85c12c385 | |||
| ccd8cbc2a9 | |||
| 1075cce7b3 | |||
| 89f438df71 | |||
| 97027a7ad9 | |||
| 3a8ef2f0d6 | |||
| 4c8e6dead1 | |||
| c19a7408d9 | |||
| c7b9d27b85 | |||
| 30510dc478 | |||
| b788ae52b0 | |||
| ccbd67eab6 | |||
| f21b1eefda | |||
| 6cd55a0a10 | |||
| 0c5c63f4ba | |||
| 3dcd1e793e | |||
| 419294cddd | |||
| b9448a476e | |||
| 238a747414 | |||
| c35433ec7a | |||
| e8c9da0124 | |||
| 18cf6e3210 | |||
| c9205a58ae | |||
| 23d693cd06 | |||
| 724ad0d893 | |||
| 7a0df8553f | |||
| 477215883f | |||
| 05114815ba | |||
| 9080d5fda0 | |||
| 108b43458f | |||
| 3dfd67680c | |||
| 3a2fa18744 | |||
| 370c9f553d | |||
| e80aa008b2 | |||
| 812d91cbf8 | |||
| 942c8bf8a7 | |||
| b6b3e2f1cc | |||
| ee5ff943f3 | |||
| b28f48c1bd | |||
| 49e2fa428f | |||
| 2fcf97e207 | |||
| f3ac3cd1e2 | |||
| dd74757319 | |||
| ab0b5d9890 | |||
| 23f576360c | |||
| d7bd9c7559 | |||
| 9fb91f70aa | |||
| a32a3bcda8 | |||
| 16dc73ff48 | |||
| 0a1321aaae | |||
| 189add1d5a | |||
| 755062fb76 | |||
| 7d577832cd | |||
| c4866afbe0 | |||
| ee60248d19 | |||
| d546813c05 | |||
| f02d9e22d7 | |||
| cc67f45892 | |||
| c6a110ec59 | |||
| 7408396fd4 | |||
| da081b81c4 | |||
| 500ad776ea | |||
| e4a2b69b37 | |||
| 8eaa9bb469 | |||
| b283dd3682 | |||
| 4a5dd73a3c | |||
| 950570b8c3 | |||
| 72beb577b6 | |||
| de65ee08c9 | |||
| c06f4eb7f7 | |||
| 0abe13e3ad | |||
| 2ac808ffa2 | |||
| 58078e2c1e | |||
| 01503f8a7b | |||
| aa99e1220b | |||
| 783375af56 | |||
| 1d9f21f6dc | |||
| 0900c281a3 | |||
| f8c4b16c30 | |||
| c567339718 | |||
| 5eceb593e9 | |||
| 82244f298c | |||
| 3ab20d4ccb | |||
| ccc2644e6b | |||
| 7e2155d937 | |||
| c696d4dbb2 | |||
| f5195ee4f2 | |||
| 9f25ad42c1 | |||
| 1d4dc7d7a9 | |||
| 65d42083e1 | |||
| 6992a67dd9 | |||
| 922f5fa081 | |||
| 199df253ca | |||
| c1f435cc18 | |||
| 858592832f | |||
| a97211b572 | |||
| f196a9fc67 | |||
| 7ea71d8bfa | |||
| 9b615a0add | |||
| 4540034039 | |||
| 29d58f3d84 | |||
| 5290c820b4 | |||
| 22ad15e1f1 | |||
| 80b8abb0ec | |||
| 529ff3197a | |||
| b036bc9e04 | |||
| 9989f33fc2 | |||
| ba4456d24a | |||
| 00718ca03f | |||
| 6b8475b435 | |||
| e51295c79e | |||
| b40aa8636b | |||
| b3645df56f | |||
| 711f0a29ec | |||
| e8e1286572 | |||
| 3d722b9ba0 | |||
| 4ac0a507e0 | |||
| d54908d946 | |||
| 6ba5128f72 | |||
| c96a6c2b1b | |||
| 0b0588cc37 | |||
| 742bb12498 | |||
| f00017c8c1 | |||
| d976d02936 | |||
| 45f0886eba | |||
| 057fef88ae | |||
| 8148626ba3 | |||
| 14ffb8bd9b | |||
| 2f8510db31 | |||
| 38a4d9a6c5 | |||
| 9f5bf5f930 | |||
| 04dad28228 | |||
| a96f817738 | |||
| 8a51a7ea2d | |||
| b3cb511c06 | |||
| 7e3d02d991 | |||
| 412cec73de | |||
| a72c0f7470 | |||
| 45a245d7de | |||
| 1e60481040 | |||
| cda4c738e8 | |||
| f7c176e8ca | |||
| 3e2bc847d8 | |||
| b5092a6212 | |||
| 81d401eab5 | |||
| f986f7dfad | |||
| bcbc1dea25 | |||
| c8326ab328 | |||
| 91c6bc58dc | |||
| c722579c18 | |||
| 2f3bd5b5d9 | |||
| fad80a4f92 | |||
| 1b0a55f44c | |||
| 14b359beb0 | |||
| f32876d4bd | |||
| 13f9dbe582 | |||
| 8dfa2560b1 | |||
| 0c8c3b16d2 | |||
| 3d2812bd35 | |||
| a0e5cbc47f | |||
| d5ddc6acee | |||
| 30750d8763 | |||
| 3e4659076a | |||
| 7cd6327969 | |||
| 77128d298e | |||
| 1e8a379a03 | |||
| b889823b21 | |||
| 9723c553b6 | |||
| 6bfb0371eb | |||
| 1348bf2476 | |||
| 36446f3a58 | |||
| 5a484f7493 | |||
| 244d60fb96 | |||
| 0ae9638aa5 | |||
| 55b893c784 | |||
| c9b6bc848f | |||
| f97310c757 | |||
| 87b3520d02 | |||
| 887de0523f | |||
| a7a1f802a8 | |||
| 163cc62a00 | |||
| 5f566ebf05 | |||
| 1622986467 | |||
| ce6db53e5f | |||
| 292ea0607d | |||
| 3bd7256dc8 | |||
| da2429a1d0 | |||
| 54b5f35125 | |||
| 23bfb03d2a | |||
| 7381b1a696 | |||
| 99bcb7a0f6 | |||
| 17773c5644 | |||
| e1e47c6e43 | |||
| 2bb3d31d5c | |||
| 2a7bf1234b | |||
| e56e462317 | |||
| 3245e992c3 | |||
| 1299fa2801 | |||
| 52c78194b3 | |||
| 58466a304b | |||
| 51c09e94b8 | |||
| 22ccaf7f4a | |||
| b237ebba44 | |||
| 6038bbd6ec | |||
| 84a67ef766 | |||
| e03df97919 | |||
| 7bedd9ef7a | |||
| 72cd670636 | |||
| 9b028e76d2 | |||
| 479b4c9531 | |||
| 28f7d2151e | |||
| cd1560e2b0 | |||
| 87b4c93af9 | |||
| 0393d4ba71 | |||
| 717a0592ba | |||
| a67aff220f | |||
| e47e9c5ac3 | |||
| 4cd9367520 | |||
| c4ac014a0c | |||
| 21c99ca1e8 | |||
| 2cd3e9bcdb | |||
| f56fbbb777 | |||
| b64156aa2b | |||
| 5cb4129bc7 | |||
| bed264ad77 | |||
| f8be0448b0 | |||
| 75fa891e0a | |||
| 21574f7e16 | |||
| 0903a7f1e0 | |||
| c8d385c998 | |||
| 4c35bfbc10 | |||
| 83e322c5f3 | |||
| 6020b964c7 | |||
| b2d5367830 | |||
| d307ee8c2f | |||
| 202f57552a | |||
| 5ba864b6da | |||
| 30e97d5223 | |||
| 69be02c0fd | |||
| eefda551de | |||
| ac7ca07221 | |||
| b043390ec2 | |||
| 4c94ba6d8a | |||
| 7b87519c15 | |||
| 14bfe0293a | |||
| 931d422443 | |||
| 895150eebe | |||
| a5ccae5779 | |||
| 1635132fd5 | |||
| 048aee66cf | |||
| aea2868ce1 | |||
| 723755788d | |||
| 032d0836a7 | |||
| 5736bbc3e4 | |||
| 1b773a8585 | |||
| 3f0214ed6b | |||
| ea83608ee4 | |||
| 8cae1aee80 | |||
| 3db061e670 | |||
| efaaf9b5f9 | |||
| c451bf370d | |||
| 212b187447 | |||
| ac9a6e891f | |||
| 1fc44d5788 | |||
| 913c70557d | |||
| e8b785677d | |||
| 5c17eb69cb | |||
| f6d1e0f299 | |||
| e42bdc2022 | |||
| cf44f499b8 | |||
| 39c984fda8 | |||
| 9c23bc53b7 | |||
| 96b6cd46e6 | |||
| bc6e3879d4 | |||
| 3dcfd0dc98 | |||
| 40b8934487 | |||
| 0d39ac6528 | |||
| 157e1394d6 | |||
| edf78b077e | |||
| 753e64c76c | |||
| 3a072a294d | |||
| 23ae3c69b5 | |||
| bef9c5e2d5 | |||
| 58add24830 | |||
| fdffbfd2c4 | |||
| 357f841e88 | |||
| 7d73ac4405 | |||
| c429a5be25 | |||
| fba58fb1f0 | |||
| dad7f1cf0f | |||
| 8bee7a2b7f | |||
| 632b4e6332 | |||
| f26798bd19 | |||
| 7f4dcefc80 | |||
| d702cb9b49 | |||
| 2081228055 | |||
| f85283c036 | |||
| 14d6ab384d | |||
| 62036745f3 | |||
| 264274685a | |||
| 1babf28491 | |||
| 5a15dc4ddf | |||
| 0f782b385b | |||
| 1534fcfafd | |||
| 1c7ed6e693 | |||
| 0c5c56cf88 | |||
| 53c5275aa4 | |||
| c93d7a1721 | |||
| 9c7dad1bdf | |||
| bfe09b933e | |||
| 823f5779e1 | |||
| 96a18ba1d5 | |||
| b8bb41107c | |||
| ccffb7e380 | |||
| 39088b2035 | |||
| 8db05b8c17 | |||
| ba069ded40 | |||
| 6b8209fdc9 | |||
| ccf62fc525 | |||
| 6bfab6427a | |||
| aab4f85383 | |||
| 99b9208896 | |||
| 7402e3743d | |||
| 7ddfacb58f | |||
| c7a80418d9 | |||
| e98104dac8 | |||
| 884f668855 | |||
| 2bbe9d1b1f | |||
| 91c3cffe41 | |||
| 60a55aa897 | |||
| 9325a78043 | |||
| cd0e80126c | |||
| 1778c0d7d8 | |||
| b07901f1a0 | |||
| a15a1abb21 | |||
| 7da2eea8f0 | |||
| 596a1af7d9 | |||
| 6b6a7757ce | |||
| 8a6266239c | |||
| d83af679bd | |||
| af005668da | |||
| d07e94056d | |||
| 4d7aab1df4 | |||
| c3cbfecbae | |||
| a9d9553fff | |||
| 5cdf774ea7 | |||
| b75d4beb1a | |||
| 35a0430c81 | |||
| 1b5caa4112 | |||
| cbbb04eb40 | |||
| 7396aaf2c2 | |||
| 555ccce4ee | |||
| b838d123f8 | |||
| 1d3926e187 | |||
| 194f8fc791 | |||
| c316766144 | |||
| c00b2f9254 | |||
| 35c38e0b5e | |||
| 87ef4eeb36 | |||
| 06f5f0b7c8 | |||
| 9f91ff4734 | |||
| f7936b4709 | |||
| 60965e3a6c | |||
| b5cb2d633c | |||
| 810734718b | |||
| ca48c2e51b | |||
| 86ed2ee72b | |||
| 2abeed069b | |||
| 569106acd3 | |||
| 507d2f8afb | |||
| 54769a823e | |||
| cf3287f8ec | |||
| c5fbc9f05c | |||
| 2940b0f390 | |||
| 442e41f825 | |||
| e95b718cf4 | |||
| 143c08bfcb | |||
| b3902c8950 | |||
| 7210796cb8 | |||
| 949cfe2ceb | |||
| c39f27d5f8 | |||
| c9ac4f06ff | |||
| 674b14faa3 | |||
| a8fdcd30a5 | |||
| db3cb23c1d | |||
| 7972f7a2da | |||
| 8c3d50be28 | |||
| 4198f3062c | |||
| c2a6efc77a | |||
| 6928487aa1 | |||
| e608317e70 | |||
| 64b5d7a519 | |||
| eef41e76d7 | |||
| d583f7f307 | |||
| f97eca34df | |||
| 1363e044ac | |||
| 9967d4eadd | |||
| 2972de1047 | |||
| 38db4115fc | |||
| 0e824914b2 | |||
| 7832a6d1d8 | |||
| 0cce93a1f8 | |||
| 679f2480c9 | |||
| db7ca2c7ca | |||
| e2850df07e | |||
| 48def2fecb | |||
| ba236ad108 | |||
| 9a28402b90 | |||
| 8c705cc4ea | |||
| 24aaa24b5c | |||
| fd795a7eeb | |||
| abdc254e55 | |||
| f843b7e2e3 | |||
| eea86ab891 | |||
| d7a452588c | |||
| 56d9418119 | |||
| 0109104027 | |||
| c9c965ef85 | |||
| af3adf5244 | |||
| 43f97b4da1 | |||
| 63f07db303 | |||
| 2e9ca89dd3 | |||
| 8586aeabe8 | |||
| c031b4d5d0 | |||
| d91b3464a4 | |||
| 650d94e1a6 | |||
| 543134a6d2 | |||
| ab2626f9df | |||
| d50f9249d6 | |||
| 10a9753183 | |||
| 1e8718fe87 | |||
| 678d0b87d0 | |||
| 319dfce300 | |||
| c3e86933d0 | |||
| 936acf8c10 | |||
| 358d402cd0 | |||
| d11593eb5c | |||
| 4bdedc7fde | |||
| 9be625d6e8 | |||
| 898b245bb2 | |||
| be37f10fed | |||
| a87f651b4d | |||
| 575195a283 | |||
| 5621ee4239 | |||
| f5c4e67aef | |||
| 2f7de9d696 | |||
| 9cee723e92 | |||
| 4a1c0fedca | |||
| b510958f9e | |||
| d6c8bb35b4 | |||
| 4899674b6c | |||
| 4eeed5376f | |||
| 77ad538e68 | |||
| b699aca65b | |||
| ffd19fe961 | |||
| 208912ceac | |||
| 89c253ce41 | |||
| b1d4508d90 | |||
| 8249dc8559 | |||
| 59fe4f8a36 | |||
| acb493665d | |||
| 73123bc313 | |||
| 947aa1342c | |||
| 20c8f9fe31 | |||
| 66ee3bc217 | |||
| 031a2a1b1e | |||
| 9c87b73ad5 | |||
| 97139a530f | |||
| b483b1e380 | |||
| d9126eeb2c | |||
| d8048e136f | |||
| fd64fea279 | |||
| 667fd24c84 | |||
| 9ffb38d6be | |||
| 13397feccf | |||
| 57b05fb6d4 | |||
| 41a2c25b5a | |||
| b214249290 | |||
| 8dad1c382b | |||
| 6713a47d38 | |||
| 02e0223fe8 | |||
| 6c4add67e1 | |||
| 2b7324c06e | |||
| c95bd6cadf | |||
| d73694d3e6 | |||
| 5d607f9484 | |||
| 816c38e9e4 | |||
| 09130e5dcd | |||
| b87447787a | |||
| 5a548f8366 | |||
| 023658605e | |||
| 031111014b | |||
| b5b9303e66 | |||
| 3deaa2fad3 | |||
| c3cd4c80b2 | |||
| be3f3c4bf0 | |||
| d1ca8b4470 | |||
| 72e3386c6a | |||
| 4010a6b745 | |||
| 0218957eb5 | |||
| cd65ab7dfa | |||
| c4adf541c1 | |||
| 88e3a22772 | |||
| 191803ad53 | |||
| 1b7be672fa | |||
| 9581325b9c | |||
| 59990acc2a | |||
| 0bf7716332 | |||
| 3339e5dc6a | |||
| 30b3f68ddf | |||
| 75552f2765 | |||
| 4a0bf9fd44 | |||
| fa28098a83 | |||
| 646409cbdf | |||
| 9e8fb89a52 | |||
| 14441e0e53 | |||
| 7f5814c4d8 | |||
| e3afc3587e | |||
| 8a52c44d15 | |||
| e6e18732b5 | |||
| 3f8ae8f1ce | |||
| a79129835c | |||
| 6fcaecdc9f | |||
| 8dc8090888 | |||
| 4db2f1ec7d | |||
| 06fa030260 | |||
| 018f9fc278 | |||
| c0f6d46cd8 | |||
| 8e43b1fe04 | |||
| 04df919a1c | |||
| f82e628f08 | |||
| 07a7c03adb | |||
| e800f8c0a7 | |||
| d02dbef9d2 | |||
| 55262bc6e9 | |||
| 51ed22afb9 | |||
| 0cf32e2946 | |||
| 693ab4d8bc | |||
| e4869b447c | |||
| 547f356131 | |||
| 017ab1b3fd | |||
| 07ef566de1 | |||
| 3c8a29a179 | |||
| f74032d244 | |||
| eb489ac886 | |||
| 986628b8b2 | |||
| da4f5c6f96 | |||
| f09ae196a8 | |||
| 3e702ae2bc | |||
| 490d1c6a8e | |||
| 0e748d98d5 | |||
| c41e893361 | |||
| 2fd8d6916b | |||
| b4eaf1ab5a | |||
| 3c8e9a7f24 | |||
| 67ec72284d | |||
| 754a122aa0 | |||
| 0486d633c6 | |||
| 09b063002b | |||
| 8828f4d0e5 | |||
| 9495cf71fd | |||
| 4d0f70439c | |||
| d3bb5d4fcb | |||
| 61315d4dfb | |||
| 2a123a4760 | |||
| 4689ffb72d | |||
| d2895c7985 | |||
| 6abb9d151e | |||
| 08827efae3 | |||
| 16c05d2c05 | |||
| 4d4d699780 | |||
| ca1ec4b221 | |||
| facb922f3e | |||
| d7987677ac | |||
| 5fbbfa482f | |||
| 7287b5cd3c | |||
| 0692cf304d | |||
| 88ebe7e4c1 | |||
| d647ed60e8 | |||
| 1cb29ece08 | |||
| de8b7fbc5f | |||
| 98d78f7ff9 | |||
| 3d1eb7dcad | |||
| e1c0b5ea24 | |||
| 30820c1bde | |||
| 46b9802902 | |||
| b6fb28de1a | |||
| 17fc4ba5e3 | |||
| 596514f7aa | |||
| 6e3a6f90e9 | |||
| fbd6439bd2 | |||
| f72c735f9e | |||
| 9b23fc5db8 | |||
| aaff188b3b | |||
| 2595d85cca | |||
| 2c96c29938 | |||
| 65919b7037 | |||
| f1bb7c33f1 | |||
| 8e7a4046d0 | |||
| f4e0c6a348 | |||
| 885a5e9f90 | |||
| 22e3e9a18b | |||
| 445cde2dd4 | |||
| 3dd58cf765 | |||
| cd87061749 | |||
| 1311e051d9 | |||
| c9363832fe | |||
| 6ab22e7cbb | |||
| bb13f7f234 | |||
| 13ba253aec | |||
| c58469e234 | |||
| 57a03bc2b5 | |||
| a7b4e0b5c5 | |||
| bbb45e326c | |||
| dfe3e402a8 | |||
| e884da50ea | |||
| 4c576631ad | |||
| 5f6f046d25 | |||
| 65e033c64b | |||
| 07346da740 | |||
| c95f182a90 | |||
| 289c74788e | |||
| 65a8d9278d | |||
| 887c830d56 | |||
| 6db4e08efb | |||
| 4f81bce800 | |||
| 932fcfa8bf | |||
| 314832e35a | |||
| 6a9dd185e6 | |||
| 2172827f43 | |||
| 90e9cd970a | |||
| 845ca95888 | |||
| 92d8d2f8a4 | |||
| 7cd52b2857 | |||
| 9fbd6c6c3f | |||
| 08fdc5fa98 | |||
| 7e362249ff | |||
| 9fa2db0227 | |||
| 4c75a72a90 | |||
| a0b2778161 | |||
| bc6d94cd25 | |||
| e70141e57b | |||
| 2e70b82eaa | |||
| 117ed52091 | |||
| e9d5e53dbe | |||
| e308694521 | |||
| 7e498012ff | |||
| f98bf32be8 | |||
| 0f2ed8c84d | |||
| 8d67cdfaab | |||
| 67ba924ed3 | |||
| 764cfb6a0d | |||
| 4e3c945093 | |||
| 8558bc1d41 | |||
| 03f56419d0 | |||
| 3ee9d24c3f | |||
| 9b1ad6c873 | |||
| 97daaade8a | |||
| 8a1d5a21c3 | |||
| 301a8ba3a4 | |||
| 2d4453968d | |||
| a2580a85a8 | |||
| 8a0e15ff37 | |||
| 8953a73fb1 | |||
| 1a2883ae9a | |||
| a2358ffc41 | |||
| 33eb2786f8 | |||
| c8ec4fd505 | |||
| fe98ab5398 | |||
| 76be7eb1e7 | |||
| 6e32812bc1 | |||
| 50a7be6385 | |||
| 3763c95784 | |||
| 5fa5252648 | |||
| 1e82561a22 | |||
| ba32a52bb6 | |||
| 6027fda0ce | |||
| e5cae87502 | |||
| ba0ec1b4ba | |||
| 2acd6f52f1 | |||
| b520228bc6 | |||
| 343a8241ab | |||
| 00525274a0 | |||
| 2915d9dbd9 | |||
| 0c49e5ba56 | |||
| b65bc45236 | |||
| 94b7e5a227 | |||
| 928572d384 | |||
| 2d1988bb6f | |||
| e007dc3614 | |||
| 2ed929eadc | |||
| f1924fa7ea | |||
| df822513fa | |||
| 762bdaeee1 | |||
| 8003278a55 | |||
| 5f22d30fc9 | |||
| d6dc855511 | |||
| 8f49f2c25e | |||
| f4304ca062 | |||
| 044662e4b7 | |||
| d9d8319c22 | |||
| 56b094767b | |||
| 98bc60cfb7 | |||
| 2aacca76de | |||
| aea615edb3 | |||
| f6b2dfd09f | |||
| 7f9bcfbc7d | |||
| 22cc5af1fe | |||
| 0dc3f1984e | |||
| e74976e645 | |||
| 91278608c5 | |||
| 8e2d765671 | |||
| 2af39e51f8 | |||
| 1504cf8812 | |||
| 3bece35bbd | |||
| ae41ad5d1e | |||
| ee61f7a55c | |||
| 241ddf76d4 | |||
| fe6e73ede9 | |||
| 8dab4c418c | |||
| 41e2c41ecc | |||
| 30108c7855 | |||
| 40ba9db406 | |||
| f1f2941511 | |||
| d886fb118b | |||
| 46d46c49a8 | |||
| 40dd1fb853 | |||
| ab77fc14d5 | |||
| e0d0edc0fc | |||
| 7eb124d99c | |||
| 88f236ccd3 | |||
| 34a0c998cd | |||
| e658df493a | |||
| b2a257b6b0 | |||
| cde577690a | |||
| c85d53cfbe | |||
| 26127cd2f2 | |||
| 42d9140afe | |||
| 52ad28df92 | |||
| 58ba5fd0dd | |||
| 71cc91cc4a | |||
| 6db3463d17 | |||
| 969818db6f | |||
| d4b191d27d | |||
| 6b23f2dfa3 | |||
| 17e6840f92 | |||
| a1f987c016 | |||
| cff805e0a3 | |||
| 25a941666a | |||
| 8c59849ea4 | |||
| f9e569099c | |||
| 6fd6e4eacf | |||
| ce07270973 | |||
| ac05863f72 | |||
| 7f6df7695c | |||
| ed58571079 | |||
| 80fbe8a788 | |||
| 0fdec056fa | |||
| 1915fe831f | |||
| 98c01a40e6 | |||
| 33234d605a | |||
| c6defe9a6d | |||
| 244daba501 | |||
| 43dadcd9e4 | |||
| 90db244b5f | |||
| e98f1205e5 | |||
| 2205f91e5f | |||
| da1dee351d | |||
| 694e9a5fec | |||
| 9ac5d8ad2d | |||
| 706b9f2357 | |||
| 55b3675dee | |||
| dc86563486 | |||
| ebccfa93c3 | |||
| e9f4576d4d | |||
| 880902fecc | |||
| 96cf5b7282 | |||
| f89c9f7ea4 | |||
| 27a4c871fd | |||
| 37190b2caf | |||
| c6b948323e | |||
| ad2957ed98 | |||
| c469bfcb9e | |||
| 3d16be1822 | |||
| ad10d4ee74 | |||
| 3ba3c1ca31 | |||
| 4ad28964de | |||
| 15ce7fb8f7 | |||
| 80a22b6484 | |||
| 8cfac7c6b8 | |||
| 9ac6b4c2c8 | |||
| 861a933b7b | |||
| 58e5404d61 | |||
| 3676fdc4db | |||
| e7a555a953 | |||
| 1e6ba29cd2 | |||
| c5c83882b3 | |||
| ea3bc0518c | |||
| 2d46a1001e | |||
| 4edd7fe647 | |||
| ad5dfab428 | |||
| 85838c1d57 | |||
| 4fbb3ccf66 | |||
| 7f533c6a25 | |||
| 6dab9d54d1 | |||
| 7ed5d12717 | |||
| 23b2c1f902 | |||
| 5d579dd1a8 | |||
| 0ce138341c | |||
| 61b682131e | |||
| a53e3dda75 | |||
| 2220c22c90 | |||
| e3a70e5021 | |||
| af4faaf666 | |||
| f2548570b8 | |||
| d678f1d88b | |||
| f0ce1bff3e | |||
| ab3c23478d | |||
| 4e65e9bfa9 | |||
| 053aeee5d3 | |||
| 1652dc02ab | |||
| 099c2f3593 | |||
| 0378422fcb | |||
| 0760415d08 | |||
| 5730ccaa39 | |||
| aaa7082aa8 | |||
| 5d7815beed | |||
| a0ee2ccce7 | |||
| 110be7f500 | |||
| 52663e8a44 | |||
| 8646647b3e | |||
| a04dac670e | |||
| 81dd71dd71 | |||
| 4d15391c88 | |||
| fc2473f826 | |||
| 000d848f0b | |||
| 3c5007352e | |||
| e0fa497948 | |||
| f082c73539 | |||
| d4f8bd665f | |||
| 58d0db6eab | |||
| 1544ddf0a5 | |||
| 488ec612bb | |||
| 484702a2e5 | |||
| 371058fb9e | |||
| 97b00670f5 | |||
| 704b386cc5 | |||
| aace50ebf4 | |||
| 49201e892b | |||
| 19b8deba66 | |||
| 068c0076af | |||
| 37c93a6ad8 | |||
| 1b1c76da34 | |||
| 5db1aec1d1 | |||
| 05ca5c88ef | |||
| 980167c99b | |||
| 76f8c1e4e2 | |||
| b273d616ad | |||
| c16b895460 | |||
| ec1a4884d9 | |||
| bb4ed8b1db | |||
| 4e54f93477 | |||
| 4ac1772149 | |||
| 3f235c51f6 | |||
| 7bdf0d5156 | |||
| d2a6fd58a3 | |||
| 05098862c5 | |||
| c8dd0246da | |||
| b3c389c0a0 | |||
| 15b9fc9099 | |||
| 1c14c13c2f | |||
| b79b92967f | |||
| f77832fea2 | |||
| 4ce8a17afd | |||
| 702c62c9a2 | |||
| 661059bf0f | |||
| 7fb4c4ce4f | |||
| 52834744fa | |||
| 3e9934e810 | |||
| 556e9b8fee | |||
| ef92c63455 | |||
| ae8858c516 | |||
| 0691486813 | |||
| 49db3bf916 | |||
| 99f28f25b0 | |||
| 27358c8ce8 | |||
| 899fbdfe08 | |||
| eea23a012a | |||
| 2235a53824 | |||
| 52f82f0b94 | |||
| a572b13743 | |||
| 4340a6f8fe | |||
| 4be97dc735 | |||
| 799126df0e | |||
| a65ec178df | |||
| cf4027df38 | |||
| 127b845960 | |||
| e3637b8452 | |||
| 745d36dc76 | |||
| 2355d6a4a0 | |||
| e1965fe7d9 | |||
| 781444d12c | |||
| b6571904ce | |||
| 97b25755e7 | |||
| bdfafb454b | |||
| 0fee20a415 | |||
| 186e0f3b04 | |||
| 2e6c5e590f | |||
| 9d4e715ee7 | |||
| 932e5ec9b8 | |||
| af5fb1d535 | |||
| 2953daebf5 | |||
| dd82b68175 | |||
| 05a081a7b8 | |||
| 9c417d33cc | |||
| 975f92fc84 | |||
| 0097beb84c | |||
| 65e16cc8bb | |||
| 2687155200 | |||
| 49321961e8 | |||
| 65f43637ce | |||
| f37cfc486d | |||
| a337a59fb8 | |||
| ec917e8e2f | |||
| 89d84131a4 | |||
| 2033f60d24 | |||
| d4159430f0 | |||
| 14255a38fc | |||
| 6c5f1b78d7 | |||
| 1f2d3a0132 | |||
| c23e4ed412 | |||
| 8c0118e7ae | |||
| 9d4107a515 | |||
| d07b9c2c11 | |||
| 3108a6a997 | |||
| b734cab6fc | |||
| 08b1bed38b | |||
| df6320eb0a | |||
| 80f1ec0d7e | |||
| 98f4f5a4f0 | |||
| 8feb089f5c | |||
| 2ba3a88ace | |||
| 1f10195c47 | |||
| dc40702b53 | |||
| a9e0e57797 | |||
| 9b47048231 | |||
| c05a3d3549 | |||
| 2083884ae3 | |||
| 2f644c7ed5 | |||
| d17c4a098c | |||
| 96303893bc | |||
| a329f9c84a | |||
| fc96e965ae | |||
| 365a62d63a | |||
| 3a87c3c3ba | |||
| 54dc26b145 | |||
| 78c1629aeb | |||
| bd518ed264 | |||
| 56379bffac | |||
| 11d7a0e394 | |||
| 3ad86a1c1b | |||
| 931f5875cd | |||
| f22b02a762 | |||
| 505f61ff43 | |||
| 0c2efa1253 | |||
| 5921e18be2 | |||
| 5b9e6fe440 | |||
| 35ba7d70dc | |||
| 026b4b8f4c | |||
| c924a50bed | |||
| 574792d2e1 | |||
| ab2eb5a603 | |||
| 3d7f925d52 | |||
| 93b584ff84 | |||
| 705849ca73 | |||
| 1a57f095e8 | |||
| ac9c2e8c4a | |||
| 45f8efec41 | |||
| ef7df2416d | |||
| cb113315f7 | |||
| d61f90a9c2 | |||
| 3d11fb219e | |||
| 4d9a368f96 | |||
| 43220b604c | |||
| 91f28fa87c | |||
| 82547db2db | |||
| 64ac3a4739 | |||
| c90616b963 | |||
| 0ceaa01c3d | |||
| 7d7e503603 | |||
| 7ee81eac0f | |||
| c9a08fc7b2 | |||
| 69ab93a824 | |||
| 179b660eee | |||
| a0ef7be9a5 | |||
| 2a735b14dc | |||
| a294dd8d9b | |||
| dc1522282b | |||
| 528a4c6a02 | |||
| 1770a8b62b | |||
| 77a0a6de95 | |||
| c1fd977e79 | |||
| cbbda93292 | |||
| c6340ce12d | |||
| 7bc739e965 | |||
| 9f988808c7 | |||
| 6e9ce47a4b | |||
| 89f59c0e1c | |||
| 3793919892 | |||
| d1699d50f6 | |||
| b94d950b1e | |||
| d3e77a0f63 | |||
| 965a71ee90 | |||
| fd794b44b2 | |||
| c9dcba17a9 | |||
| 88de4e15b3 | |||
| 7f0c6629b9 | |||
| f6ca39403d | |||
| ea7cf35ea4 | |||
| accea119d9 | |||
| 6ea7b43c90 | |||
| ddfad996a9 | |||
| d786f99094 | |||
| 008563450c | |||
| 69c238a2e6 | |||
| b3fdf170b1 | |||
| 56cc038603 | |||
| 4c83c689a8 | |||
| 2b7953c472 | |||
| 368ef1cea9 | |||
| bd696a6b37 | |||
| a9fc272e39 | |||
| c5952d1ddc | |||
| 656a406377 | |||
| 5fcd6cc510 | |||
| e09b6020a6 | |||
| 84d2d623b6 | |||
| 1694e5bd12 | |||
| 5170e251f9 | |||
| 4697e946d3 | |||
| 9286e3984c | |||
| 88f3d747b7 | |||
| d4427c54ea | |||
| 92ade80557 | |||
| 8138446b8c | |||
| 58ecc7fad4 | |||
| 311cc5c62f | |||
| 4a928ea9fa | |||
| c1eb444a39 | |||
| b5270e0bb3 | |||
| 6cfc31777b | |||
| 0e1e8503e0 | |||
| 974d0b4e91 | |||
| d66832a39c | |||
| d5889a941f | |||
| 6248e986e5 | |||
| 786b98a013 | |||
| f63d8dbf38 | |||
| 15aad09ce7 | |||
| fdd314f7f8 | |||
| 39b456d88c | |||
| d930fb8d3d | |||
| 3ec64eb0fd | |||
| d81c9819b8 | |||
| 98ce1d14c1 | |||
| 69f9ab88b9 | |||
| 1a0676f0eb | |||
| 658923175f | |||
| 877f4340ba | |||
| 35a9ccb41f | |||
| c7c2f9385f | |||
| d1f3dbb9a1 | |||
| cfb30fe975 | |||
| 2d1b378ef2 | |||
| 06bbee2449 | |||
| 85bc300dfa | |||
| 0662adacb9 | |||
| 65a153da6c | |||
| c5ee010640 | |||
| 1b18b4a0ee | |||
| 58765aab6b | |||
| 1483af1baa | |||
| 13979793f6 | |||
| 9c228e7625 | |||
| 6a667b36d1 | |||
| fe37f1968d | |||
| 4565bdd7ce | |||
| 5f3c07ca3b | |||
| 8d1da6a8ca | |||
| ff8e09495d | |||
| e868eef679 | |||
| ea90f781ff | |||
| c1b161b44b | |||
| aa84b2e328 | |||
| ebc1602816 | |||
| dcd6a55529 | |||
| fb258a7416 | |||
| 5aed927887 | |||
| d964bb9f2f | |||
| 87dc7a5b97 | |||
| ae63ca481d | |||
| 8e599103b6 | |||
| af0140e260 | |||
| 9d43907710 | |||
| a3fcd67a82 | |||
| fe3a43e2d4 | |||
| e095047b02 | |||
| 31a106344f | |||
| b0a82aa420 | |||
| c003d6032d | |||
| 1665595138 | |||
| bbccf76b45 | |||
| 1f47fa9243 | |||
| 7b24a12a66 | |||
| 921e7ac3e9 | |||
| 8d807add41 | |||
| 8501fce38b | |||
| 55d7984582 | |||
| 8dbac9c0bd | |||
| 47b8510b0c | |||
| 4e399b41cd | |||
| bdec1e037f | |||
| 9924cce466 | |||
| d3ecbd75af | |||
| 75b65ab2a2 | |||
| eca8825ab4 | |||
| 9f9b7cf885 | |||
| cd695d7f15 | |||
| 9c0ae597cd | |||
| 4461b4c56f | |||
| 93361118b7 | |||
| 50f963f6c6 | |||
| 67c3e47ed4 | |||
| f14cf8289e | |||
| 790aa68621 | |||
| 6d1d61026d | |||
| a4e3ca933b | |||
| 1fb58f9f8e | |||
| 86e0b9386d | |||
| c7c4cf221a | |||
| d6e439244a | |||
| 5ff225fa1b | |||
| 2a77e840cc | |||
| 00272883f6 | |||
| bd6f3b1273 | |||
| 4e9767bb22 | |||
| 1d54b7a266 | |||
| c9506d939a | |||
| 21217824b7 | |||
| 8024df2991 |
@@ -1 +1,7 @@
|
||||
build
|
||||
winbuild
|
||||
win64build
|
||||
include/SLES
|
||||
include/sndio.h
|
||||
include/sys
|
||||
openal-soft.kdev4
|
||||
|
||||
+351
-160
@@ -19,8 +19,12 @@
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __MINGW32__
|
||||
#define _WIN32_IE 0x501
|
||||
#else
|
||||
#define _WIN32_IE 0x400
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -28,219 +32,381 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef _WIN32_IE
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
typedef struct ConfigEntry {
|
||||
char *key;
|
||||
char *value;
|
||||
} ConfigEntry;
|
||||
|
||||
typedef struct ConfigBlock {
|
||||
char *name;
|
||||
ConfigEntry *entries;
|
||||
size_t entryCount;
|
||||
unsigned int entryCount;
|
||||
} ConfigBlock;
|
||||
static ConfigBlock cfgBlock;
|
||||
|
||||
static ConfigBlock *cfgBlocks;
|
||||
static size_t cfgCount;
|
||||
|
||||
static char buffer[1024];
|
||||
static char *lstrip(char *line)
|
||||
{
|
||||
while(isspace(line[0]))
|
||||
line++;
|
||||
return line;
|
||||
}
|
||||
|
||||
static char *rstrip(char *line)
|
||||
{
|
||||
size_t len = strlen(line);
|
||||
while(len > 0 && isspace(line[len-1]))
|
||||
len--;
|
||||
line[len] = 0;
|
||||
return line;
|
||||
}
|
||||
|
||||
static int readline(FILE *f, char **output, size_t *maxlen)
|
||||
{
|
||||
size_t len = 0;
|
||||
int c;
|
||||
|
||||
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
|
||||
;
|
||||
if(c == EOF)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
if(len+1 >= *maxlen)
|
||||
{
|
||||
void *temp = NULL;
|
||||
size_t newmax;
|
||||
|
||||
newmax = (*maxlen ? (*maxlen)<<1 : 32);
|
||||
if(newmax > *maxlen)
|
||||
temp = realloc(*output, newmax);
|
||||
if(!temp)
|
||||
{
|
||||
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*output = temp;
|
||||
*maxlen = newmax;
|
||||
}
|
||||
(*output)[len++] = c;
|
||||
(*output)[len] = '\0';
|
||||
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static char *expdup(const char *str)
|
||||
{
|
||||
char *output = NULL;
|
||||
size_t maxlen = 0;
|
||||
size_t len = 0;
|
||||
|
||||
while(*str != '\0')
|
||||
{
|
||||
const char *addstr;
|
||||
size_t addstrlen;
|
||||
size_t i;
|
||||
|
||||
if(str[0] != '$')
|
||||
{
|
||||
const char *next = strchr(str, '$');
|
||||
addstr = str;
|
||||
addstrlen = next ? (size_t)(next-str) : strlen(str);
|
||||
|
||||
str += addstrlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
str++;
|
||||
if(*str == '$')
|
||||
{
|
||||
const char *next = strchr(str+1, '$');
|
||||
addstr = str;
|
||||
addstrlen = next ? (size_t)(next-str) : strlen(str);
|
||||
|
||||
str += addstrlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
char envname[1024];
|
||||
size_t k = 0;
|
||||
|
||||
while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1)
|
||||
envname[k++] = *(str++);
|
||||
envname[k++] = '\0';
|
||||
|
||||
if((addstr=getenv(envname)) == NULL)
|
||||
continue;
|
||||
addstrlen = strlen(addstr);
|
||||
}
|
||||
}
|
||||
if(addstrlen == 0)
|
||||
continue;
|
||||
|
||||
if(addstrlen >= maxlen-len)
|
||||
{
|
||||
void *temp = NULL;
|
||||
size_t newmax;
|
||||
|
||||
newmax = len+addstrlen+1;
|
||||
if(newmax > maxlen)
|
||||
temp = realloc(output, newmax);
|
||||
if(!temp)
|
||||
{
|
||||
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen);
|
||||
return output;
|
||||
}
|
||||
|
||||
output = temp;
|
||||
maxlen = newmax;
|
||||
}
|
||||
|
||||
for(i = 0;i < addstrlen;i++)
|
||||
output[len++] = addstr[i];
|
||||
output[len] = '\0';
|
||||
}
|
||||
|
||||
return output ? output : calloc(1, 1);
|
||||
}
|
||||
|
||||
|
||||
static void LoadConfigFromFile(FILE *f)
|
||||
{
|
||||
ConfigBlock *curBlock = cfgBlocks;
|
||||
char curSection[128] = "";
|
||||
char *buffer = NULL;
|
||||
size_t maxlen = 0;
|
||||
ConfigEntry *ent;
|
||||
|
||||
while(fgets(buffer, sizeof(buffer), f))
|
||||
while(readline(f, &buffer, &maxlen))
|
||||
{
|
||||
size_t i = 0;
|
||||
char *line, *comment;
|
||||
char key[256] = "";
|
||||
char value[256] = "";
|
||||
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
if(!buffer[i] || buffer[i] == '#')
|
||||
comment = strchr(buffer, '#');
|
||||
if(comment) *(comment++) = 0;
|
||||
|
||||
line = rstrip(lstrip(buffer));
|
||||
if(!line[0])
|
||||
continue;
|
||||
|
||||
memmove(buffer, buffer+i, strlen(buffer+i)+1);
|
||||
|
||||
if(buffer[0] == '[')
|
||||
if(line[0] == '[')
|
||||
{
|
||||
ConfigBlock *nextBlock;
|
||||
char *section = line+1;
|
||||
char *endsection;
|
||||
|
||||
i = 1;
|
||||
while(buffer[i] && buffer[i] != ']')
|
||||
i++;
|
||||
|
||||
if(!buffer[i])
|
||||
endsection = strchr(section, ']');
|
||||
if(!endsection || section == endsection || endsection[1] != 0)
|
||||
{
|
||||
AL_PRINT("config parse error: bad line \"%s\"\n", buffer);
|
||||
ERR("config parse error: bad line \"%s\"\n", line);
|
||||
continue;
|
||||
}
|
||||
buffer[i] = 0;
|
||||
*endsection = 0;
|
||||
|
||||
do {
|
||||
i++;
|
||||
if(buffer[i] && !isspace(buffer[i]))
|
||||
{
|
||||
if(buffer[i] != '#')
|
||||
AL_PRINT("config warning: extra data after block: \"%s\"\n", buffer+i);
|
||||
break;
|
||||
}
|
||||
} while(buffer[i]);
|
||||
|
||||
nextBlock = NULL;
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
if(strcasecmp(section, "general") == 0)
|
||||
curSection[0] = 0;
|
||||
else
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].name, buffer+1) == 0)
|
||||
{
|
||||
nextBlock = cfgBlocks+i;
|
||||
// AL_PRINT("found block '%s'\n", nextBlock->name);
|
||||
break;
|
||||
}
|
||||
strncpy(curSection, section, sizeof(curSection)-1);
|
||||
curSection[sizeof(curSection)-1] = 0;
|
||||
}
|
||||
|
||||
if(!nextBlock)
|
||||
{
|
||||
nextBlock = realloc(cfgBlocks, (cfgCount+1)*sizeof(ConfigBlock));
|
||||
if(!nextBlock)
|
||||
{
|
||||
AL_PRINT("config parse error: error reallocating config blocks\n");
|
||||
continue;
|
||||
}
|
||||
cfgBlocks = nextBlock;
|
||||
nextBlock = cfgBlocks+cfgCount;
|
||||
cfgCount++;
|
||||
|
||||
nextBlock->name = strdup(buffer+1);
|
||||
nextBlock->entries = NULL;
|
||||
nextBlock->entryCount = 0;
|
||||
|
||||
// AL_PRINT("found new block '%s'\n", nextBlock->name);
|
||||
}
|
||||
curBlock = nextBlock;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Look for the option name */
|
||||
i = 0;
|
||||
while(buffer[i] && buffer[i] != '#' && buffer[i] != '=' &&
|
||||
!isspace(buffer[i]))
|
||||
i++;
|
||||
|
||||
if(!buffer[i] || buffer[i] == '#' || i == 0)
|
||||
if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
|
||||
sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
|
||||
sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
|
||||
{
|
||||
AL_PRINT("config parse error: malformed option line: \"%s\"\n", buffer);
|
||||
/* sscanf doesn't handle '' or "" as empty values, so clip it
|
||||
* manually. */
|
||||
if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0)
|
||||
value[0] = 0;
|
||||
}
|
||||
else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
|
||||
{
|
||||
/* Special case for 'key =' */
|
||||
value[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("config parse error: malformed option line: \"%s\"\n\n", line);
|
||||
continue;
|
||||
}
|
||||
rstrip(key);
|
||||
|
||||
/* Seperate the option */
|
||||
if(buffer[i] != '=')
|
||||
if(curSection[0] != 0)
|
||||
{
|
||||
buffer[i++] = 0;
|
||||
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
if(buffer[i] != '=')
|
||||
{
|
||||
AL_PRINT("config parse error: option without a value: \"%s\"\n", buffer);
|
||||
continue;
|
||||
}
|
||||
size_t len = strlen(curSection);
|
||||
memmove(&key[len+1], key, sizeof(key)-1-len);
|
||||
key[len] = '/';
|
||||
memcpy(key, curSection, len);
|
||||
}
|
||||
/* Find the start of the value */
|
||||
buffer[i++] = 0;
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
|
||||
/* Check if we already have this option set */
|
||||
ent = curBlock->entries;
|
||||
while((size_t)(ent-curBlock->entries) < curBlock->entryCount)
|
||||
ent = cfgBlock.entries;
|
||||
while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount)
|
||||
{
|
||||
if(strcasecmp(ent->key, buffer) == 0)
|
||||
if(strcasecmp(ent->key, key) == 0)
|
||||
break;
|
||||
ent++;
|
||||
}
|
||||
|
||||
if((size_t)(ent-curBlock->entries) >= curBlock->entryCount)
|
||||
if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount)
|
||||
{
|
||||
/* Allocate a new option entry */
|
||||
ent = realloc(curBlock->entries, (curBlock->entryCount+1)*sizeof(ConfigEntry));
|
||||
ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry));
|
||||
if(!ent)
|
||||
{
|
||||
AL_PRINT("config parse error: error reallocating config entries\n");
|
||||
ERR("config parse error: error reallocating config entries\n");
|
||||
continue;
|
||||
}
|
||||
curBlock->entries = ent;
|
||||
ent = curBlock->entries + curBlock->entryCount;
|
||||
curBlock->entryCount++;
|
||||
cfgBlock.entries = ent;
|
||||
ent = cfgBlock.entries + cfgBlock.entryCount;
|
||||
cfgBlock.entryCount++;
|
||||
|
||||
ent->key = strdup(buffer);
|
||||
ent->key = strdup(key);
|
||||
ent->value = NULL;
|
||||
}
|
||||
|
||||
/* Look for the end of the line (Null term, new-line, or #-symbol) and
|
||||
eat up the trailing whitespace */
|
||||
memmove(buffer, buffer+i, strlen(buffer+i)+1);
|
||||
|
||||
i = 0;
|
||||
while(buffer[i] && buffer[i] != '#' && buffer[i] != '\n')
|
||||
i++;
|
||||
do {
|
||||
i--;
|
||||
} while(isspace(buffer[i]));
|
||||
buffer[++i] = 0;
|
||||
|
||||
free(ent->value);
|
||||
ent->value = strdup(buffer);
|
||||
ent->value = expdup(value);
|
||||
|
||||
// AL_PRINT("found '%s' = '%s'\n", ent->key, ent->value);
|
||||
TRACE("found '%s' = '%s'\n", ent->key, ent->value);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
const WCHAR *str;
|
||||
FILE *f;
|
||||
|
||||
cfgBlocks = calloc(1, sizeof(ConfigBlock));
|
||||
cfgBlocks->name = strdup("general");
|
||||
cfgCount = 1;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(SHGetSpecialFolderPathA(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
{
|
||||
int p = strlen(buffer);
|
||||
snprintf(buffer+p, sizeof(buffer)-p, "\\alsoft.ini");
|
||||
f = fopen(buffer, "rt");
|
||||
size_t p = lstrlenW(buffer);
|
||||
_snwprintf(buffer+p, PATH_MAX-p, L"\\alsoft.ini");
|
||||
|
||||
TRACE("Loading config %ls...\n", buffer);
|
||||
f = _wfopen(buffer, L"rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
|
||||
{
|
||||
TRACE("Loading config %ls...\n", str);
|
||||
f = _wfopen(str, L"rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
f = fopen("/etc/openal/alsoft.conf", "r");
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
const char *str;
|
||||
FILE *f;
|
||||
|
||||
str = "/etc/openal/alsoft.conf";
|
||||
|
||||
TRACE("Loading config %s...\n", str);
|
||||
f = al_fopen(str, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
if(getenv("HOME") && *(getenv("HOME")))
|
||||
|
||||
if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
|
||||
str = "/etc/xdg";
|
||||
strncpy(buffer, str, sizeof(buffer)-1);
|
||||
buffer[sizeof(buffer)-1] = 0;
|
||||
/* Go through the list in reverse, since "the order of base directories
|
||||
* denotes their importance; the first directory listed is the most
|
||||
* important". Ergo, we need to load the settings from the later dirs
|
||||
* first so that the settings in the earlier dirs override them.
|
||||
*/
|
||||
while(1)
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", getenv("HOME"));
|
||||
f = fopen(buffer, "r");
|
||||
char *next = strrchr(buffer, ':');
|
||||
if(next) *(next++) = 0;
|
||||
else next = buffer;
|
||||
|
||||
if(next[0] != '/')
|
||||
WARN("Ignoring XDG config dir: %s\n", next);
|
||||
else
|
||||
{
|
||||
size_t len = strlen(next);
|
||||
strncpy(next+len, "/alsoft.conf", buffer+sizeof(buffer)-next-len);
|
||||
buffer[sizeof(buffer)-1] = 0;
|
||||
|
||||
TRACE("Loading config %s...\n", next);
|
||||
f = al_fopen(next, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
if(next == buffer)
|
||||
break;
|
||||
}
|
||||
|
||||
if((str=getenv("HOME")) != NULL && *str)
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str);
|
||||
|
||||
TRACE("Loading config %s...\n", buffer);
|
||||
f = al_fopen(buffer, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(getenv("ALSOFT_CONF"))
|
||||
|
||||
if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s", str, "alsoft.conf");
|
||||
else
|
||||
{
|
||||
f = fopen(getenv("ALSOFT_CONF"), "r");
|
||||
buffer[0] = 0;
|
||||
if((str=getenv("HOME")) != NULL && str[0] != 0)
|
||||
snprintf(buffer, sizeof(buffer), "%s/.config/%s", str, "alsoft.conf");
|
||||
}
|
||||
if(buffer[0] != 0)
|
||||
{
|
||||
TRACE("Loading config %s...\n", buffer);
|
||||
f = al_fopen(buffer, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
|
||||
{
|
||||
TRACE("Loading config %s...\n", str);
|
||||
f = al_fopen(str, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
@@ -248,73 +414,98 @@ void ReadALConfig(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void FreeALConfig(void)
|
||||
{
|
||||
size_t i;
|
||||
unsigned int i;
|
||||
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
for(i = 0;i < cfgBlock.entryCount;i++)
|
||||
{
|
||||
size_t j;
|
||||
for(j = 0;j < cfgBlocks[i].entryCount;j++)
|
||||
{
|
||||
free(cfgBlocks[i].entries[j].key);
|
||||
free(cfgBlocks[i].entries[j].value);
|
||||
}
|
||||
free(cfgBlocks[i].entries);
|
||||
free(cfgBlocks[i].name);
|
||||
free(cfgBlock.entries[i].key);
|
||||
free(cfgBlock.entries[i].value);
|
||||
}
|
||||
free(cfgBlocks);
|
||||
cfgBlocks = NULL;
|
||||
cfgCount = 0;
|
||||
free(cfgBlock.entries);
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
|
||||
{
|
||||
size_t i, j;
|
||||
unsigned int i;
|
||||
char key[256];
|
||||
|
||||
if(keyName)
|
||||
if(!keyName)
|
||||
return def;
|
||||
|
||||
if(blockName && strcasecmp(blockName, "general") != 0)
|
||||
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
|
||||
else
|
||||
{
|
||||
if(!blockName)
|
||||
blockName = "general";
|
||||
strncpy(key, keyName, sizeof(key)-1);
|
||||
key[sizeof(key)-1] = 0;
|
||||
}
|
||||
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
for(i = 0;i < cfgBlock.entryCount;i++)
|
||||
{
|
||||
if(strcasecmp(cfgBlock.entries[i].key, key) == 0)
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].name, blockName) != 0)
|
||||
continue;
|
||||
|
||||
for(j = 0;j < cfgBlocks[i].entryCount;j++)
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].entries[j].key, keyName) == 0)
|
||||
return cfgBlocks[i].entries[j].value;
|
||||
}
|
||||
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
|
||||
if(cfgBlock.entries[i].value[0])
|
||||
return cfgBlock.entries[i].value;
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("Key %s not found\n", key);
|
||||
return def;
|
||||
}
|
||||
|
||||
int GetConfigValueInt(const char *blockName, const char *keyName, int def)
|
||||
int ConfigValueExists(const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
|
||||
if(!val[0]) return def;
|
||||
return strtol(val, NULL, 0);
|
||||
return !!val[0];
|
||||
}
|
||||
|
||||
float GetConfigValueFloat(const char *blockName, const char *keyName, float def)
|
||||
int ConfigValueStr(const char *blockName, const char *keyName, const char **ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueInt(const char *blockName, const char *keyName, int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtol(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtoul(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
if(!val[0]) return def;
|
||||
#ifdef HAVE_STRTOF
|
||||
return strtof(val, NULL);
|
||||
*ret = strtof(val, NULL);
|
||||
#else
|
||||
return (float)strtod(val, NULL);
|
||||
*ret = (float)strtod(val, NULL);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetConfigValueBool(const char *blockName, const char *keyName, float def)
|
||||
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
|
||||
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
typedef struct ALechoState {
|
||||
// Must be first in all effects!
|
||||
ALeffectState state;
|
||||
|
||||
ALfloat *SampleBuffer;
|
||||
ALuint BufferLength;
|
||||
|
||||
// The echo is two tap. The third tap is the offset to write the feedback
|
||||
// and input sample to
|
||||
struct {
|
||||
ALuint offset;
|
||||
} Tap[3];
|
||||
// The LR gains for the first tap. The second tap uses the reverse
|
||||
ALfloat GainL;
|
||||
ALfloat GainR;
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
FILTER iirFilter;
|
||||
ALfloat history[2];
|
||||
} ALechoState;
|
||||
|
||||
// Find the next power of 2. Actually, this will return the input value if
|
||||
// it is already a power of 2.
|
||||
static ALuint NextPowerOf2(ALuint value)
|
||||
{
|
||||
ALuint powerOf2 = 1;
|
||||
|
||||
if(value)
|
||||
{
|
||||
value--;
|
||||
while(value)
|
||||
{
|
||||
value >>= 1;
|
||||
powerOf2 <<= 1;
|
||||
}
|
||||
}
|
||||
return powerOf2;
|
||||
}
|
||||
|
||||
ALvoid EchoDestroy(ALeffectState *effect)
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
if(state)
|
||||
{
|
||||
free(state->SampleBuffer);
|
||||
state->SampleBuffer = NULL;
|
||||
free(state);
|
||||
}
|
||||
}
|
||||
|
||||
ALvoid EchoUpdate(ALeffectState *effect, ALCcontext *Context, ALeffect *Effect)
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
ALuint newdelay1, newdelay2;
|
||||
ALfloat lrpan, cw, a, g;
|
||||
|
||||
newdelay1 = (ALuint)(Effect->Echo.Delay * Context->Frequency);
|
||||
newdelay2 = (ALuint)(Effect->Echo.LRDelay * Context->Frequency);
|
||||
|
||||
state->Tap[0].offset = (state->BufferLength - newdelay1 - 1 +
|
||||
state->Tap[2].offset)%state->BufferLength;
|
||||
state->Tap[1].offset = (state->BufferLength - newdelay1 - newdelay2 - 1 +
|
||||
state->Tap[2].offset)%state->BufferLength;
|
||||
|
||||
lrpan = Effect->Echo.Spread*0.5f + 0.5f;
|
||||
state->GainL = aluSqrt( lrpan);
|
||||
state->GainR = aluSqrt(1.0f-lrpan);
|
||||
|
||||
state->FeedGain = Effect->Echo.Feedback;
|
||||
|
||||
cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / Context->Frequency);
|
||||
g = 1.0f - Effect->Echo.Damping;
|
||||
a = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
a = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
state->iirFilter.coeff = a;
|
||||
}
|
||||
|
||||
ALvoid EchoProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
const ALuint delay = state->BufferLength-1;
|
||||
ALuint tap1off = state->Tap[0].offset;
|
||||
ALuint tap2off = state->Tap[1].offset;
|
||||
ALuint fboff = state->Tap[2].offset;
|
||||
ALfloat gain = Slot->Gain;
|
||||
ALfloat samp[2];
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
// Apply damping
|
||||
samp[0] = lpFilter2P(&state->iirFilter, 0, state->SampleBuffer[tap2off]+SamplesIn[i]);
|
||||
|
||||
// Apply feedback gain and mix in the new sample
|
||||
state->SampleBuffer[fboff] = samp[0] * state->FeedGain;
|
||||
|
||||
tap1off = (tap1off+1) & delay;
|
||||
tap2off = (tap2off+1) & delay;
|
||||
fboff = (fboff+1) & delay;
|
||||
|
||||
// Sample first tap
|
||||
samp[0] = state->SampleBuffer[tap1off]*state->GainL;
|
||||
samp[1] = state->SampleBuffer[tap1off]*state->GainR;
|
||||
// Sample second tap. Reverse LR panning
|
||||
samp[0] += state->SampleBuffer[tap2off]*state->GainR;
|
||||
samp[1] += state->SampleBuffer[tap2off]*state->GainL;
|
||||
// Apply slot gain
|
||||
samp[0] *= gain;
|
||||
samp[1] *= gain;
|
||||
|
||||
SamplesOut[i][FRONT_LEFT] += samp[0];
|
||||
SamplesOut[i][FRONT_RIGHT] += samp[1];
|
||||
SamplesOut[i][SIDE_LEFT] += samp[0];
|
||||
SamplesOut[i][SIDE_RIGHT] += samp[1];
|
||||
SamplesOut[i][BACK_LEFT] += samp[0];
|
||||
SamplesOut[i][BACK_RIGHT] += samp[1];
|
||||
}
|
||||
|
||||
state->Tap[0].offset = tap1off;
|
||||
state->Tap[1].offset = tap2off;
|
||||
state->Tap[2].offset = fboff;
|
||||
}
|
||||
|
||||
ALeffectState *EchoCreate(ALCcontext *Context)
|
||||
{
|
||||
ALechoState *state;
|
||||
ALuint i, maxlen;
|
||||
|
||||
state = malloc(sizeof(*state));
|
||||
if(!state)
|
||||
{
|
||||
alSetError(AL_OUT_OF_MEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state->state.Destroy = EchoDestroy;
|
||||
state->state.Update = EchoUpdate;
|
||||
state->state.Process = EchoProcess;
|
||||
|
||||
maxlen = (ALuint)(AL_ECHO_MAX_DELAY * Context->Frequency);
|
||||
maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Context->Frequency);
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
state->BufferLength = NextPowerOf2(maxlen+1);
|
||||
state->SampleBuffer = malloc(state->BufferLength * sizeof(ALfloat));
|
||||
if(!state->SampleBuffer)
|
||||
{
|
||||
free(state);
|
||||
alSetError(AL_OUT_OF_MEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(i = 0;i < state->BufferLength;i++)
|
||||
state->SampleBuffer[i] = 0.0f;
|
||||
|
||||
state->Tap[0].offset = 0;
|
||||
state->Tap[1].offset = 0;
|
||||
state->Tap[2].offset = 0;
|
||||
state->GainL = 0.0f;
|
||||
state->GainR = 0.0f;
|
||||
|
||||
for(i = 0;i < 2;i++)
|
||||
state->iirFilter.history[i] = 0.0f;
|
||||
state->iirFilter.coeff = 0.0f;
|
||||
|
||||
return &state->state;
|
||||
}
|
||||
-795
@@ -1,795 +0,0 @@
|
||||
/**
|
||||
* Reverb for the OpenAL cross platform audio library
|
||||
* Copyright (C) 2008-2009 by Christopher Fitzgerald.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alEffect.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
typedef struct DelayLine
|
||||
{
|
||||
// The delay lines use sample lengths that are powers of 2 to allow
|
||||
// bitmasking instead of modulus wrapping.
|
||||
ALuint Mask;
|
||||
ALfloat *Line;
|
||||
} DelayLine;
|
||||
|
||||
typedef struct ALverbState {
|
||||
// Must be first in all effects!
|
||||
ALeffectState state;
|
||||
|
||||
// All delay lines are allocated as a single buffer to reduce memory
|
||||
// fragmentation and management code.
|
||||
ALfloat *SampleBuffer;
|
||||
// Master effect low-pass filter (2 chained 1-pole filters).
|
||||
FILTER LpFilter;
|
||||
ALfloat LpHistory[2];
|
||||
// Initial effect delay and decorrelation.
|
||||
DelayLine Delay;
|
||||
// The tap points for the initial delay. First tap goes to early
|
||||
// reflections, the last four decorrelate to late reverb.
|
||||
ALuint Tap[5];
|
||||
struct {
|
||||
// Total gain for early reflections.
|
||||
ALfloat Gain;
|
||||
// Early reflections are done with 4 delay lines.
|
||||
ALfloat Coeff[4];
|
||||
DelayLine Delay[4];
|
||||
ALuint Offset[4];
|
||||
// The gain for each output channel based on 3D panning.
|
||||
ALfloat PanGain[OUTPUTCHANNELS];
|
||||
} Early;
|
||||
struct {
|
||||
// Total gain for late reverb.
|
||||
ALfloat Gain;
|
||||
// Attenuation to compensate for modal density and decay rate.
|
||||
ALfloat DensityGain;
|
||||
// The feed-back and feed-forward all-pass coefficient.
|
||||
ALfloat ApFeedCoeff;
|
||||
// Mixing matrix coefficient.
|
||||
ALfloat MixCoeff;
|
||||
// Late reverb has 4 parallel all-pass filters.
|
||||
ALfloat ApCoeff[4];
|
||||
DelayLine ApDelay[4];
|
||||
ALuint ApOffset[4];
|
||||
// In addition to 4 cyclical delay lines.
|
||||
ALfloat Coeff[4];
|
||||
DelayLine Delay[4];
|
||||
ALuint Offset[4];
|
||||
// The cyclical delay lines are 1-pole low-pass filtered.
|
||||
ALfloat LpCoeff[4];
|
||||
ALfloat LpSample[4];
|
||||
// The gain for each output channel based on 3D panning.
|
||||
ALfloat PanGain[OUTPUTCHANNELS];
|
||||
} Late;
|
||||
// The current read offset for all delay lines.
|
||||
ALuint Offset;
|
||||
} ALverbState;
|
||||
|
||||
// All delay line lengths are specified in seconds.
|
||||
|
||||
// The lengths of the early delay lines.
|
||||
static const ALfloat EARLY_LINE_LENGTH[4] =
|
||||
{
|
||||
0.0015f, 0.0045f, 0.0135f, 0.0405f
|
||||
};
|
||||
|
||||
// The lengths of the late all-pass delay lines.
|
||||
static const ALfloat ALLPASS_LINE_LENGTH[4] =
|
||||
{
|
||||
0.0151f, 0.0167f, 0.0183f, 0.0200f,
|
||||
};
|
||||
|
||||
// The lengths of the late cyclical delay lines.
|
||||
static const ALfloat LATE_LINE_LENGTH[4] =
|
||||
{
|
||||
0.0211f, 0.0311f, 0.0461f, 0.0680f
|
||||
};
|
||||
|
||||
// The late cyclical delay lines have a variable length dependent on the
|
||||
// effect's density parameter (inverted for some reason) and this multiplier.
|
||||
static const ALfloat LATE_LINE_MULTIPLIER = 4.0f;
|
||||
|
||||
// Input into the late reverb is decorrelated between four channels. Their
|
||||
// timings are dependent on a fraction and multiplier. See VerbUpdate() for
|
||||
// the calculations involved.
|
||||
static const ALfloat DECO_FRACTION = 1.0f / 32.0f;
|
||||
static const ALfloat DECO_MULTIPLIER = 2.0f;
|
||||
|
||||
// The maximum length of initial delay for the master delay line (a sum of
|
||||
// the maximum early reflection and late reverb delays).
|
||||
static const ALfloat MASTER_LINE_LENGTH = 0.3f + 0.1f;
|
||||
|
||||
// Find the next power of 2. Actually, this will return the input value if
|
||||
// it is already a power of 2.
|
||||
static ALuint NextPowerOf2(ALuint value)
|
||||
{
|
||||
ALuint powerOf2 = 1;
|
||||
|
||||
if(value)
|
||||
{
|
||||
value--;
|
||||
while(value)
|
||||
{
|
||||
value >>= 1;
|
||||
powerOf2 <<= 1;
|
||||
}
|
||||
}
|
||||
return powerOf2;
|
||||
}
|
||||
|
||||
// Basic delay line input/output routines.
|
||||
static __inline ALfloat DelayLineOut(DelayLine *Delay, ALuint offset)
|
||||
{
|
||||
return Delay->Line[offset&Delay->Mask];
|
||||
}
|
||||
|
||||
static __inline ALvoid DelayLineIn(DelayLine *Delay, ALuint offset, ALfloat in)
|
||||
{
|
||||
Delay->Line[offset&Delay->Mask] = in;
|
||||
}
|
||||
|
||||
// Delay line output routine for early reflections.
|
||||
static __inline ALfloat EarlyDelayLineOut(ALverbState *State, ALuint index)
|
||||
{
|
||||
return State->Early.Coeff[index] *
|
||||
DelayLineOut(&State->Early.Delay[index],
|
||||
State->Offset - State->Early.Offset[index]);
|
||||
}
|
||||
|
||||
// Given an input sample, this function produces stereo output for early
|
||||
// reflections.
|
||||
static __inline ALvoid EarlyReflection(ALverbState *State, ALfloat in, ALfloat *out)
|
||||
{
|
||||
ALfloat d[4], v, f[4];
|
||||
|
||||
// Obtain the decayed results of each early delay line.
|
||||
d[0] = EarlyDelayLineOut(State, 0);
|
||||
d[1] = EarlyDelayLineOut(State, 1);
|
||||
d[2] = EarlyDelayLineOut(State, 2);
|
||||
d[3] = EarlyDelayLineOut(State, 3);
|
||||
|
||||
/* The following uses a lossless scattering junction from waveguide
|
||||
* theory. It actually amounts to a householder mixing matrix, which
|
||||
* will produce a maximally diffuse response, and means this can probably
|
||||
* be considered a simple feedback delay network (FDN).
|
||||
* N
|
||||
* ---
|
||||
* \
|
||||
* v = 2/N / d_i
|
||||
* ---
|
||||
* i=1
|
||||
*/
|
||||
v = (d[0] + d[1] + d[2] + d[3]) * 0.5f;
|
||||
// The junction is loaded with the input here.
|
||||
v += in;
|
||||
|
||||
// Calculate the feed values for the delay lines.
|
||||
f[0] = v - d[0];
|
||||
f[1] = v - d[1];
|
||||
f[2] = v - d[2];
|
||||
f[3] = v - d[3];
|
||||
|
||||
// Refeed the delay lines.
|
||||
DelayLineIn(&State->Early.Delay[0], State->Offset, f[0]);
|
||||
DelayLineIn(&State->Early.Delay[1], State->Offset, f[1]);
|
||||
DelayLineIn(&State->Early.Delay[2], State->Offset, f[2]);
|
||||
DelayLineIn(&State->Early.Delay[3], State->Offset, f[3]);
|
||||
|
||||
// Output the results of the junction for all four lines.
|
||||
out[0] = State->Early.Gain * f[0];
|
||||
out[1] = State->Early.Gain * f[1];
|
||||
out[2] = State->Early.Gain * f[2];
|
||||
out[3] = State->Early.Gain * f[3];
|
||||
}
|
||||
|
||||
// All-pass input/output routine for late reverb.
|
||||
static __inline ALfloat LateAllPassInOut(ALverbState *State, ALuint index, ALfloat in)
|
||||
{
|
||||
ALfloat out;
|
||||
|
||||
out = State->Late.ApCoeff[index] *
|
||||
DelayLineOut(&State->Late.ApDelay[index],
|
||||
State->Offset - State->Late.ApOffset[index]);
|
||||
out -= (State->Late.ApFeedCoeff * in);
|
||||
DelayLineIn(&State->Late.ApDelay[index], State->Offset,
|
||||
(State->Late.ApFeedCoeff * out) + in);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Delay line output routine for late reverb.
|
||||
static __inline ALfloat LateDelayLineOut(ALverbState *State, ALuint index)
|
||||
{
|
||||
return State->Late.Coeff[index] *
|
||||
DelayLineOut(&State->Late.Delay[index],
|
||||
State->Offset - State->Late.Offset[index]);
|
||||
}
|
||||
|
||||
// Low-pass filter input/output routine for late reverb.
|
||||
static __inline ALfloat LateLowPassInOut(ALverbState *State, ALuint index, ALfloat in)
|
||||
{
|
||||
State->Late.LpSample[index] = in +
|
||||
((State->Late.LpSample[index] - in) * State->Late.LpCoeff[index]);
|
||||
return State->Late.LpSample[index];
|
||||
}
|
||||
|
||||
// Given four decorrelated input samples, this function produces stereo
|
||||
// output for late reverb.
|
||||
static __inline ALvoid LateReverb(ALverbState *State, ALfloat *in, ALfloat *out)
|
||||
{
|
||||
ALfloat d[4], f[4];
|
||||
|
||||
// Obtain the decayed results of the cyclical delay lines, and add the
|
||||
// corresponding input channels attenuated by density. Then pass the
|
||||
// results through the low-pass filters.
|
||||
d[0] = LateLowPassInOut(State, 0, (State->Late.DensityGain * in[0]) +
|
||||
LateDelayLineOut(State, 0));
|
||||
d[1] = LateLowPassInOut(State, 1, (State->Late.DensityGain * in[1]) +
|
||||
LateDelayLineOut(State, 1));
|
||||
d[2] = LateLowPassInOut(State, 2, (State->Late.DensityGain * in[2]) +
|
||||
LateDelayLineOut(State, 2));
|
||||
d[3] = LateLowPassInOut(State, 3, (State->Late.DensityGain * in[3]) +
|
||||
LateDelayLineOut(State, 3));
|
||||
|
||||
// To help increase diffusion, run each line through an all-pass filter.
|
||||
// The order of the all-pass filters is selected so that the shortest
|
||||
// all-pass filter will feed the shortest delay line.
|
||||
d[0] = LateAllPassInOut(State, 1, d[0]);
|
||||
d[1] = LateAllPassInOut(State, 3, d[1]);
|
||||
d[2] = LateAllPassInOut(State, 0, d[2]);
|
||||
d[3] = LateAllPassInOut(State, 2, d[3]);
|
||||
|
||||
/* Late reverb is done with a modified feedback delay network (FDN)
|
||||
* topology. Four input lines are each fed through their own all-pass
|
||||
* filter and then into the mixing matrix. The four outputs of the
|
||||
* mixing matrix are then cycled back to the inputs. Each output feeds
|
||||
* a different input to form a circlular feed cycle.
|
||||
*
|
||||
* The mixing matrix used is a 4D skew-symmetric rotation matrix derived
|
||||
* using a single unitary rotational parameter:
|
||||
*
|
||||
* [ d, a, b, c ] 1 = a^2 + b^2 + c^2 + d^2
|
||||
* [ -a, d, c, -b ]
|
||||
* [ -b, -c, d, a ]
|
||||
* [ -c, b, -a, d ]
|
||||
*
|
||||
* The rotation is constructed from the effect's diffusion parameter,
|
||||
* yielding: 1 = x^2 + 3 y^2; where a, b, and c are the coefficient y
|
||||
* with differing signs, and d is the coefficient x. The matrix is thus:
|
||||
*
|
||||
* [ x, y, -y, y ] x = 1 - (0.5 diffusion^3)
|
||||
* [ -y, x, y, y ] y = sqrt((1 - x^2) / 3)
|
||||
* [ y, -y, x, y ]
|
||||
* [ -y, -y, -y, x ]
|
||||
*
|
||||
* To reduce the number of multiplies, the x coefficient is applied with
|
||||
* the cyclical delay line coefficients. Thus only the y coefficient is
|
||||
* applied when mixing, and is modified to be: y / x.
|
||||
*/
|
||||
f[0] = d[0] + (State->Late.MixCoeff * ( d[1] - d[2] + d[3]));
|
||||
f[1] = d[1] + (State->Late.MixCoeff * (-d[0] + d[2] + d[3]));
|
||||
f[2] = d[2] + (State->Late.MixCoeff * ( d[0] - d[1] + d[3]));
|
||||
f[3] = d[3] + (State->Late.MixCoeff * (-d[0] - d[1] - d[2]));
|
||||
|
||||
// Output the results of the matrix for all four cyclical delay lines,
|
||||
// attenuated by the late reverb gain (which is attenuated by the 'x'
|
||||
// mix coefficient).
|
||||
out[0] = State->Late.Gain * f[0];
|
||||
out[1] = State->Late.Gain * f[1];
|
||||
out[2] = State->Late.Gain * f[2];
|
||||
out[3] = State->Late.Gain * f[3];
|
||||
|
||||
// The delay lines are fed circularly in the order:
|
||||
// 0 -> 1 -> 3 -> 2 -> 0 ...
|
||||
DelayLineIn(&State->Late.Delay[0], State->Offset, f[2]);
|
||||
DelayLineIn(&State->Late.Delay[1], State->Offset, f[0]);
|
||||
DelayLineIn(&State->Late.Delay[2], State->Offset, f[3]);
|
||||
DelayLineIn(&State->Late.Delay[3], State->Offset, f[1]);
|
||||
}
|
||||
|
||||
// Process the reverb for a given input sample, resulting in separate four-
|
||||
// channel output for both early reflections and late reverb.
|
||||
static __inline ALvoid ReverbInOut(ALverbState *State, ALfloat in, ALfloat *early, ALfloat *late)
|
||||
{
|
||||
ALfloat taps[4];
|
||||
|
||||
// Low-pass filter the incoming sample.
|
||||
in = lpFilter2P(&State->LpFilter, 0, in);
|
||||
|
||||
// Feed the initial delay line.
|
||||
DelayLineIn(&State->Delay, State->Offset, in);
|
||||
|
||||
// Calculate the early reflection from the first delay tap.
|
||||
in = DelayLineOut(&State->Delay, State->Offset - State->Tap[0]);
|
||||
EarlyReflection(State, in, early);
|
||||
|
||||
// Calculate the late reverb from the last four delay taps.
|
||||
taps[0] = DelayLineOut(&State->Delay, State->Offset - State->Tap[1]);
|
||||
taps[1] = DelayLineOut(&State->Delay, State->Offset - State->Tap[2]);
|
||||
taps[2] = DelayLineOut(&State->Delay, State->Offset - State->Tap[3]);
|
||||
taps[3] = DelayLineOut(&State->Delay, State->Offset - State->Tap[4]);
|
||||
LateReverb(State, taps, late);
|
||||
|
||||
// Step all delays forward one sample.
|
||||
State->Offset++;
|
||||
}
|
||||
|
||||
// This destroys the reverb state. It should be called only when the effect
|
||||
// slot has a different (or no) effect loaded over the reverb effect.
|
||||
ALvoid VerbDestroy(ALeffectState *effect)
|
||||
{
|
||||
ALverbState *State = (ALverbState*)effect;
|
||||
if(State)
|
||||
{
|
||||
free(State->SampleBuffer);
|
||||
State->SampleBuffer = NULL;
|
||||
free(State);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Temp, remove later.
|
||||
static __inline ALint aluCart2LUTpos(ALfloat re, ALfloat im)
|
||||
{
|
||||
ALint pos = 0;
|
||||
ALfloat denom = aluFabs(re) + aluFabs(im);
|
||||
if(denom > 0.0f)
|
||||
pos = (ALint)(QUADRANT_NUM*aluFabs(im) / denom + 0.5);
|
||||
|
||||
if(re < 0.0)
|
||||
pos = 2 * QUADRANT_NUM - pos;
|
||||
if(im < 0.0)
|
||||
pos = LUT_NUM - pos;
|
||||
return pos%LUT_NUM;
|
||||
}
|
||||
|
||||
// This updates the reverb state. This is called any time the reverb effect
|
||||
// is loaded into a slot.
|
||||
ALvoid VerbUpdate(ALeffectState *effect, ALCcontext *Context, ALeffect *Effect)
|
||||
{
|
||||
ALverbState *State = (ALverbState*)effect;
|
||||
ALuint index;
|
||||
ALfloat length, mixCoeff, cw, g, coeff;
|
||||
ALfloat hfRatio = Effect->Reverb.DecayHFRatio;
|
||||
|
||||
// Calculate the master low-pass filter (from the master effect HF gain).
|
||||
cw = cos(2.0 * M_PI * Effect->Reverb.HFReference / Context->Frequency);
|
||||
g = __max(Effect->Reverb.GainHF, 0.0001f);
|
||||
State->LpFilter.coeff = 0.0f;
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
State->LpFilter.coeff = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
|
||||
// Calculate the initial delay taps.
|
||||
length = Effect->Reverb.ReflectionsDelay;
|
||||
State->Tap[0] = (ALuint)(length * Context->Frequency);
|
||||
|
||||
length += Effect->Reverb.LateReverbDelay;
|
||||
|
||||
/* The four inputs to the late reverb are decorrelated to smooth the
|
||||
* initial reverb and reduce harsh echos. The timings are calculated as
|
||||
* multiples of a fraction of the smallest cyclical delay time. This
|
||||
* result is then adjusted so that the first tap occurs immediately (all
|
||||
* taps are reduced by the shortest fraction).
|
||||
*
|
||||
* offset[index] = ((FRACTION MULTIPLIER^index) - 1) delay
|
||||
*/
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
length += LATE_LINE_LENGTH[0] *
|
||||
(1.0f + (Effect->Reverb.Density * LATE_LINE_MULTIPLIER)) *
|
||||
(DECO_FRACTION * (pow(DECO_MULTIPLIER, (ALfloat)index) - 1.0f));
|
||||
State->Tap[1 + index] = (ALuint)(length * Context->Frequency);
|
||||
}
|
||||
|
||||
// Calculate the early reflections gain (from the master effect gain, and
|
||||
// reflections gain parameters).
|
||||
State->Early.Gain = Effect->Reverb.Gain * Effect->Reverb.ReflectionsGain;
|
||||
|
||||
// Calculate the gain (coefficient) for each early delay line.
|
||||
for(index = 0;index < 4;index++)
|
||||
State->Early.Coeff[index] = pow(10.0f, EARLY_LINE_LENGTH[index] /
|
||||
Effect->Reverb.LateReverbDelay *
|
||||
-60.0f / 20.0f);
|
||||
|
||||
// Calculate the first mixing matrix coefficient (x).
|
||||
mixCoeff = 1.0f - (0.5f * pow(Effect->Reverb.Diffusion, 3.0f));
|
||||
|
||||
// Calculate the late reverb gain (from the master effect gain, and late
|
||||
// reverb gain parameters). Since the output is tapped prior to the
|
||||
// application of the delay line coefficients, this gain needs to be
|
||||
// attenuated by the 'x' mix coefficient from above.
|
||||
State->Late.Gain = Effect->Reverb.Gain * Effect->Reverb.LateReverbGain * mixCoeff;
|
||||
|
||||
/* To compensate for changes in modal density and decay time of the late
|
||||
* reverb signal, the input is attenuated based on the maximal energy of
|
||||
* the outgoing signal. This is calculated as the ratio between a
|
||||
* reference value and the current approximation of energy for the output
|
||||
* signal.
|
||||
*
|
||||
* Reverb output matches exponential decay of the form Sum(a^n), where a
|
||||
* is the attenuation coefficient, and n is the sample ranging from 0 to
|
||||
* infinity. The signal energy can thus be approximated using the area
|
||||
* under this curve, calculated as: 1 / (1 - a).
|
||||
*
|
||||
* The reference energy is calculated from a signal at the lowest (effect
|
||||
* at 1.0) density with a decay time of one second.
|
||||
*
|
||||
* The coefficient is calculated as the average length of the cyclical
|
||||
* delay lines. This produces a better result than calculating the gain
|
||||
* for each line individually (most likely a side effect of diffusion).
|
||||
*
|
||||
* The final result is the square root of the ratio bound to a maximum
|
||||
* value of 1 (no amplification).
|
||||
*/
|
||||
length = (LATE_LINE_LENGTH[0] + LATE_LINE_LENGTH[1] +
|
||||
LATE_LINE_LENGTH[2] + LATE_LINE_LENGTH[3]);
|
||||
g = length * (1.0f + LATE_LINE_MULTIPLIER) * 0.25f;
|
||||
g = pow(10.0f, g * -60.0f / 20.0f);
|
||||
g = 1.0f / (1.0f - (g * g));
|
||||
length *= 1.0f + (Effect->Reverb.Density * LATE_LINE_MULTIPLIER) * 0.25f;
|
||||
length = pow(10.0f, length / Effect->Reverb.DecayTime * -60.0f / 20.0f);
|
||||
length = 1.0f / (1.0f - (length * length));
|
||||
State->Late.DensityGain = __min(aluSqrt(g / length), 1.0f);
|
||||
|
||||
// Calculate the all-pass feed-back and feed-forward coefficient.
|
||||
State->Late.ApFeedCoeff = 0.6f * pow(Effect->Reverb.Diffusion, 3.0f);
|
||||
|
||||
// Calculate the mixing matrix coefficient (y / x).
|
||||
g = aluSqrt((1.0f - (mixCoeff * mixCoeff)) / 3.0f);
|
||||
State->Late.MixCoeff = g / mixCoeff;
|
||||
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
// Calculate the gain (coefficient) for each all-pass line.
|
||||
State->Late.ApCoeff[index] = pow(10.0f, ALLPASS_LINE_LENGTH[index] /
|
||||
Effect->Reverb.DecayTime *
|
||||
-60.0f / 20.0f);
|
||||
}
|
||||
|
||||
// If the HF limit parameter is flagged, calculate an appropriate limit
|
||||
// based on the air absorption parameter.
|
||||
if(Effect->Reverb.DecayHFLimit && Effect->Reverb.AirAbsorptionGainHF < 1.0f)
|
||||
{
|
||||
ALfloat limitRatio;
|
||||
|
||||
// For each of the cyclical delays, find the attenuation due to air
|
||||
// absorption in dB (converting delay time to meters using the speed
|
||||
// of sound). Then reversing the decay equation, solve for HF ratio.
|
||||
// The delay length is cancelled out of the equation, so it can be
|
||||
// calculated once for all lines.
|
||||
limitRatio = 1.0f / (log10(Effect->Reverb.AirAbsorptionGainHF) *
|
||||
SPEEDOFSOUNDMETRESPERSEC *
|
||||
Effect->Reverb.DecayTime / -60.0f * 20.0f);
|
||||
// Need to limit the result to a minimum of 0.1, just like the HF
|
||||
// ratio parameter.
|
||||
limitRatio = __max(limitRatio, 0.1f);
|
||||
|
||||
// Using the limit calculated above, apply the upper bound to the
|
||||
// HF ratio.
|
||||
hfRatio = __min(hfRatio, limitRatio);
|
||||
}
|
||||
|
||||
// Calculate the low-pass filter frequency.
|
||||
cw = cos(2.0f * M_PI * Effect->Reverb.HFReference / Context->Frequency);
|
||||
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
// Calculate the length (in seconds) of each cyclical delay line.
|
||||
length = LATE_LINE_LENGTH[index] * (1.0f + (Effect->Reverb.Density *
|
||||
LATE_LINE_MULTIPLIER));
|
||||
// Calculate the delay offset for the cyclical delay lines.
|
||||
State->Late.Offset[index] = (ALuint)(length * Context->Frequency);
|
||||
|
||||
// Calculate the gain (coefficient) for each cyclical line.
|
||||
State->Late.Coeff[index] = pow(10.0f, length / Effect->Reverb.DecayTime *
|
||||
-60.0f / 20.0f);
|
||||
|
||||
// Eventually this should boost the high frequencies when the ratio
|
||||
// exceeds 1.
|
||||
coeff = 0.0f;
|
||||
if (hfRatio < 1.0f)
|
||||
{
|
||||
// Calculate the decay equation for each low-pass filter.
|
||||
g = pow(10.0f, length / (Effect->Reverb.DecayTime * hfRatio) *
|
||||
-60.0f / 20.0f) / State->Late.Coeff[index];
|
||||
g = __max(g, 0.1f);
|
||||
g *= g;
|
||||
|
||||
// Calculate the gain (coefficient) for each low-pass filter.
|
||||
if(g < 0.9999f) // 1-epsilon
|
||||
coeff = (1 - g*cw - aluSqrt(2*g*(1-cw) - g*g*(1 - cw*cw))) / (1 - g);
|
||||
|
||||
// Very low decay times will produce minimal output, so apply an
|
||||
// upper bound to the coefficient.
|
||||
coeff = __min(coeff, 0.98f);
|
||||
}
|
||||
State->Late.LpCoeff[index] = coeff;
|
||||
|
||||
// Attenuate the cyclical line coefficients by the mixing coefficient
|
||||
// (x).
|
||||
State->Late.Coeff[index] *= mixCoeff;
|
||||
}
|
||||
|
||||
// Calculate the 3D-panning gains for the early reflections and late
|
||||
// reverb (for EAX mode).
|
||||
{
|
||||
ALfloat earlyPan[3] = { Effect->Reverb.ReflectionsPan[0], Effect->Reverb.ReflectionsPan[1], Effect->Reverb.ReflectionsPan[2] };
|
||||
ALfloat latePan[3] = { Effect->Reverb.LateReverbPan[0], Effect->Reverb.LateReverbPan[1], Effect->Reverb.LateReverbPan[2] };
|
||||
ALfloat *speakerGain, dirGain, ambientGain;
|
||||
ALfloat length;
|
||||
ALint pos;
|
||||
|
||||
length = earlyPan[0]*earlyPan[0] + earlyPan[1]*earlyPan[1] + earlyPan[2]*earlyPan[2];
|
||||
if(length > 1.0f)
|
||||
{
|
||||
length = 1.0f / aluSqrt(length);
|
||||
earlyPan[0] *= length;
|
||||
earlyPan[1] *= length;
|
||||
earlyPan[2] *= length;
|
||||
}
|
||||
length = latePan[0]*latePan[0] + latePan[1]*latePan[1] + latePan[2]*latePan[2];
|
||||
if(length > 1.0f)
|
||||
{
|
||||
length = 1.0f / aluSqrt(length);
|
||||
latePan[0] *= length;
|
||||
latePan[1] *= length;
|
||||
latePan[2] *= length;
|
||||
}
|
||||
|
||||
// This code applies directional reverb just like the mixer applies
|
||||
// directional sources. It diffuses the sound toward all speakers
|
||||
// as the magnitude of the panning vector drops, which is only an
|
||||
// approximation of the expansion of sound across the speakers from
|
||||
// the panning direction.
|
||||
pos = aluCart2LUTpos(earlyPan[2], earlyPan[0]);
|
||||
speakerGain = &Context->PanningLUT[OUTPUTCHANNELS * pos];
|
||||
dirGain = aluSqrt((earlyPan[0] * earlyPan[0]) + (earlyPan[2] * earlyPan[2]));
|
||||
ambientGain = (1.0 - dirGain);
|
||||
for(index = 0;index < OUTPUTCHANNELS;index++)
|
||||
State->Early.PanGain[index] = dirGain * speakerGain[index] + ambientGain;
|
||||
|
||||
pos = aluCart2LUTpos(latePan[2], latePan[0]);
|
||||
speakerGain = &Context->PanningLUT[OUTPUTCHANNELS * pos];
|
||||
dirGain = aluSqrt((latePan[0] * latePan[0]) + (latePan[2] * latePan[2]));
|
||||
ambientGain = (1.0 - dirGain);
|
||||
for(index = 0;index < OUTPUTCHANNELS;index++)
|
||||
State->Late.PanGain[index] = dirGain * speakerGain[index] + ambientGain;
|
||||
}
|
||||
}
|
||||
|
||||
// This processes the reverb state, given the input samples and an output
|
||||
// buffer.
|
||||
ALvoid VerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
|
||||
{
|
||||
ALverbState *State = (ALverbState*)effect;
|
||||
ALuint index;
|
||||
ALfloat early[4], late[4], out[4];
|
||||
ALfloat gain = Slot->Gain;
|
||||
|
||||
for(index = 0;index < SamplesToDo;index++)
|
||||
{
|
||||
// Process reverb for this sample.
|
||||
ReverbInOut(State, SamplesIn[index], early, late);
|
||||
|
||||
// Mix early reflections and late reverb.
|
||||
out[0] = (early[0] + late[0]) * gain;
|
||||
out[1] = (early[1] + late[1]) * gain;
|
||||
out[2] = (early[2] + late[2]) * gain;
|
||||
out[3] = (early[3] + late[3]) * gain;
|
||||
|
||||
// Output the results.
|
||||
SamplesOut[index][FRONT_LEFT] += out[0];
|
||||
SamplesOut[index][FRONT_RIGHT] += out[1];
|
||||
SamplesOut[index][FRONT_CENTER] += out[3];
|
||||
SamplesOut[index][SIDE_LEFT] += out[0];
|
||||
SamplesOut[index][SIDE_RIGHT] += out[1];
|
||||
SamplesOut[index][BACK_LEFT] += out[0];
|
||||
SamplesOut[index][BACK_RIGHT] += out[1];
|
||||
SamplesOut[index][BACK_CENTER] += out[2];
|
||||
}
|
||||
}
|
||||
|
||||
// This processes the EAX reverb state, given the input samples and an output
|
||||
// buffer.
|
||||
ALvoid EAXVerbProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS])
|
||||
{
|
||||
ALverbState *State = (ALverbState*)effect;
|
||||
ALuint index;
|
||||
ALfloat early[4], late[4];
|
||||
ALfloat gain = Slot->Gain;
|
||||
|
||||
for(index = 0;index < SamplesToDo;index++)
|
||||
{
|
||||
// Process reverb for this sample.
|
||||
ReverbInOut(State, SamplesIn[index], early, late);
|
||||
|
||||
// Unfortunately, while the number and configuration of gains for
|
||||
// panning adjust according to OUTPUTCHANNELS, the output from the
|
||||
// reverb engine is not so scalable.
|
||||
SamplesOut[index][FRONT_LEFT] +=
|
||||
(State->Early.PanGain[FRONT_LEFT]*early[0] +
|
||||
State->Late.PanGain[FRONT_LEFT]*late[0]) * gain;
|
||||
SamplesOut[index][FRONT_RIGHT] +=
|
||||
(State->Early.PanGain[FRONT_RIGHT]*early[1] +
|
||||
State->Late.PanGain[FRONT_RIGHT]*late[1]) * gain;
|
||||
SamplesOut[index][FRONT_CENTER] +=
|
||||
(State->Early.PanGain[FRONT_CENTER]*early[3] +
|
||||
State->Late.PanGain[FRONT_CENTER]*late[3]) * gain;
|
||||
SamplesOut[index][SIDE_LEFT] +=
|
||||
(State->Early.PanGain[SIDE_LEFT]*early[0] +
|
||||
State->Late.PanGain[SIDE_LEFT]*late[0]) * gain;
|
||||
SamplesOut[index][SIDE_RIGHT] +=
|
||||
(State->Early.PanGain[SIDE_RIGHT]*early[1] +
|
||||
State->Late.PanGain[SIDE_RIGHT]*late[1]) * gain;
|
||||
SamplesOut[index][BACK_LEFT] +=
|
||||
(State->Early.PanGain[BACK_LEFT]*early[0] +
|
||||
State->Late.PanGain[BACK_LEFT]*late[0]) * gain;
|
||||
SamplesOut[index][BACK_RIGHT] +=
|
||||
(State->Early.PanGain[BACK_RIGHT]*early[1] +
|
||||
State->Late.PanGain[BACK_RIGHT]*late[1]) * gain;
|
||||
SamplesOut[index][BACK_CENTER] +=
|
||||
(State->Early.PanGain[BACK_CENTER]*early[2] +
|
||||
State->Late.PanGain[BACK_CENTER]*late[2]) * gain;
|
||||
}
|
||||
}
|
||||
|
||||
// This creates the reverb state. It should be called only when the reverb
|
||||
// effect is loaded into a slot that doesn't already have a reverb effect.
|
||||
ALeffectState *VerbCreate(ALCcontext *Context)
|
||||
{
|
||||
ALverbState *State = NULL;
|
||||
ALuint samples, length[13], totalLength, index;
|
||||
|
||||
State = malloc(sizeof(ALverbState));
|
||||
if(!State)
|
||||
{
|
||||
alSetError(AL_OUT_OF_MEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
State->state.Destroy = VerbDestroy;
|
||||
State->state.Update = VerbUpdate;
|
||||
State->state.Process = VerbProcess;
|
||||
|
||||
// All line lengths are powers of 2, calculated from their lengths, with
|
||||
// an additional sample in case of rounding errors.
|
||||
|
||||
// See VerbUpdate() for an explanation of the additional calculation
|
||||
// added to the master line length.
|
||||
samples = (ALuint)
|
||||
((MASTER_LINE_LENGTH +
|
||||
(LATE_LINE_LENGTH[0] * (1.0f + LATE_LINE_MULTIPLIER) *
|
||||
(DECO_FRACTION * ((DECO_MULTIPLIER * DECO_MULTIPLIER *
|
||||
DECO_MULTIPLIER) - 1.0f)))) *
|
||||
Context->Frequency) + 1;
|
||||
length[0] = NextPowerOf2(samples);
|
||||
totalLength = length[0];
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
samples = (ALuint)(EARLY_LINE_LENGTH[index] * Context->Frequency) + 1;
|
||||
length[1 + index] = NextPowerOf2(samples);
|
||||
totalLength += length[1 + index];
|
||||
}
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
samples = (ALuint)(ALLPASS_LINE_LENGTH[index] * Context->Frequency) + 1;
|
||||
length[5 + index] = NextPowerOf2(samples);
|
||||
totalLength += length[5 + index];
|
||||
}
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
samples = (ALuint)(LATE_LINE_LENGTH[index] *
|
||||
(1.0f + LATE_LINE_MULTIPLIER) * Context->Frequency) + 1;
|
||||
length[9 + index] = NextPowerOf2(samples);
|
||||
totalLength += length[9 + index];
|
||||
}
|
||||
|
||||
// All lines share a single sample buffer and have their masks and start
|
||||
// addresses calculated once.
|
||||
State->SampleBuffer = malloc(totalLength * sizeof(ALfloat));
|
||||
if(!State->SampleBuffer)
|
||||
{
|
||||
free(State);
|
||||
alSetError(AL_OUT_OF_MEMORY);
|
||||
return NULL;
|
||||
}
|
||||
for(index = 0; index < totalLength;index++)
|
||||
State->SampleBuffer[index] = 0.0f;
|
||||
|
||||
State->LpFilter.coeff = 0.0f;
|
||||
State->LpFilter.history[0] = 0.0f;
|
||||
State->LpFilter.history[1] = 0.0f;
|
||||
State->Delay.Mask = length[0] - 1;
|
||||
State->Delay.Line = &State->SampleBuffer[0];
|
||||
totalLength = length[0];
|
||||
|
||||
State->Tap[0] = 0;
|
||||
State->Tap[1] = 0;
|
||||
State->Tap[2] = 0;
|
||||
State->Tap[3] = 0;
|
||||
State->Tap[4] = 0;
|
||||
|
||||
State->Early.Gain = 0.0f;
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
State->Early.Coeff[index] = 0.0f;
|
||||
State->Early.Delay[index].Mask = length[1 + index] - 1;
|
||||
State->Early.Delay[index].Line = &State->SampleBuffer[totalLength];
|
||||
totalLength += length[1 + index];
|
||||
|
||||
// The early delay lines have their read offsets calculated once.
|
||||
State->Early.Offset[index] = (ALuint)(EARLY_LINE_LENGTH[index] *
|
||||
Context->Frequency);
|
||||
}
|
||||
|
||||
State->Late.Gain = 0.0f;
|
||||
State->Late.DensityGain = 0.0f;
|
||||
State->Late.ApFeedCoeff = 0.0f;
|
||||
State->Late.MixCoeff = 0.0f;
|
||||
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
State->Late.ApCoeff[index] = 0.0f;
|
||||
State->Late.ApDelay[index].Mask = length[5 + index] - 1;
|
||||
State->Late.ApDelay[index].Line = &State->SampleBuffer[totalLength];
|
||||
totalLength += length[5 + index];
|
||||
|
||||
// The late all-pass lines have their read offsets calculated once.
|
||||
State->Late.ApOffset[index] = (ALuint)(ALLPASS_LINE_LENGTH[index] *
|
||||
Context->Frequency);
|
||||
}
|
||||
|
||||
for(index = 0;index < 4;index++)
|
||||
{
|
||||
State->Late.Coeff[index] = 0.0f;
|
||||
State->Late.Delay[index].Mask = length[9 + index] - 1;
|
||||
State->Late.Delay[index].Line = &State->SampleBuffer[totalLength];
|
||||
totalLength += length[9 + index];
|
||||
|
||||
State->Late.Offset[index] = 0;
|
||||
|
||||
State->Late.LpCoeff[index] = 0.0f;
|
||||
State->Late.LpSample[index] = 0.0f;
|
||||
}
|
||||
|
||||
// Panning is applied as an independent gain for each output channel.
|
||||
for(index = 0;index < OUTPUTCHANNELS;index++)
|
||||
{
|
||||
State->Early.PanGain[index] = 0.0f;
|
||||
State->Late.PanGain[index] = 0.0f;
|
||||
}
|
||||
|
||||
State->Offset = 0;
|
||||
return &State->state;
|
||||
}
|
||||
|
||||
ALeffectState *EAXVerbCreate(ALCcontext *Context)
|
||||
{
|
||||
ALeffectState *State = VerbCreate(Context);
|
||||
if(State) State->Process = EAXVerbProcess;
|
||||
return State;
|
||||
}
|
||||
+34
-30
@@ -24,6 +24,8 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
struct RingBuffer {
|
||||
@@ -34,26 +36,23 @@ struct RingBuffer {
|
||||
ALint read_pos;
|
||||
ALint write_pos;
|
||||
|
||||
CRITICAL_SECTION cs;
|
||||
almtx_t mtx;
|
||||
};
|
||||
|
||||
|
||||
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
|
||||
{
|
||||
RingBuffer *ring = calloc(1, sizeof(*ring));
|
||||
RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size));
|
||||
if(ring)
|
||||
{
|
||||
ring->mem = (ALubyte*)(ring+1);
|
||||
|
||||
ring->frame_size = frame_size;
|
||||
ring->length = length+1;
|
||||
ring->write_pos = 1;
|
||||
ring->mem = malloc((length+1)*frame_size);
|
||||
if(!ring->mem)
|
||||
{
|
||||
free(ring);
|
||||
ring = NULL;
|
||||
}
|
||||
ring->read_pos = 0;
|
||||
ring->write_pos = 0;
|
||||
|
||||
InitializeCriticalSection(&ring->cs);
|
||||
almtx_init(&ring->mtx, almtx_plain);
|
||||
}
|
||||
return ring;
|
||||
}
|
||||
@@ -62,8 +61,7 @@ void DestroyRingBuffer(RingBuffer *ring)
|
||||
{
|
||||
if(ring)
|
||||
{
|
||||
DeleteCriticalSection(&ring->cs);
|
||||
free(ring->mem);
|
||||
almtx_destroy(&ring->mtx);
|
||||
free(ring);
|
||||
}
|
||||
}
|
||||
@@ -72,9 +70,9 @@ ALsizei RingBufferSize(RingBuffer *ring)
|
||||
{
|
||||
ALsizei s;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
s = (ring->write_pos-ring->read_pos-1+ring->length) % ring->length;
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
almtx_lock(&ring->mtx);
|
||||
s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
|
||||
almtx_unlock(&ring->mtx);
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -83,31 +81,37 @@ void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
|
||||
{
|
||||
int remain;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
almtx_lock(&ring->mtx);
|
||||
|
||||
remain = ring->length - ring->write_pos;
|
||||
if((ring->read_pos-ring->write_pos+ring->length)%ring->length < len)
|
||||
ring->read_pos = (ring->write_pos+len) % ring->length;
|
||||
remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length;
|
||||
if(remain < len) len = remain;
|
||||
|
||||
if(remain < len)
|
||||
if(len > 0)
|
||||
{
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data, remain*ring->frame_size);
|
||||
memcpy(ring->mem, data+(remain*ring->frame_size), (len-remain)*ring->frame_size);
|
||||
remain = ring->length - ring->write_pos;
|
||||
if(remain < len)
|
||||
{
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
|
||||
remain*ring->frame_size);
|
||||
memcpy(ring->mem, data+(remain*ring->frame_size),
|
||||
(len-remain)*ring->frame_size);
|
||||
}
|
||||
else
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
|
||||
len*ring->frame_size);
|
||||
|
||||
ring->write_pos += len;
|
||||
ring->write_pos %= ring->length;
|
||||
}
|
||||
else
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data, len*ring->frame_size);
|
||||
|
||||
ring->write_pos += len;
|
||||
ring->write_pos %= ring->length;
|
||||
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
almtx_unlock(&ring->mtx);
|
||||
}
|
||||
|
||||
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
|
||||
{
|
||||
int remain;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
almtx_lock(&ring->mtx);
|
||||
|
||||
remain = ring->length - ring->read_pos;
|
||||
if(remain < len)
|
||||
@@ -121,5 +125,5 @@ void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
|
||||
ring->read_pos += len;
|
||||
ring->read_pos %= ring->length;
|
||||
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
almtx_unlock(&ring->mtx);
|
||||
}
|
||||
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alThunk.h"
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
typedef struct {
|
||||
ALuint (*func)(ALvoid*);
|
||||
ALvoid *ptr;
|
||||
HANDLE thread;
|
||||
} ThreadInfo;
|
||||
|
||||
static DWORD CALLBACK StarterFunc(void *ptr)
|
||||
{
|
||||
ThreadInfo *inf = (ThreadInfo*)ptr;
|
||||
ALint ret;
|
||||
|
||||
ret = inf->func(inf->ptr);
|
||||
ExitThread((DWORD)ret);
|
||||
|
||||
return (DWORD)ret;
|
||||
}
|
||||
|
||||
ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
|
||||
{
|
||||
DWORD dummy;
|
||||
ThreadInfo *inf = malloc(sizeof(ThreadInfo));
|
||||
if(!inf) return 0;
|
||||
|
||||
inf->func = func;
|
||||
inf->ptr = ptr;
|
||||
|
||||
inf->thread = CreateThread(NULL, 0, StarterFunc, inf, 0, &dummy);
|
||||
if(!inf->thread)
|
||||
{
|
||||
free(inf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return inf;
|
||||
}
|
||||
|
||||
ALuint StopThread(ALvoid *thread)
|
||||
{
|
||||
ThreadInfo *inf = thread;
|
||||
DWORD ret = 0;
|
||||
|
||||
WaitForSingleObject(inf->thread, INFINITE);
|
||||
GetExitCodeThread(inf->thread, &ret);
|
||||
CloseHandle(inf->thread);
|
||||
|
||||
free(inf);
|
||||
|
||||
return (ALuint)ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct {
|
||||
ALuint (*func)(ALvoid*);
|
||||
ALvoid *ptr;
|
||||
ALuint ret;
|
||||
pthread_t thread;
|
||||
} ThreadInfo;
|
||||
|
||||
static void *StarterFunc(void *ptr)
|
||||
{
|
||||
ThreadInfo *inf = (ThreadInfo*)ptr;
|
||||
inf->ret = inf->func(inf->ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
|
||||
{
|
||||
ThreadInfo *inf = malloc(sizeof(ThreadInfo));
|
||||
if(!inf) return NULL;
|
||||
|
||||
inf->func = func;
|
||||
inf->ptr = ptr;
|
||||
if(pthread_create(&inf->thread, NULL, StarterFunc, inf) != 0)
|
||||
{
|
||||
free(inf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return inf;
|
||||
}
|
||||
|
||||
ALuint StopThread(ALvoid *thread)
|
||||
{
|
||||
ThreadInfo *inf = thread;
|
||||
ALuint ret;
|
||||
|
||||
pthread_join(inf->thread, NULL);
|
||||
ret = inf->ret;
|
||||
|
||||
free(inf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
-1051
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,45 @@
|
||||
#ifndef ALSTRING_H
|
||||
#define ALSTRING_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
typedef char al_string_char_type;
|
||||
TYPEDEF_VECTOR(al_string_char_type, al_string)
|
||||
|
||||
inline void al_string_deinit(al_string *str)
|
||||
{ VECTOR_DEINIT(*str); }
|
||||
#define AL_STRING_INIT(_x) do { (_x) = (al_string)NULL; } while(0)
|
||||
#define AL_STRING_INIT_STATIC() ((al_string)NULL)
|
||||
#define AL_STRING_DEINIT(_x) al_string_deinit(&(_x))
|
||||
|
||||
inline ALsizei al_string_length(const_al_string str)
|
||||
{ return VECTOR_SIZE(str); }
|
||||
|
||||
inline ALboolean al_string_empty(const_al_string str)
|
||||
{ return al_string_length(str) == 0; }
|
||||
|
||||
inline const al_string_char_type *al_string_get_cstr(const_al_string str)
|
||||
{ return str ? &VECTOR_FRONT(str) : ""; }
|
||||
|
||||
void al_string_clear(al_string *str);
|
||||
|
||||
int al_string_cmp(const_al_string str1, const_al_string str2);
|
||||
int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
|
||||
|
||||
void al_string_copy(al_string *str, const_al_string from);
|
||||
void al_string_copy_cstr(al_string *str, const al_string_char_type *from);
|
||||
|
||||
void al_string_append_char(al_string *str, const al_string_char_type c);
|
||||
void al_string_append_cstr(al_string *str, const al_string_char_type *from);
|
||||
void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wchar.h>
|
||||
/* Windows-only methods to deal with WideChar strings. */
|
||||
void al_string_copy_wcstr(al_string *str, const wchar_t *from);
|
||||
#endif
|
||||
|
||||
#endif /* ALSTRING_H */
|
||||
+1387
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,232 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
/* Base ALCbackend method implementations. */
|
||||
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
|
||||
{
|
||||
int ret;
|
||||
self->mDevice = device;
|
||||
ret = almtx_init(&self->mMutex, almtx_recursive);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
void ALCbackend_Destruct(ALCbackend *self)
|
||||
{
|
||||
almtx_destroy(&self->mMutex);
|
||||
}
|
||||
|
||||
ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
|
||||
{
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
|
||||
ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ALint64 ALCbackend_getLatency(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ALCbackend_lock(ALCbackend *self)
|
||||
{
|
||||
int ret = almtx_lock(&self->mMutex);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
void ALCbackend_unlock(ALCbackend *self)
|
||||
{
|
||||
int ret = almtx_unlock(&self->mMutex);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
|
||||
/* Base ALCbackendFactory method implementations. */
|
||||
void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* Wrappers to use an old-style backend with the new interface. */
|
||||
typedef struct PlaybackWrapper {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
const BackendFuncs *Funcs;
|
||||
} PlaybackWrapper;
|
||||
|
||||
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, Destruct)
|
||||
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name);
|
||||
static void PlaybackWrapper_close(PlaybackWrapper *self);
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self);
|
||||
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self);
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self);
|
||||
static DECLARE_FORWARD2(PlaybackWrapper, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, ALCuint, availableSamples)
|
||||
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self);
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(PlaybackWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(PlaybackWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(PlaybackWrapper);
|
||||
|
||||
static void PlaybackWrapper_Construct(PlaybackWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(PlaybackWrapper, ALCbackend, self);
|
||||
|
||||
self->Funcs = funcs;
|
||||
}
|
||||
|
||||
static ALCenum PlaybackWrapper_open(PlaybackWrapper *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->OpenPlayback(device, name);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_close(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->ClosePlayback(device);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_reset(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->ResetPlayback(device);
|
||||
}
|
||||
|
||||
static ALCboolean PlaybackWrapper_start(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->StartPlayback(device);
|
||||
}
|
||||
|
||||
static void PlaybackWrapper_stop(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StopPlayback(device);
|
||||
}
|
||||
|
||||
static ALint64 PlaybackWrapper_getLatency(PlaybackWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->GetLatency(device);
|
||||
}
|
||||
|
||||
|
||||
typedef struct CaptureWrapper {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
const BackendFuncs *Funcs;
|
||||
} CaptureWrapper;
|
||||
|
||||
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, Destruct)
|
||||
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name);
|
||||
static void CaptureWrapper_close(CaptureWrapper *self);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self);
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self);
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples);
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self);
|
||||
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self);
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(CaptureWrapper, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(CaptureWrapper)
|
||||
DEFINE_ALCBACKEND_VTABLE(CaptureWrapper);
|
||||
|
||||
|
||||
static void CaptureWrapper_Construct(CaptureWrapper *self, ALCdevice *device, const BackendFuncs *funcs)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(CaptureWrapper, ALCbackend, self);
|
||||
|
||||
self->Funcs = funcs;
|
||||
}
|
||||
|
||||
static ALCenum CaptureWrapper_open(CaptureWrapper *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->OpenCapture(device, name);
|
||||
}
|
||||
|
||||
static void CaptureWrapper_close(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->CloseCapture(device);
|
||||
}
|
||||
|
||||
static ALCboolean CaptureWrapper_start(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StartCapture(device);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void CaptureWrapper_stop(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
self->Funcs->StopCapture(device);
|
||||
}
|
||||
|
||||
static ALCenum CaptureWrapper_captureSamples(CaptureWrapper *self, void *buffer, ALCuint samples)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->CaptureSamples(device, buffer, samples);
|
||||
}
|
||||
|
||||
static ALCuint CaptureWrapper_availableSamples(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->AvailableSamples(device);
|
||||
}
|
||||
|
||||
static ALint64 CaptureWrapper_getLatency(CaptureWrapper *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
return self->Funcs->GetLatency(device);
|
||||
}
|
||||
|
||||
|
||||
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
PlaybackWrapper *backend;
|
||||
|
||||
backend = PlaybackWrapper_New(sizeof(*backend));
|
||||
if(!backend) return NULL;
|
||||
|
||||
PlaybackWrapper_Construct(backend, device, funcs);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
CaptureWrapper *backend;
|
||||
|
||||
backend = CaptureWrapper_New(sizeof(*backend));
|
||||
if(!backend) return NULL;
|
||||
|
||||
CaptureWrapper_Construct(backend, device, funcs);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
#ifndef AL_BACKENDS_BASE_H
|
||||
#define AL_BACKENDS_BASE_H
|
||||
|
||||
#include "alMain.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
struct ALCbackendVtable;
|
||||
|
||||
typedef struct ALCbackend {
|
||||
const struct ALCbackendVtable *vtbl;
|
||||
|
||||
ALCdevice *mDevice;
|
||||
|
||||
almtx_t mMutex;
|
||||
} ALCbackend;
|
||||
|
||||
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device);
|
||||
void ALCbackend_Destruct(ALCbackend *self);
|
||||
ALCboolean ALCbackend_reset(ALCbackend *self);
|
||||
ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
|
||||
ALCuint ALCbackend_availableSamples(ALCbackend *self);
|
||||
ALint64 ALCbackend_getLatency(ALCbackend *self);
|
||||
void ALCbackend_lock(ALCbackend *self);
|
||||
void ALCbackend_unlock(ALCbackend *self);
|
||||
|
||||
struct ALCbackendVtable {
|
||||
void (*const Destruct)(ALCbackend*);
|
||||
|
||||
ALCenum (*const open)(ALCbackend*, const ALCchar*);
|
||||
void (*const close)(ALCbackend*);
|
||||
|
||||
ALCboolean (*const reset)(ALCbackend*);
|
||||
ALCboolean (*const start)(ALCbackend*);
|
||||
void (*const stop)(ALCbackend*);
|
||||
|
||||
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
|
||||
ALCuint (*const availableSamples)(ALCbackend*);
|
||||
|
||||
ALint64 (*const getLatency)(ALCbackend*);
|
||||
|
||||
void (*const lock)(ALCbackend*);
|
||||
void (*const unlock)(ALCbackend*);
|
||||
|
||||
void (*const Delete)(void*);
|
||||
};
|
||||
|
||||
#define DEFINE_ALCBACKEND_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
|
||||
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, close) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, stop) \
|
||||
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALint64, getLatency) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, lock) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, unlock) \
|
||||
static void T##_ALCbackend_Delete(void *ptr) \
|
||||
{ T##_Delete(STATIC_UPCAST(T, ALCbackend, (ALCbackend*)ptr)); } \
|
||||
\
|
||||
static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
|
||||
T##_ALCbackend_Destruct, \
|
||||
\
|
||||
T##_ALCbackend_open, \
|
||||
T##_ALCbackend_close, \
|
||||
T##_ALCbackend_reset, \
|
||||
T##_ALCbackend_start, \
|
||||
T##_ALCbackend_stop, \
|
||||
T##_ALCbackend_captureSamples, \
|
||||
T##_ALCbackend_availableSamples, \
|
||||
T##_ALCbackend_getLatency, \
|
||||
T##_ALCbackend_lock, \
|
||||
T##_ALCbackend_unlock, \
|
||||
\
|
||||
T##_ALCbackend_Delete, \
|
||||
}
|
||||
|
||||
|
||||
typedef enum ALCbackend_Type {
|
||||
ALCbackend_Playback,
|
||||
ALCbackend_Capture,
|
||||
ALCbackend_Loopback
|
||||
} ALCbackend_Type;
|
||||
|
||||
|
||||
struct ALCbackendFactoryVtable;
|
||||
|
||||
typedef struct ALCbackendFactory {
|
||||
const struct ALCbackendFactoryVtable *vtbl;
|
||||
} ALCbackendFactory;
|
||||
|
||||
void ALCbackendFactory_deinit(ALCbackendFactory *self);
|
||||
|
||||
struct ALCbackendFactoryVtable {
|
||||
ALCboolean (*const init)(ALCbackendFactory *self);
|
||||
void (*const deinit)(ALCbackendFactory *self);
|
||||
|
||||
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
|
||||
|
||||
void (*const probe)(ALCbackendFactory *self, enum DevProbe type);
|
||||
|
||||
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
};
|
||||
|
||||
#define DEFINE_ALCBACKENDFACTORY_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
|
||||
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
|
||||
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
|
||||
DECLARE_THUNK1(T, ALCbackendFactory, void, probe, enum DevProbe) \
|
||||
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
|
||||
\
|
||||
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
|
||||
T##_ALCbackendFactory_init, \
|
||||
T##_ALCbackendFactory_deinit, \
|
||||
T##_ALCbackendFactory_querySupport, \
|
||||
T##_ALCbackendFactory_probe, \
|
||||
T##_ALCbackendFactory_createBackend, \
|
||||
}
|
||||
|
||||
|
||||
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
|
||||
ALCbackend *create_backend_wrapper(ALCdevice *device, const BackendFuncs *funcs, ALCbackend_Type type);
|
||||
|
||||
#endif /* AL_BACKENDS_BASE_H */
|
||||
@@ -0,0 +1,707 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <alloca.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <unistd.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
AudioUnit audioUnit;
|
||||
|
||||
ALuint frameSize;
|
||||
ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
|
||||
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
|
||||
|
||||
AudioConverterRef audioConverter; // Sample rate converter if needed
|
||||
AudioBufferList *bufferList; // Buffer for data coming from the input device
|
||||
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
|
||||
|
||||
RingBuffer *ring;
|
||||
} ca_data;
|
||||
|
||||
static const ALCchar ca_device[] = "CoreAudio Default";
|
||||
|
||||
|
||||
static void destroy_buffer_list(AudioBufferList* list)
|
||||
{
|
||||
if(list)
|
||||
{
|
||||
UInt32 i;
|
||||
for(i = 0;i < list->mNumberBuffers;i++)
|
||||
free(list->mBuffers[i].mData);
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
|
||||
{
|
||||
AudioBufferList *list;
|
||||
|
||||
list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
|
||||
if(list)
|
||||
{
|
||||
list->mNumberBuffers = 1;
|
||||
|
||||
list->mBuffers[0].mNumberChannels = channelCount;
|
||||
list->mBuffers[0].mDataByteSize = byteSize;
|
||||
list->mBuffers[0].mData = malloc(byteSize);
|
||||
if(list->mBuffers[0].mData == NULL)
|
||||
{
|
||||
free(list);
|
||||
list = NULL;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inRefCon;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
aluMixData(device, ioData->mBuffers[0].mData,
|
||||
ioData->mBuffers[0].mDataByteSize / data->frameSize);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inUserData;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
// Read from the ring buffer and store temporarily in a large buffer
|
||||
ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
|
||||
|
||||
// Set the input data
|
||||
ioData->mNumberBuffers = 1;
|
||||
ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
|
||||
ioData->mBuffers[0].mData = data->resampleBuffer;
|
||||
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList *ioData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inRefCon;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
AudioUnitRenderActionFlags flags = 0;
|
||||
OSStatus err;
|
||||
|
||||
// fill the bufferList with data from the input device
|
||||
err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitRender error: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
ComponentDescription desc;
|
||||
Component comp;
|
||||
ca_data *data;
|
||||
OSStatus err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = ca_device;
|
||||
else if(strcmp(deviceName, ca_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
/* open the default output unit */
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
comp = FindNextComponent(NULL, &desc);
|
||||
if(comp == NULL)
|
||||
{
|
||||
ERR("FindNextComponent failed\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
|
||||
err = OpenAComponent(comp, &data->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("OpenAComponent failed\n");
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
/* init and start the default audio unit... */
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
CloseComponent(data->audioUnit);
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ca_close_playback(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
AudioUnitUninitialize(data->audioUnit);
|
||||
CloseComponent(data->audioUnit);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
AudioStreamBasicDescription streamFormat;
|
||||
AURenderCallbackStruct input;
|
||||
OSStatus err;
|
||||
UInt32 size;
|
||||
|
||||
err = AudioUnitUninitialize(data->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("-- AudioUnitUninitialize failed.\n");
|
||||
|
||||
/* retrieve default output unit's properties (output side) */
|
||||
size = sizeof(AudioStreamBasicDescription);
|
||||
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
|
||||
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
TRACE("Output streamFormat of default output unit -\n");
|
||||
TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket);
|
||||
TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame);
|
||||
TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel);
|
||||
TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket);
|
||||
TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame);
|
||||
TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
|
||||
#endif
|
||||
|
||||
/* set default output unit's input side to match output side */
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(device->Frequency != streamFormat.mSampleRate)
|
||||
{
|
||||
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
|
||||
streamFormat.mSampleRate /
|
||||
device->Frequency);
|
||||
device->Frequency = streamFormat.mSampleRate;
|
||||
}
|
||||
|
||||
/* FIXME: How to tell what channels are what in the output device, and how
|
||||
* to specify what we're giving? eg, 6.0 vs 5.1 */
|
||||
switch(streamFormat.mChannelsPerFrame)
|
||||
{
|
||||
case 1:
|
||||
device->FmtChans = DevFmtMono;
|
||||
break;
|
||||
case 2:
|
||||
device->FmtChans = DevFmtStereo;
|
||||
break;
|
||||
case 4:
|
||||
device->FmtChans = DevFmtQuad;
|
||||
break;
|
||||
case 6:
|
||||
device->FmtChans = DevFmtX51;
|
||||
break;
|
||||
case 7:
|
||||
device->FmtChans = DevFmtX61;
|
||||
break;
|
||||
case 8:
|
||||
device->FmtChans = DevFmtX71;
|
||||
break;
|
||||
default:
|
||||
ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
|
||||
device->FmtChans = DevFmtStereo;
|
||||
streamFormat.mChannelsPerFrame = 2;
|
||||
break;
|
||||
}
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
/* use channel count and sample rate from the default output unit's current
|
||||
* parameters, but reset everything else */
|
||||
streamFormat.mFramesPerPacket = 1;
|
||||
streamFormat.mFormatFlags = 0;
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtUByte:
|
||||
device->FmtType = DevFmtByte;
|
||||
/* fall-through */
|
||||
case DevFmtByte:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
device->FmtType = DevFmtShort;
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
device->FmtType = DevFmtInt;
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
||||
streamFormat.mBitsPerChannel = 32;
|
||||
break;
|
||||
}
|
||||
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
|
||||
streamFormat.mBitsPerChannel / 8;
|
||||
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
|
||||
streamFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
|
||||
kLinearPCMFormatFlagIsPacked;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
/* setup callback */
|
||||
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
input.inputProc = ca_callback;
|
||||
input.inputProcRefCon = device;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
/* init the default audio unit... */
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ca_start_playback(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err;
|
||||
|
||||
err = AudioOutputUnitStart(data->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioOutputUnitStart failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ca_stop_playback(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err;
|
||||
|
||||
err = AudioOutputUnitStop(data->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
}
|
||||
|
||||
static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
AudioStreamBasicDescription requestedFormat; // The application requested format
|
||||
AudioStreamBasicDescription hardwareFormat; // The hardware format
|
||||
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
|
||||
AURenderCallbackStruct input;
|
||||
ComponentDescription desc;
|
||||
AudioDeviceID inputDevice;
|
||||
UInt32 outputFrameCount;
|
||||
UInt32 propertySize;
|
||||
UInt32 enableIO;
|
||||
Component comp;
|
||||
ca_data *data;
|
||||
OSStatus err;
|
||||
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
// Search for component with given description
|
||||
comp = FindNextComponent(NULL, &desc);
|
||||
if(comp == NULL)
|
||||
{
|
||||
ERR("FindNextComponent failed\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
device->ExtraData = data;
|
||||
|
||||
// Open the component
|
||||
err = OpenAComponent(comp, &data->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("OpenAComponent failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Turn off AudioUnit output
|
||||
enableIO = 0;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Turn on AudioUnit input
|
||||
enableIO = 1;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Get the default input device
|
||||
propertySize = sizeof(AudioDeviceID);
|
||||
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioHardwareGetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(inputDevice == kAudioDeviceUnknown)
|
||||
{
|
||||
ERR("No input device found\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Track the input device
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// set capture callback
|
||||
input.inputProc = ca_capture_callback;
|
||||
input.inputProcRefCon = device;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Initialize the device
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Get the hardware format
|
||||
propertySize = sizeof(AudioStreamBasicDescription);
|
||||
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Set up the requested format description
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtUByte:
|
||||
requestedFormat.mBitsPerChannel = 8;
|
||||
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
requestedFormat.mBitsPerChannel = 16;
|
||||
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
requestedFormat.mBitsPerChannel = 32;
|
||||
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
requestedFormat.mBitsPerChannel = 32;
|
||||
requestedFormat.mFormatFlags = kAudioFormatFlagIsPacked;
|
||||
break;
|
||||
case DevFmtByte:
|
||||
case DevFmtUShort:
|
||||
case DevFmtUInt:
|
||||
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
requestedFormat.mChannelsPerFrame = 1;
|
||||
break;
|
||||
case DevFmtStereo:
|
||||
requestedFormat.mChannelsPerFrame = 2;
|
||||
break;
|
||||
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
|
||||
goto error;
|
||||
}
|
||||
|
||||
requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8;
|
||||
requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame;
|
||||
requestedFormat.mSampleRate = device->Frequency;
|
||||
requestedFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
requestedFormat.mReserved = 0;
|
||||
requestedFormat.mFramesPerPacket = 1;
|
||||
|
||||
// save requested format description for later use
|
||||
data->format = requestedFormat;
|
||||
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
// Use intermediate format for sample rate conversion (outputFormat)
|
||||
// Set sample rate to the same as hardware for resampling later
|
||||
outputFormat = requestedFormat;
|
||||
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
|
||||
|
||||
// Determine sample rate ratio for resampling
|
||||
data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
|
||||
|
||||
// The output format should be the requested format, but using the hardware sample rate
|
||||
// This is because the AudioUnit will automatically scale other properties, except for sample rate
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Set the AudioUnit output format frame count
|
||||
outputFrameCount = device->UpdateSize * data->sampleRateRatio;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Set up sample converter
|
||||
err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioConverterNew failed: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Create a buffer for use in the resample callback
|
||||
data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
|
||||
|
||||
// Allocate buffer for the AudioUnit output
|
||||
data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
|
||||
if(data->bufferList == NULL)
|
||||
goto error;
|
||||
|
||||
data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
|
||||
if(data->ring == NULL)
|
||||
goto error;
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
error:
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data->resampleBuffer);
|
||||
destroy_buffer_list(data->bufferList);
|
||||
|
||||
if(data->audioConverter)
|
||||
AudioConverterDispose(data->audioConverter);
|
||||
if(data->audioUnit)
|
||||
CloseComponent(data->audioUnit);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void ca_close_capture(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data->resampleBuffer);
|
||||
destroy_buffer_list(data->bufferList);
|
||||
|
||||
AudioConverterDispose(data->audioConverter);
|
||||
CloseComponent(data->audioUnit);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void ca_start_capture(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err = AudioOutputUnitStart(data->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStart failed\n");
|
||||
}
|
||||
|
||||
static void ca_stop_capture(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err = AudioOutputUnitStop(data->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
}
|
||||
|
||||
static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
AudioBufferList *list;
|
||||
UInt32 frameCount;
|
||||
OSStatus err;
|
||||
|
||||
// If no samples are requested, just return
|
||||
if(samples == 0)
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
// Allocate a temporary AudioBufferList to use as the return resamples data
|
||||
list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
|
||||
|
||||
// Point the resampling buffer to the capture buffer
|
||||
list->mNumberBuffers = 1;
|
||||
list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
|
||||
list->mBuffers[0].mDataByteSize = samples * data->frameSize;
|
||||
list->mBuffers[0].mData = buffer;
|
||||
|
||||
// Resample into another AudioBufferList
|
||||
frameCount = samples;
|
||||
err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
|
||||
device, &frameCount, list, NULL);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint ca_available_samples(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = device->ExtraData;
|
||||
return RingBufferSize(data->ring) / data->sampleRateRatio;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs ca_funcs = {
|
||||
ca_open_playback,
|
||||
ca_close_playback,
|
||||
ca_reset_playback,
|
||||
ca_start_playback,
|
||||
ca_stop_playback,
|
||||
ca_open_capture,
|
||||
ca_close_capture,
|
||||
ca_start_capture,
|
||||
ca_stop_capture,
|
||||
ca_capture_samples,
|
||||
ca_available_samples,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_ca_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = ca_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_ca_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_ca_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(ca_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(ca_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2011 by Chris Robinson
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
typedef struct ALCloopback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
} ALCloopback;
|
||||
|
||||
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
|
||||
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
|
||||
static void ALCloopback_close(ALCloopback *self);
|
||||
static ALCboolean ALCloopback_reset(ALCloopback *self);
|
||||
static ALCboolean ALCloopback_start(ALCloopback *self);
|
||||
static void ALCloopback_stop(ALCloopback *self);
|
||||
static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCloopback);
|
||||
|
||||
|
||||
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCloopback, ALCbackend, self);
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCloopback_close(ALCloopback* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
static ALCboolean ALCloopback_reset(ALCloopback *self)
|
||||
{
|
||||
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self))
|
||||
{
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCloopback_stop(ALCloopback* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCloopbackFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCloopbackFactory;
|
||||
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
|
||||
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void)
|
||||
{
|
||||
static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self))
|
||||
{
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Loopback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type))
|
||||
{
|
||||
}
|
||||
|
||||
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Loopback)
|
||||
{
|
||||
ALCloopback *backend;
|
||||
|
||||
backend = ALCloopback_New(sizeof(*backend));
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCloopback_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2010 by Chris Robinson
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
typedef struct ALCnullBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} ALCnullBackend;
|
||||
|
||||
static int ALCnullBackend_mixerProc(void *ptr);
|
||||
|
||||
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
|
||||
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
|
||||
static void ALCnullBackend_close(ALCnullBackend *self);
|
||||
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
|
||||
static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
|
||||
static void ALCnullBackend_stop(ALCnullBackend *self);
|
||||
static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
|
||||
|
||||
|
||||
static const ALCchar nullDevice[] = "No Output";
|
||||
|
||||
|
||||
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
|
||||
}
|
||||
|
||||
|
||||
static int ALCnullBackend_mixerProc(void *ptr)
|
||||
{
|
||||
ALCnullBackend *self = (ALCnullBackend*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
struct timespec now, start;
|
||||
ALuint64 avail, done;
|
||||
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
|
||||
device->Frequency / 2);
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
done = 0;
|
||||
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get starting time\n");
|
||||
return 1;
|
||||
}
|
||||
while(!self->killNow && device->Connected)
|
||||
{
|
||||
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get current time\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
|
||||
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
|
||||
if(avail < done)
|
||||
{
|
||||
/* Oops, time skipped backwards. Reset the number of samples done
|
||||
* with one update available since we (likely) just came back from
|
||||
* sleeping. */
|
||||
done = avail - device->UpdateSize;
|
||||
}
|
||||
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(0, restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
aluMixData(device, NULL, device->UpdateSize);
|
||||
done += device->UpdateSize;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device;
|
||||
|
||||
if(!name)
|
||||
name = nullDevice;
|
||||
else if(strcmp(name, nullDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCnullBackend_close(ALCnullBackend* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
|
||||
{
|
||||
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
|
||||
{
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCnullBackend_stop(ALCnullBackend *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(self->killNow)
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCnullBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCnullBackendFactory;
|
||||
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self))
|
||||
{
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(nullDevice);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCnullBackend *backend;
|
||||
|
||||
backend = ALCnullBackend_New(sizeof(*backend));
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCnullBackend_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* This is an OpenAL backend for Android using the native audio APIs based on
|
||||
* OpenSL ES 1.0.1. It is based on source code for the native-audio sample app
|
||||
* bundled with NDK.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
/* Helper macros */
|
||||
#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
|
||||
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* engine interfaces */
|
||||
SLObjectItf engineObject;
|
||||
SLEngineItf engine;
|
||||
|
||||
/* output mix interfaces */
|
||||
SLObjectItf outputMix;
|
||||
|
||||
/* buffer queue player interfaces */
|
||||
SLObjectItf bufferQueueObject;
|
||||
|
||||
void *buffer;
|
||||
ALuint bufferSize;
|
||||
ALuint curBuffer;
|
||||
|
||||
ALuint frameSize;
|
||||
} osl_data;
|
||||
|
||||
|
||||
static const ALCchar opensl_device[] = "OpenSL";
|
||||
|
||||
|
||||
static SLuint32 GetChannelMask(enum DevFmtChannels chans)
|
||||
{
|
||||
switch(chans)
|
||||
{
|
||||
case DevFmtMono: return SL_SPEAKER_FRONT_CENTER;
|
||||
case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT;
|
||||
case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
|
||||
case DevFmtX51: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT;
|
||||
case DevFmtX61: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_CENTER|
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
case DevFmtX71: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_BACK_LEFT|SL_SPEAKER_BACK_RIGHT|
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
case DevFmtX51Side: return SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT|
|
||||
SL_SPEAKER_FRONT_CENTER|SL_SPEAKER_LOW_FREQUENCY|
|
||||
SL_SPEAKER_SIDE_LEFT|SL_SPEAKER_SIDE_RIGHT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *res_str(SLresult result)
|
||||
{
|
||||
switch(result)
|
||||
{
|
||||
case SL_RESULT_SUCCESS: return "Success";
|
||||
case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
|
||||
case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
|
||||
case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
|
||||
case SL_RESULT_RESOURCE_ERROR: return "Resource error";
|
||||
case SL_RESULT_RESOURCE_LOST: return "Resource lost";
|
||||
case SL_RESULT_IO_ERROR: return "I/O error";
|
||||
case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
|
||||
case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
|
||||
case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
|
||||
case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
|
||||
case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
|
||||
case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
|
||||
case SL_RESULT_INTERNAL_ERROR: return "Internal error";
|
||||
case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
|
||||
case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
|
||||
case SL_RESULT_CONTROL_LOST: return "Control lost";
|
||||
#ifdef SL_RESULT_READONLY
|
||||
case SL_RESULT_READONLY: return "ReadOnly";
|
||||
#endif
|
||||
#ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED
|
||||
case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
|
||||
#endif
|
||||
#ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE
|
||||
case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
|
||||
#endif
|
||||
}
|
||||
return "Unknown error code";
|
||||
}
|
||||
|
||||
#define PRINTERR(x, s) do { \
|
||||
if((x) != SL_RESULT_SUCCESS) \
|
||||
ERR("%s: %s\n", (s), res_str((x))); \
|
||||
} while(0)
|
||||
|
||||
/* this callback handler is called every time a buffer finishes playing */
|
||||
static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
{
|
||||
ALCdevice *Device = context;
|
||||
osl_data *data = Device->ExtraData;
|
||||
ALvoid *buf;
|
||||
SLresult result;
|
||||
|
||||
buf = (ALbyte*)data->buffer + data->curBuffer*data->bufferSize;
|
||||
aluMixData(Device, buf, data->bufferSize/data->frameSize);
|
||||
|
||||
result = VCALL(bq,Enqueue)(buf, data->bufferSize);
|
||||
PRINTERR(result, "bq->Enqueue");
|
||||
|
||||
data->curBuffer = (data->curBuffer+1) % Device->NumUpdates;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum opensl_open_playback(ALCdevice *Device, const ALCchar *deviceName)
|
||||
{
|
||||
osl_data *data = NULL;
|
||||
SLresult result;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = opensl_device;
|
||||
else if(strcmp(deviceName, opensl_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
if(!data)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
|
||||
// create engine
|
||||
result = slCreateEngine(&data->engineObject, 0, NULL, 0, NULL, NULL);
|
||||
PRINTERR(result, "slCreateEngine");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->engineObject,Realize)(SL_BOOLEAN_FALSE);
|
||||
PRINTERR(result, "engine->Realize");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->engineObject,GetInterface)(SL_IID_ENGINE, &data->engine);
|
||||
PRINTERR(result, "engine->GetInterface");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->engine,CreateOutputMix)(&data->outputMix, 0, NULL, NULL);
|
||||
PRINTERR(result, "engine->CreateOutputMix");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->outputMix,Realize)(SL_BOOLEAN_FALSE);
|
||||
PRINTERR(result, "outputMix->Realize");
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
if(data->outputMix != NULL)
|
||||
VCALL0(data->outputMix,Destroy)();
|
||||
data->outputMix = NULL;
|
||||
|
||||
if(data->engineObject != NULL)
|
||||
VCALL0(data->engineObject,Destroy)();
|
||||
data->engineObject = NULL;
|
||||
data->engine = NULL;
|
||||
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&Device->DeviceName, deviceName);
|
||||
Device->ExtraData = data;
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static void opensl_close_playback(ALCdevice *Device)
|
||||
{
|
||||
osl_data *data = Device->ExtraData;
|
||||
|
||||
if(data->bufferQueueObject != NULL)
|
||||
VCALL0(data->bufferQueueObject,Destroy)();
|
||||
data->bufferQueueObject = NULL;
|
||||
|
||||
VCALL0(data->outputMix,Destroy)();
|
||||
data->outputMix = NULL;
|
||||
|
||||
VCALL0(data->engineObject,Destroy)();
|
||||
data->engineObject = NULL;
|
||||
data->engine = NULL;
|
||||
|
||||
free(data);
|
||||
Device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean opensl_reset_playback(ALCdevice *Device)
|
||||
{
|
||||
osl_data *data = Device->ExtraData;
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
SLDataLocator_OutputMix loc_outmix;
|
||||
SLDataFormat_PCM format_pcm;
|
||||
SLDataSource audioSrc;
|
||||
SLDataSink audioSnk;
|
||||
SLInterfaceID id;
|
||||
SLboolean req;
|
||||
SLresult result;
|
||||
|
||||
|
||||
Device->UpdateSize = (ALuint64)Device->UpdateSize * 44100 / Device->Frequency;
|
||||
Device->UpdateSize = Device->UpdateSize * Device->NumUpdates / 2;
|
||||
Device->NumUpdates = 2;
|
||||
|
||||
Device->Frequency = 44100;
|
||||
Device->FmtChans = DevFmtStereo;
|
||||
Device->FmtType = DevFmtShort;
|
||||
|
||||
SetDefaultWFXChannelOrder(Device);
|
||||
|
||||
|
||||
id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
req = SL_BOOLEAN_TRUE;
|
||||
|
||||
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
loc_bufq.numBuffers = Device->NumUpdates;
|
||||
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = ChannelsFromDevFmt(Device->FmtChans);
|
||||
format_pcm.samplesPerSec = Device->Frequency * 1000;
|
||||
format_pcm.bitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
|
||||
format_pcm.containerSize = format_pcm.bitsPerSample;
|
||||
format_pcm.channelMask = GetChannelMask(Device->FmtChans);
|
||||
format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
|
||||
SL_BYTEORDER_BIGENDIAN;
|
||||
|
||||
audioSrc.pLocator = &loc_bufq;
|
||||
audioSrc.pFormat = &format_pcm;
|
||||
|
||||
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
loc_outmix.outputMix = data->outputMix;
|
||||
audioSnk.pLocator = &loc_outmix;
|
||||
audioSnk.pFormat = NULL;
|
||||
|
||||
|
||||
if(data->bufferQueueObject != NULL)
|
||||
VCALL0(data->bufferQueueObject,Destroy)();
|
||||
data->bufferQueueObject = NULL;
|
||||
|
||||
result = VCALL(data->engine,CreateAudioPlayer)(&data->bufferQueueObject, &audioSrc, &audioSnk, 1, &id, &req);
|
||||
PRINTERR(result, "engine->CreateAudioPlayer");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->bufferQueueObject,Realize)(SL_BOOLEAN_FALSE);
|
||||
PRINTERR(result, "bufferQueue->Realize");
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
if(data->bufferQueueObject != NULL)
|
||||
VCALL0(data->bufferQueueObject,Destroy)();
|
||||
data->bufferQueueObject = NULL;
|
||||
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean opensl_start_playback(ALCdevice *Device)
|
||||
{
|
||||
osl_data *data = Device->ExtraData;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
SLPlayItf player;
|
||||
SLresult result;
|
||||
ALuint i;
|
||||
|
||||
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
|
||||
PRINTERR(result, "bufferQueue->GetInterface");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(bufferQueue,RegisterCallback)(opensl_callback, Device);
|
||||
PRINTERR(result, "bufferQueue->RegisterCallback");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
data->frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
|
||||
data->bufferSize = Device->UpdateSize * data->frameSize;
|
||||
data->buffer = calloc(Device->NumUpdates, data->bufferSize);
|
||||
if(!data->buffer)
|
||||
{
|
||||
result = SL_RESULT_MEMORY_FAILURE;
|
||||
PRINTERR(result, "calloc");
|
||||
}
|
||||
}
|
||||
/* enqueue the first buffer to kick off the callbacks */
|
||||
for(i = 0;i < Device->NumUpdates;i++)
|
||||
{
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
ALvoid *buf = (ALbyte*)data->buffer + i*data->bufferSize;
|
||||
result = VCALL(bufferQueue,Enqueue)(buf, data->bufferSize);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
}
|
||||
}
|
||||
data->curBuffer = 0;
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
|
||||
PRINTERR(result, "bufferQueue->GetInterface");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
|
||||
PRINTERR(result, "player->SetPlayState");
|
||||
}
|
||||
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
{
|
||||
if(data->bufferQueueObject != NULL)
|
||||
VCALL0(data->bufferQueueObject,Destroy)();
|
||||
data->bufferQueueObject = NULL;
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
data->bufferSize = 0;
|
||||
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void opensl_stop_playback(ALCdevice *Device)
|
||||
{
|
||||
osl_data *data = Device->ExtraData;
|
||||
SLPlayItf player;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
SLresult result;
|
||||
|
||||
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_PLAY, &player);
|
||||
PRINTERR(result, "bufferQueue->GetInterface");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
|
||||
PRINTERR(result, "player->SetPlayState");
|
||||
}
|
||||
|
||||
result = VCALL(data->bufferQueueObject,GetInterface)(SL_IID_BUFFERQUEUE, &bufferQueue);
|
||||
PRINTERR(result, "bufferQueue->GetInterface");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL0(bufferQueue,Clear)();
|
||||
PRINTERR(result, "bufferQueue->Clear");
|
||||
}
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
data->bufferSize = 0;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs opensl_funcs = {
|
||||
opensl_open_playback,
|
||||
opensl_close_playback,
|
||||
opensl_reset_playback,
|
||||
opensl_start_playback,
|
||||
opensl_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
|
||||
ALCboolean alc_opensl_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = opensl_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_opensl_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_opensl_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(opensl_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,632 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
/*
|
||||
* The OSS documentation talks about SOUND_MIXER_READ, but the header
|
||||
* only contains MIXER_READ. Play safe. Same for WRITE.
|
||||
*/
|
||||
#ifndef SOUND_MIXER_READ
|
||||
#define SOUND_MIXER_READ MIXER_READ
|
||||
#endif
|
||||
#ifndef SOUND_MIXER_WRITE
|
||||
#define SOUND_MIXER_WRITE MIXER_WRITE
|
||||
#endif
|
||||
|
||||
|
||||
static const ALCchar oss_device[] = "OSS Default";
|
||||
|
||||
static const char *oss_driver = "/dev/dsp";
|
||||
static const char *oss_capture = "/dev/dsp";
|
||||
|
||||
static int log2i(ALCuint x)
|
||||
{
|
||||
int y = 0;
|
||||
while (x > 1)
|
||||
{
|
||||
x >>= 1;
|
||||
y++;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCplaybackOSS {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
int fd;
|
||||
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} ALCplaybackOSS;
|
||||
|
||||
static int ALCplaybackOSS_mixerProc(void *ptr);
|
||||
|
||||
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, Destruct)
|
||||
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name);
|
||||
static void ALCplaybackOSS_close(ALCplaybackOSS *self);
|
||||
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self);
|
||||
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self);
|
||||
static void ALCplaybackOSS_stop(ALCplaybackOSS *self);
|
||||
static DECLARE_FORWARD2(ALCplaybackOSS, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCplaybackOSS, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCplaybackOSS)
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCplaybackOSS);
|
||||
|
||||
|
||||
static int ALCplaybackOSS_mixerProc(void *ptr)
|
||||
{
|
||||
ALCplaybackOSS *self = (ALCplaybackOSS*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALint frameSize;
|
||||
ssize_t wrote;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
while(!self->killNow && device->Connected)
|
||||
{
|
||||
ALint len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !self->killNow)
|
||||
{
|
||||
wrote = write(self->fd, WritePtr, len);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
ALCplaybackOSS_lock(self);
|
||||
aluHandleDisconnect(device);
|
||||
ALCplaybackOSS_unlock(self);
|
||||
break;
|
||||
}
|
||||
|
||||
al_nssleep(0, 1000000);
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ALCplaybackOSS_Construct(ALCplaybackOSS *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCplaybackOSS, ALCbackend, self);
|
||||
}
|
||||
|
||||
static ALCenum ALCplaybackOSS_open(ALCplaybackOSS *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
if(!name)
|
||||
name = oss_device;
|
||||
else if(strcmp(name, oss_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->killNow = 0;
|
||||
|
||||
self->fd = open(oss_driver, O_WRONLY);
|
||||
if(self->fd == -1)
|
||||
{
|
||||
ERR("Could not open %s: %s\n", oss_driver, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCplaybackOSS_close(ALCplaybackOSS *self)
|
||||
{
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
}
|
||||
|
||||
static ALCboolean ALCplaybackOSS_reset(ALCplaybackOSS *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
int numFragmentsLogSize;
|
||||
int log2FragmentSize;
|
||||
unsigned int periods;
|
||||
audio_buf_info info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
int ossFormat;
|
||||
int ossSpeed;
|
||||
char *err;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
ossFormat = AFMT_S8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
ossFormat = AFMT_U8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
device->FmtType = DevFmtShort;
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
ossFormat = AFMT_S16_NE;
|
||||
break;
|
||||
}
|
||||
|
||||
periods = device->NumUpdates;
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
|
||||
ossSpeed = device->Frequency;
|
||||
log2FragmentSize = log2i(device->UpdateSize * frameSize);
|
||||
|
||||
/* according to the OSS spec, 16 bytes are the minimum */
|
||||
if (log2FragmentSize < 4)
|
||||
log2FragmentSize = 4;
|
||||
/* Subtract one period since the temp mixing buffer counts as one. Still
|
||||
* need at least two on the card, though. */
|
||||
if(periods > 2) periods--;
|
||||
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
|
||||
|
||||
#define CHECKERR(func) if((func) < 0) { \
|
||||
err = #func; \
|
||||
goto err; \
|
||||
}
|
||||
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
|
||||
* that's reported back via GETOSPACE */
|
||||
ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &info));
|
||||
if(0)
|
||||
{
|
||||
err:
|
||||
ERR("%s failed: %s\n", err, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
#undef CHECKERR
|
||||
|
||||
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
|
||||
{
|
||||
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
|
||||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
|
||||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
|
||||
{
|
||||
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = ossSpeed;
|
||||
device->UpdateSize = info.fragsize / frameSize;
|
||||
device->NumUpdates = info.fragments + 1;
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCplaybackOSS_start(ALCplaybackOSS *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
self->mix_data = calloc(1, self->data_size);
|
||||
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCplaybackOSS_mixerProc, self) != althrd_success)
|
||||
{
|
||||
free(self->mix_data);
|
||||
self->mix_data = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCplaybackOSS_stop(ALCplaybackOSS *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(self->killNow)
|
||||
return;
|
||||
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
if(ioctl(self->fd, SNDCTL_DSP_RESET) != 0)
|
||||
ERR("Error resetting device: %s\n", strerror(errno));
|
||||
|
||||
free(self->mix_data);
|
||||
self->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCcaptureOSS {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
int fd;
|
||||
|
||||
ALubyte *read_data;
|
||||
int data_size;
|
||||
|
||||
RingBuffer *ring;
|
||||
int doCapture;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} ALCcaptureOSS;
|
||||
|
||||
static int ALCcaptureOSS_recordProc(void *ptr);
|
||||
|
||||
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, Destruct)
|
||||
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name);
|
||||
static void ALCcaptureOSS_close(ALCcaptureOSS *self);
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self);
|
||||
static void ALCcaptureOSS_stop(ALCcaptureOSS *self);
|
||||
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self);
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, ALint64, getLatency)
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCcaptureOSS, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCcaptureOSS)
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCcaptureOSS);
|
||||
|
||||
|
||||
static int ALCcaptureOSS_recordProc(void *ptr)
|
||||
{
|
||||
ALCcaptureOSS *self = (ALCcaptureOSS*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
int frameSize;
|
||||
int amt;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), "alsoft-record");
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
while(!self->killNow)
|
||||
{
|
||||
amt = read(self->fd, self->read_data, self->data_size);
|
||||
if(amt < 0)
|
||||
{
|
||||
ERR("read failed: %s\n", strerror(errno));
|
||||
ALCcaptureOSS_lock(self);
|
||||
aluHandleDisconnect(device);
|
||||
ALCcaptureOSS_unlock(self);
|
||||
break;
|
||||
}
|
||||
if(amt == 0)
|
||||
{
|
||||
al_nssleep(0, 1000000);
|
||||
continue;
|
||||
}
|
||||
if(self->doCapture)
|
||||
WriteRingBuffer(self->ring, self->read_data, amt/frameSize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ALCcaptureOSS_Construct(ALCcaptureOSS *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCcaptureOSS, ALCbackend, self);
|
||||
}
|
||||
|
||||
static ALCenum ALCcaptureOSS_open(ALCcaptureOSS *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
int numFragmentsLogSize;
|
||||
int log2FragmentSize;
|
||||
unsigned int periods;
|
||||
audio_buf_info info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
int ossFormat;
|
||||
int ossSpeed;
|
||||
char *err;
|
||||
|
||||
if(!name)
|
||||
name = oss_device;
|
||||
else if(strcmp(name, oss_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->fd = open(oss_capture, O_RDONLY);
|
||||
if(self->fd == -1)
|
||||
{
|
||||
ERR("Could not open %s: %s\n", oss_capture, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
ossFormat = AFMT_S8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
ossFormat = AFMT_U8;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
ossFormat = AFMT_S16_NE;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
periods = 4;
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
ossSpeed = device->Frequency;
|
||||
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
|
||||
frameSize / periods);
|
||||
|
||||
/* according to the OSS spec, 16 bytes are the minimum */
|
||||
if (log2FragmentSize < 4)
|
||||
log2FragmentSize = 4;
|
||||
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
|
||||
|
||||
#define CHECKERR(func) if((func) < 0) { \
|
||||
err = #func; \
|
||||
goto err; \
|
||||
}
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SETFMT, &ossFormat));
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_CHANNELS, &numChannels));
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_SPEED, &ossSpeed));
|
||||
CHECKERR(ioctl(self->fd, SNDCTL_DSP_GETISPACE, &info));
|
||||
if(0)
|
||||
{
|
||||
err:
|
||||
ERR("%s failed: %s\n", err, strerror(errno));
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
#undef CHECKERR
|
||||
|
||||
if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
|
||||
{
|
||||
ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
|
||||
(ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
|
||||
(ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
|
||||
{
|
||||
ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
self->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
|
||||
if(!self->ring)
|
||||
{
|
||||
ERR("Ring buffer create failed\n");
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
self->data_size = info.fragsize;
|
||||
self->read_data = calloc(1, self->data_size);
|
||||
|
||||
self->killNow = 0;
|
||||
if(althrd_create(&self->thread, ALCcaptureOSS_recordProc, self) != althrd_success)
|
||||
{
|
||||
device->ExtraData = NULL;
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ALCcaptureOSS_close(ALCcaptureOSS *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
self->killNow = 1;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
|
||||
DestroyRingBuffer(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
free(self->read_data);
|
||||
self->read_data = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ALCcaptureOSS_start(ALCcaptureOSS *self)
|
||||
{
|
||||
self->doCapture = 1;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCcaptureOSS_stop(ALCcaptureOSS *self)
|
||||
{
|
||||
self->doCapture = 0;
|
||||
}
|
||||
|
||||
static ALCenum ALCcaptureOSS_captureSamples(ALCcaptureOSS *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ReadRingBuffer(self->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint ALCcaptureOSS_availableSamples(ALCcaptureOSS *self)
|
||||
{
|
||||
return RingBufferSize(self->ring);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCossBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCossBackendFactory;
|
||||
#define ALCOSSBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCossBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCossBackendFactory_init(ALCossBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCossBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCossBackendFactory_probe(ALCossBackendFactory *self, enum DevProbe type);
|
||||
static ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCossBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCossBackendFactory factory = ALCOSSBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
ALCboolean ALCossBackendFactory_init(ALCossBackendFactory* UNUSED(self))
|
||||
{
|
||||
ConfigValueStr("oss", "device", &oss_driver);
|
||||
ConfigValueStr("oss", "capture", &oss_capture);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
ALCboolean ALCossBackendFactory_querySupport(ALCossBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void ALCossBackendFactory_probe(ALCossBackendFactory* UNUSED(self), enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(oss_driver, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(oss_device);
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(oss_capture, &buf) == 0)
|
||||
#endif
|
||||
AppendCaptureDeviceList(oss_device);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ALCbackend* ALCossBackendFactory_createBackend(ALCossBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCplaybackOSS *backend;
|
||||
|
||||
backend = ALCplaybackOSS_New(sizeof(*backend));
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCplaybackOSS_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCcaptureOSS *backend;
|
||||
|
||||
backend = ALCcaptureOSS_New(sizeof(*backend));
|
||||
if(!backend) return NULL;
|
||||
memset(backend, 0, sizeof(*backend));
|
||||
|
||||
ALCcaptureOSS_Construct(backend, device);
|
||||
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
|
||||
static const ALCchar pa_device[] = "PortAudio Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static __typeof(x) * p##x
|
||||
MAKE_FUNC(Pa_Initialize);
|
||||
MAKE_FUNC(Pa_Terminate);
|
||||
MAKE_FUNC(Pa_GetErrorText);
|
||||
MAKE_FUNC(Pa_StartStream);
|
||||
MAKE_FUNC(Pa_StopStream);
|
||||
MAKE_FUNC(Pa_OpenStream);
|
||||
MAKE_FUNC(Pa_CloseStream);
|
||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||
MAKE_FUNC(Pa_GetDefaultInputDevice);
|
||||
MAKE_FUNC(Pa_GetStreamInfo);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
#define Pa_Initialize pPa_Initialize
|
||||
#define Pa_Terminate pPa_Terminate
|
||||
#define Pa_GetErrorText pPa_GetErrorText
|
||||
#define Pa_StartStream pPa_StartStream
|
||||
#define Pa_StopStream pPa_StopStream
|
||||
#define Pa_OpenStream pPa_OpenStream
|
||||
#define Pa_CloseStream pPa_CloseStream
|
||||
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
|
||||
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
|
||||
#define Pa_GetStreamInfo pPa_GetStreamInfo
|
||||
#endif
|
||||
|
||||
static ALCboolean pa_load(void)
|
||||
{
|
||||
PaError err;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!pa_handle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# define PALIB "portaudio.dll"
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
# define PALIB "libportaudio.2.dylib"
|
||||
#elif defined(__OpenBSD__)
|
||||
# define PALIB "libportaudio.so"
|
||||
#else
|
||||
# define PALIB "libportaudio.so.2"
|
||||
#endif
|
||||
|
||||
pa_handle = LoadLib(PALIB);
|
||||
if(!pa_handle)
|
||||
return ALC_FALSE;
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = GetSymbol(pa_handle, #f); \
|
||||
if(p##f == NULL) \
|
||||
{ \
|
||||
CloseLib(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
return ALC_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
LOAD_FUNC(Pa_Initialize);
|
||||
LOAD_FUNC(Pa_Terminate);
|
||||
LOAD_FUNC(Pa_GetErrorText);
|
||||
LOAD_FUNC(Pa_StartStream);
|
||||
LOAD_FUNC(Pa_StopStream);
|
||||
LOAD_FUNC(Pa_OpenStream);
|
||||
LOAD_FUNC(Pa_CloseStream);
|
||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||
LOAD_FUNC(Pa_GetDefaultInputDevice);
|
||||
LOAD_FUNC(Pa_GetStreamInfo);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if((err=Pa_Initialize()) != paNoError)
|
||||
{
|
||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
CloseLib(pa_handle);
|
||||
pa_handle = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if((err=Pa_Initialize()) != paNoError)
|
||||
{
|
||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
#endif
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
PaStream *stream;
|
||||
PaStreamParameters params;
|
||||
ALuint update_size;
|
||||
|
||||
RingBuffer *ring;
|
||||
} pa_data;
|
||||
|
||||
|
||||
static int pa_callback(const void *UNUSED(inputBuffer), void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
|
||||
aluMixData(device, outputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pa_capture_cb(const void *inputBuffer, void *UNUSED(outputBuffer),
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
|
||||
WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
pa_data *data;
|
||||
PaError err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
data->update_size = device->UpdateSize;
|
||||
|
||||
data->params.device = -1;
|
||||
if(!ConfigValueInt("port", "device", &data->params.device) ||
|
||||
data->params.device < 0)
|
||||
data->params.device = Pa_GetDefaultOutputDevice();
|
||||
data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
|
||||
(float)device->Frequency;
|
||||
data->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
data->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
data->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
data->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
data->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
data->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
|
||||
device->UpdateSize, paNoFlag, pa_callback, device);
|
||||
if(err != paNoError)
|
||||
{
|
||||
if(data->params.sampleFormat == paFloat32)
|
||||
{
|
||||
data->params.sampleFormat = paInt16;
|
||||
goto retry_open;
|
||||
}
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->ExtraData = data;
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void pa_close_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean pa_reset_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
const PaStreamInfo *streamInfo;
|
||||
|
||||
streamInfo = Pa_GetStreamInfo(data->stream);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
device->UpdateSize = data->update_size;
|
||||
|
||||
if(data->params.sampleFormat == paInt8)
|
||||
device->FmtType = DevFmtByte;
|
||||
else if(data->params.sampleFormat == paUInt8)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else if(data->params.sampleFormat == paInt16)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(data->params.sampleFormat == paInt32)
|
||||
device->FmtType = DevFmtInt;
|
||||
else if(data->params.sampleFormat == paFloat32)
|
||||
device->FmtType = DevFmtFloat;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(data->params.channelCount == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(data->params.channelCount == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected channel count: %u\n", data->params.channelCount);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean pa_start_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(data->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_stop_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StopStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
||||
static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
ALuint frame_size;
|
||||
pa_data *data;
|
||||
PaError err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
if(data == NULL)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
|
||||
if(data->ring == NULL)
|
||||
goto error;
|
||||
|
||||
data->params.device = -1;
|
||||
if(!ConfigValueInt("port", "capture", &data->params.device) ||
|
||||
data->params.device < 0)
|
||||
data->params.device = Pa_GetDefaultInputDevice();
|
||||
data->params.suggestedLatency = 0.0f;
|
||||
data->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
data->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
data->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
data->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
data->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
data->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
case DevFmtUShort:
|
||||
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
goto error;
|
||||
}
|
||||
data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
|
||||
|
||||
err = Pa_OpenStream(&data->stream, &data->params, NULL, device->Frequency,
|
||||
paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
goto error;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
error:
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void pa_close_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
|
||||
DestroyRingBuffer(data->ring);
|
||||
data->ring = NULL;
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void pa_start_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
static void pa_stop_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StopStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
ReadRingBuffer(data->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint pa_available_samples(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
return RingBufferSize(data->ring);
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs pa_funcs = {
|
||||
pa_open_playback,
|
||||
pa_close_playback,
|
||||
pa_reset_playback,
|
||||
pa_start_playback,
|
||||
pa_stop_playback,
|
||||
pa_open_capture,
|
||||
pa_close_capture,
|
||||
pa_start_capture,
|
||||
pa_stop_capture,
|
||||
pa_capture_samples,
|
||||
pa_available_samples,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_pa_init(BackendFuncs *func_list)
|
||||
{
|
||||
if(!pa_load())
|
||||
return ALC_FALSE;
|
||||
*func_list = pa_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_pa_deinit(void)
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(pa_handle)
|
||||
{
|
||||
Pa_Terminate();
|
||||
CloseLib(pa_handle);
|
||||
pa_handle = NULL;
|
||||
}
|
||||
#else
|
||||
Pa_Terminate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void alc_pa_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(pa_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(pa_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+1161
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,295 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include <sndio.h>
|
||||
|
||||
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
static ALCboolean sndio_load(void)
|
||||
{
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct sio_hdl *sndHandle;
|
||||
|
||||
ALvoid *mix_data;
|
||||
ALsizei data_size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} sndio_data;
|
||||
|
||||
|
||||
static int sndio_proc(void *ptr)
|
||||
{
|
||||
ALCdevice *device = ptr;
|
||||
sndio_data *data = device->ExtraData;
|
||||
ALsizei frameSize;
|
||||
size_t wrote;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
while(!data->killNow && device->Connected)
|
||||
{
|
||||
ALsizei len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
{
|
||||
wrote = sio_write(data->sndHandle, WritePtr, len);
|
||||
if(wrote == 0)
|
||||
{
|
||||
ERR("sio_write failed\n");
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
sndio_data *data;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = sndio_device;
|
||||
else if(strcmp(deviceName, sndio_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->sndHandle = sio_open(NULL, SIO_PLAY, 0);
|
||||
if(data->sndHandle == NULL)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open device\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void sndio_close_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
|
||||
sio_close(data->sndHandle);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean sndio_reset_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
struct sio_par par;
|
||||
|
||||
sio_initpar(&par);
|
||||
|
||||
par.rate = device->Frequency;
|
||||
par.pchan = ((device->FmtChans != DevFmtMono) ? 2 : 1);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bits = 8;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
par.bits = 8;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
case DevFmtShort:
|
||||
par.bits = 16;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
par.bits = 16;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
par.bits = 32;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
par.bits = 32;
|
||||
par.sig = 0;
|
||||
break;
|
||||
}
|
||||
par.le = SIO_LE_NATIVE;
|
||||
|
||||
par.round = device->UpdateSize;
|
||||
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
|
||||
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
|
||||
|
||||
if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par))
|
||||
{
|
||||
ERR("Failed to set device parameters\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(par.bits != par.bps*8)
|
||||
{
|
||||
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = par.rate;
|
||||
device->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
|
||||
|
||||
if(par.bits == 8 && par.sig == 1)
|
||||
device->FmtType = DevFmtByte;
|
||||
else if(par.bits == 8 && par.sig == 0)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else if(par.bits == 16 && par.sig == 1)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(par.bits == 16 && par.sig == 0)
|
||||
device->FmtType = DevFmtUShort;
|
||||
else if(par.bits == 32 && par.sig == 1)
|
||||
device->FmtType = DevFmtInt;
|
||||
else if(par.bits == 32 && par.sig == 0)
|
||||
device->FmtType = DevFmtUInt;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->UpdateSize = par.round;
|
||||
device->NumUpdates = (par.bufsz/par.round) + 1;
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean sndio_start_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
|
||||
if(!sio_start(data->sndHandle))
|
||||
{
|
||||
ERR("Error starting playback\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, sndio_proc, device) != althrd_success)
|
||||
{
|
||||
sio_stop(data->sndHandle);
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void sndio_stop_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
|
||||
if(!sio_stop(data->sndHandle))
|
||||
ERR("Error stopping device\n");
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs sndio_funcs = {
|
||||
sndio_open_playback,
|
||||
sndio_close_playback,
|
||||
sndio_reset_playback,
|
||||
sndio_start_playback,
|
||||
sndio_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_sndio_init(BackendFuncs *func_list)
|
||||
{
|
||||
if(!sndio_load())
|
||||
return ALC_FALSE;
|
||||
*func_list = sndio_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_sndio_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_sndio_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(sndio_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include <sys/audioio.h>
|
||||
|
||||
|
||||
static const ALCchar solaris_device[] = "Solaris Default";
|
||||
|
||||
static const char *solaris_driver = "/dev/audio";
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} solaris_data;
|
||||
|
||||
|
||||
static int SolarisProc(void *ptr)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)ptr;
|
||||
solaris_data *data = (solaris_data*)Device->ExtraData;
|
||||
ALint frameSize;
|
||||
int wrote;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
|
||||
|
||||
while(!data->killNow && Device->Connected)
|
||||
{
|
||||
ALint len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
|
||||
aluMixData(Device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
{
|
||||
wrote = write(data->fd, WritePtr, len);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
ALCdevice_Lock(Device);
|
||||
aluHandleDisconnect(Device);
|
||||
ALCdevice_Unlock(Device);
|
||||
break;
|
||||
}
|
||||
|
||||
al_nssleep(0, 1000000);
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
solaris_data *data;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = solaris_device;
|
||||
else if(strcmp(deviceName, solaris_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (solaris_data*)calloc(1, sizeof(solaris_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(solaris_driver, O_WRONLY);
|
||||
if(data->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void solaris_close_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
close(data->fd);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
audio_info_t info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
|
||||
AUDIO_INITINFO(&info);
|
||||
|
||||
info.play.sample_rate = device->Frequency;
|
||||
|
||||
if(device->FmtChans != DevFmtMono)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
info.play.channels = numChannels;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
info.play.precision = 8;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
info.play.precision = 8;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
device->FmtType = DevFmtShort;
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
info.play.precision = 16;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
}
|
||||
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
|
||||
|
||||
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
|
||||
{
|
||||
ERR("ioctl failed: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
|
||||
{
|
||||
ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && device->FmtType == DevFmtUByte) ||
|
||||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtByte) ||
|
||||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtShort) ||
|
||||
(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && device->FmtType == DevFmtInt)))
|
||||
{
|
||||
ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(device->FmtType),
|
||||
info.play.precision, info.play.encoding);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = info.play.sample_rate;
|
||||
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_start_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, SolarisProc, device) != althrd_success)
|
||||
{
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void solaris_stop_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
|
||||
if(ioctl(data->fd, AUDIO_DRAIN) < 0)
|
||||
ERR("Error draining device: %s\n", strerror(errno));
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs solaris_funcs = {
|
||||
solaris_open_playback,
|
||||
solaris_close_playback,
|
||||
solaris_reset_playback,
|
||||
solaris_start_playback,
|
||||
solaris_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_solaris_init(BackendFuncs *func_list)
|
||||
{
|
||||
ConfigValueStr("solaris", "device", &solaris_driver);
|
||||
|
||||
*func_list = solaris_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_solaris_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_solaris_probe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
{
|
||||
#ifdef HAVE_STAT
|
||||
struct stat buf;
|
||||
if(stat(solaris_driver, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDevicesList(solaris_device);
|
||||
}
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
long DataStart;
|
||||
|
||||
ALvoid *buffer;
|
||||
ALuint size;
|
||||
|
||||
volatile int killNow;
|
||||
althrd_t thread;
|
||||
} wave_data;
|
||||
|
||||
|
||||
static const ALCchar waveDevice[] = "Wave File Writer";
|
||||
|
||||
static const ALubyte SUBTYPE_PCM[] = {
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
static const ALubyte SUBTYPE_FLOAT[] = {
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
|
||||
static const ALuint channel_masks[] = {
|
||||
0, /* invalid */
|
||||
0x4, /* Mono */
|
||||
0x1 | 0x2, /* Stereo */
|
||||
0, /* 3 channel */
|
||||
0x1 | 0x2 | 0x10 | 0x20, /* Quad */
|
||||
0, /* 5 channel */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */
|
||||
};
|
||||
|
||||
|
||||
static void fwrite16le(ALushort val, FILE *f)
|
||||
{
|
||||
fputc(val&0xff, f);
|
||||
fputc((val>>8)&0xff, f);
|
||||
}
|
||||
|
||||
static void fwrite32le(ALuint val, FILE *f)
|
||||
{
|
||||
fputc(val&0xff, f);
|
||||
fputc((val>>8)&0xff, f);
|
||||
fputc((val>>16)&0xff, f);
|
||||
fputc((val>>24)&0xff, f);
|
||||
}
|
||||
|
||||
|
||||
static int WaveProc(void *ptr)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)ptr;
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
struct timespec now, start;
|
||||
ALint64 avail, done;
|
||||
ALuint frameSize;
|
||||
size_t fs;
|
||||
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
|
||||
device->Frequency / 2);
|
||||
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
done = 0;
|
||||
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get starting time\n");
|
||||
return 1;
|
||||
}
|
||||
while(!data->killNow && device->Connected)
|
||||
{
|
||||
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get current time\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
|
||||
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
|
||||
if(avail < done)
|
||||
{
|
||||
/* Oops, time skipped backwards. Reset the number of samples done
|
||||
* with one update available since we (likely) just came back from
|
||||
* sleeping. */
|
||||
done = avail - device->UpdateSize;
|
||||
}
|
||||
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(0, restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
aluMixData(device, data->buffer, device->UpdateSize);
|
||||
done += device->UpdateSize;
|
||||
|
||||
if(!IS_LITTLE_ENDIAN)
|
||||
{
|
||||
ALuint bytesize = BytesFromDevFmt(device->FmtType);
|
||||
ALubyte *bytes = data->buffer;
|
||||
ALuint i;
|
||||
|
||||
if(bytesize == 1)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i], data->f);
|
||||
}
|
||||
else if(bytesize == 2)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^1], data->f);
|
||||
}
|
||||
else if(bytesize == 4)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^3], data->f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fs = fwrite(data->buffer, frameSize, device->UpdateSize,
|
||||
data->f);
|
||||
(void)fs;
|
||||
}
|
||||
if(ferror(data->f))
|
||||
{
|
||||
ERR("Error writing to file\n");
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
wave_data *data;
|
||||
const char *fname;
|
||||
|
||||
fname = GetConfigValue("wave", "file", "");
|
||||
if(!fname[0])
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = waveDevice;
|
||||
else if(strcmp(deviceName, waveDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (wave_data*)calloc(1, sizeof(wave_data));
|
||||
|
||||
data->f = al_fopen(fname, "wb");
|
||||
if(!data->f)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
al_string_copy_cstr(&device->DeviceName, deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void wave_close_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
|
||||
fclose(data->f);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean wave_reset_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint channels=0, bits=0;
|
||||
size_t val;
|
||||
|
||||
fseek(data->f, 0, SEEK_SET);
|
||||
clearerr(data->f);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
device->FmtType = DevFmtUByte;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
device->FmtType = DevFmtShort;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
device->FmtType = DevFmtInt;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
}
|
||||
bits = BytesFromDevFmt(device->FmtType) * 8;
|
||||
channels = ChannelsFromDevFmt(device->FmtChans);
|
||||
|
||||
fprintf(data->f, "RIFF");
|
||||
fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close
|
||||
|
||||
fprintf(data->f, "WAVE");
|
||||
|
||||
fprintf(data->f, "fmt ");
|
||||
fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
|
||||
// 16-bit val, format type id (extensible: 0xFFFE)
|
||||
fwrite16le(0xFFFE, data->f);
|
||||
// 16-bit val, channel count
|
||||
fwrite16le(channels, data->f);
|
||||
// 32-bit val, frequency
|
||||
fwrite32le(device->Frequency, data->f);
|
||||
// 32-bit val, bytes per second
|
||||
fwrite32le(device->Frequency * channels * bits / 8, data->f);
|
||||
// 16-bit val, frame size
|
||||
fwrite16le(channels * bits / 8, data->f);
|
||||
// 16-bit val, bits per sample
|
||||
fwrite16le(bits, data->f);
|
||||
// 16-bit val, extra byte count
|
||||
fwrite16le(22, data->f);
|
||||
// 16-bit val, valid bits per sample
|
||||
fwrite16le(bits, data->f);
|
||||
// 32-bit val, channel mask
|
||||
fwrite32le(channel_masks[channels], data->f);
|
||||
// 16 byte GUID, sub-type format
|
||||
val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
|
||||
(void)val;
|
||||
|
||||
fprintf(data->f, "data");
|
||||
fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close
|
||||
|
||||
if(ferror(data->f))
|
||||
{
|
||||
ERR("Error writing header: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
data->DataStart = ftell(data->f);
|
||||
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean wave_start_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
|
||||
data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->buffer = malloc(data->size);
|
||||
if(!data->buffer)
|
||||
{
|
||||
ERR("Buffer malloc failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->killNow = 0;
|
||||
if(althrd_create(&data->thread, WaveProc, device) != althrd_success)
|
||||
{
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void wave_stop_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint dataLen;
|
||||
long size;
|
||||
int res;
|
||||
|
||||
if(data->killNow)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
althrd_join(data->thread, &res);
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
|
||||
size = ftell(data->f);
|
||||
if(size > 0)
|
||||
{
|
||||
dataLen = size - data->DataStart;
|
||||
if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
|
||||
fwrite32le(dataLen, data->f); // 'data' header len
|
||||
if(fseek(data->f, 4, SEEK_SET) == 0)
|
||||
fwrite32le(size-8, data->f); // 'WAVE' header len
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs wave_funcs = {
|
||||
wave_open_playback,
|
||||
wave_close_playback,
|
||||
wave_reset_playback,
|
||||
wave_start_playback,
|
||||
wave_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alc_wave_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = wave_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_wave_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_wave_probe(enum DevProbe type)
|
||||
{
|
||||
if(!ConfigValueExists("wave", "file"))
|
||||
return;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDevicesList(waveDevice);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,716 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
// MMSYSTEM Device
|
||||
volatile ALboolean killNow;
|
||||
althrd_t thread;
|
||||
|
||||
RefCount WaveBuffersCommitted;
|
||||
WAVEHDR WaveBuffer[4];
|
||||
|
||||
union {
|
||||
HWAVEIN In;
|
||||
HWAVEOUT Out;
|
||||
} WaveHandle;
|
||||
|
||||
WAVEFORMATEX Format;
|
||||
|
||||
RingBuffer *Ring;
|
||||
} WinMMData;
|
||||
|
||||
|
||||
TYPEDEF_VECTOR(al_string, vector_al_string)
|
||||
static vector_al_string PlaybackDevices;
|
||||
static vector_al_string CaptureDevices;
|
||||
|
||||
static void clear_devlist(vector_al_string *list)
|
||||
{
|
||||
VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
|
||||
VECTOR_RESIZE(*list, 0);
|
||||
}
|
||||
|
||||
|
||||
static void ProbePlaybackDevices(void)
|
||||
{
|
||||
al_string *iter, *end;
|
||||
ALuint numdevs;
|
||||
ALuint i;
|
||||
|
||||
clear_devlist(&PlaybackDevices);
|
||||
|
||||
numdevs = waveOutGetNumDevs();
|
||||
VECTOR_RESERVE(PlaybackDevices, numdevs);
|
||||
for(i = 0;i < numdevs;i++)
|
||||
{
|
||||
WAVEOUTCAPSW WaveCaps;
|
||||
al_string dname;
|
||||
|
||||
AL_STRING_INIT(dname);
|
||||
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
ALuint count = 0;
|
||||
do {
|
||||
al_string_copy_wcstr(&dname, WaveCaps.szPname);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
al_string_append_cstr(&dname, str);
|
||||
}
|
||||
count++;
|
||||
|
||||
iter = VECTOR_ITER_BEGIN(PlaybackDevices);
|
||||
end = VECTOR_ITER_END(PlaybackDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(al_string_cmp(*iter, dname) == 0)
|
||||
break;
|
||||
}
|
||||
} while(iter != end);
|
||||
|
||||
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
|
||||
}
|
||||
VECTOR_PUSH_BACK(PlaybackDevices, dname);
|
||||
}
|
||||
}
|
||||
|
||||
static void ProbeCaptureDevices(void)
|
||||
{
|
||||
al_string *iter, *end;
|
||||
ALuint numdevs;
|
||||
ALuint i;
|
||||
|
||||
clear_devlist(&CaptureDevices);
|
||||
|
||||
numdevs = waveInGetNumDevs();
|
||||
VECTOR_RESERVE(CaptureDevices, numdevs);
|
||||
for(i = 0;i < numdevs;i++)
|
||||
{
|
||||
WAVEINCAPSW WaveCaps;
|
||||
al_string dname;
|
||||
|
||||
AL_STRING_INIT(dname);
|
||||
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
ALuint count = 0;
|
||||
do {
|
||||
al_string_copy_wcstr(&dname, WaveCaps.szPname);
|
||||
if(count != 0)
|
||||
{
|
||||
char str[64];
|
||||
snprintf(str, sizeof(str), " #%d", count+1);
|
||||
al_string_append_cstr(&dname, str);
|
||||
}
|
||||
count++;
|
||||
|
||||
iter = VECTOR_ITER_BEGIN(CaptureDevices);
|
||||
end = VECTOR_ITER_END(CaptureDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(al_string_cmp(*iter, dname) == 0)
|
||||
break;
|
||||
}
|
||||
} while(iter != end);
|
||||
|
||||
TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
|
||||
}
|
||||
VECTOR_PUSH_BACK(CaptureDevices, dname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
WaveOutProc
|
||||
|
||||
Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
|
||||
returns to the application (for more data)
|
||||
*/
|
||||
static void CALLBACK WaveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)instance;
|
||||
WinMMData *data = Device->ExtraData;
|
||||
|
||||
if(msg != WOM_DONE)
|
||||
return;
|
||||
|
||||
DecrementRef(&data->WaveBuffersCommitted);
|
||||
PostThreadMessage(data->thread, msg, 0, param1);
|
||||
}
|
||||
|
||||
FORCE_ALIGN static int PlaybackThreadProc(void *arg)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)arg;
|
||||
WinMMData *data = Device->ExtraData;
|
||||
WAVEHDR *WaveHdr;
|
||||
MSG msg;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
while(GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if(msg.message != WOM_DONE)
|
||||
continue;
|
||||
|
||||
if(data->killNow)
|
||||
{
|
||||
if(ReadRef(&data->WaveBuffersCommitted) == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
WaveHdr = ((WAVEHDR*)msg.lParam);
|
||||
aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength /
|
||||
data->Format.nBlockAlign);
|
||||
|
||||
// Send buffer back to play more data
|
||||
waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR));
|
||||
IncrementRef(&data->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
WaveInProc
|
||||
|
||||
Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
|
||||
returns to the application (with more data)
|
||||
*/
|
||||
static void CALLBACK WaveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)instance;
|
||||
WinMMData *data = Device->ExtraData;
|
||||
|
||||
if(msg != WIM_DATA)
|
||||
return;
|
||||
|
||||
DecrementRef(&data->WaveBuffersCommitted);
|
||||
PostThreadMessage(data->thread, msg, 0, param1);
|
||||
}
|
||||
|
||||
static int CaptureThreadProc(void *arg)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)arg;
|
||||
WinMMData *data = Device->ExtraData;
|
||||
WAVEHDR *WaveHdr;
|
||||
MSG msg;
|
||||
|
||||
althrd_setname(althrd_current(), "alsoft-record");
|
||||
|
||||
while(GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if(msg.message != WIM_DATA)
|
||||
continue;
|
||||
/* Don't wait for other buffers to finish before quitting. We're
|
||||
* closing so we don't need them. */
|
||||
if(data->killNow)
|
||||
break;
|
||||
|
||||
WaveHdr = ((WAVEHDR*)msg.lParam);
|
||||
WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData,
|
||||
WaveHdr->dwBytesRecorded/data->Format.nBlockAlign);
|
||||
|
||||
// Send buffer back to capture more data
|
||||
waveInAddBuffer(data->WaveHandle.In, WaveHdr, sizeof(WAVEHDR));
|
||||
IncrementRef(&data->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
|
||||
{
|
||||
WinMMData *data = NULL;
|
||||
const al_string *iter, *end;
|
||||
UINT DeviceID;
|
||||
MMRESULT res;
|
||||
|
||||
if(VECTOR_SIZE(PlaybackDevices) == 0)
|
||||
ProbePlaybackDevices();
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
iter = VECTOR_ITER_BEGIN(PlaybackDevices);
|
||||
end = VECTOR_ITER_END(PlaybackDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(!al_string_empty(*iter) &&
|
||||
(!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0))
|
||||
{
|
||||
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(iter == end)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
if(!data)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
Device->ExtraData = data;
|
||||
|
||||
retry_open:
|
||||
memset(&data->Format, 0, sizeof(WAVEFORMATEX));
|
||||
if(Device->FmtType == DevFmtFloat)
|
||||
{
|
||||
data->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
data->Format.wBitsPerSample = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
if(Device->FmtType == DevFmtUByte || Device->FmtType == DevFmtByte)
|
||||
data->Format.wBitsPerSample = 8;
|
||||
else
|
||||
data->Format.wBitsPerSample = 16;
|
||||
}
|
||||
data->Format.nChannels = ((Device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
data->Format.nBlockAlign = data->Format.wBitsPerSample *
|
||||
data->Format.nChannels / 8;
|
||||
data->Format.nSamplesPerSec = Device->Frequency;
|
||||
data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
|
||||
data->Format.nBlockAlign;
|
||||
data->Format.cbSize = 0;
|
||||
|
||||
if((res=waveOutOpen(&data->WaveHandle.Out, DeviceID, &data->Format, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
||||
{
|
||||
if(Device->FmtType == DevFmtFloat)
|
||||
{
|
||||
Device->FmtType = DevFmtShort;
|
||||
goto retry_open;
|
||||
}
|
||||
ERR("waveOutOpen failed: %u\n", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
al_string_copy(&Device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
failure:
|
||||
if(data->WaveHandle.Out)
|
||||
waveOutClose(data->WaveHandle.Out);
|
||||
|
||||
free(data);
|
||||
Device->ExtraData = NULL;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void WinMMClosePlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)device->ExtraData;
|
||||
|
||||
// Close the Wave device
|
||||
waveOutClose(data->WaveHandle.Out);
|
||||
data->WaveHandle.Out = 0;
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean WinMMResetPlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)device->ExtraData;
|
||||
|
||||
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
|
||||
data->Format.nSamplesPerSec /
|
||||
device->Frequency);
|
||||
device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
|
||||
device->NumUpdates = 4;
|
||||
device->Frequency = data->Format.nSamplesPerSec;
|
||||
|
||||
if(data->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
|
||||
{
|
||||
if(data->Format.wBitsPerSample == 32)
|
||||
device->FmtType = DevFmtFloat;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled IEEE float sample depth: %d\n", data->Format.wBitsPerSample);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
else if(data->Format.wFormatTag == WAVE_FORMAT_PCM)
|
||||
{
|
||||
if(data->Format.wBitsPerSample == 16)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(data->Format.wBitsPerSample == 8)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled PCM sample depth: %d\n", data->Format.wBitsPerSample);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unhandled format tag: 0x%04x\n", data->Format.wFormatTag);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(data->Format.nChannels == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(data->Format.nChannels == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unhandled channel count: %d\n", data->Format.nChannels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean WinMMStartPlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)device->ExtraData;
|
||||
ALbyte *BufferData;
|
||||
ALint BufferSize;
|
||||
ALuint i;
|
||||
|
||||
data->killNow = AL_FALSE;
|
||||
if(althrd_create(&data->thread, PlaybackThreadProc, device) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
|
||||
InitRef(&data->WaveBuffersCommitted, 0);
|
||||
|
||||
// Create 4 Buffers
|
||||
BufferSize = device->UpdateSize*device->NumUpdates / 4;
|
||||
BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
|
||||
BufferData = calloc(4, BufferSize);
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
||||
data->WaveBuffer[i].dwBufferLength = BufferSize;
|
||||
data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
|
||||
(data->WaveBuffer[i-1].lpData +
|
||||
data->WaveBuffer[i-1].dwBufferLength));
|
||||
waveOutPrepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
waveOutWrite(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
IncrementRef(&data->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void WinMMStopPlayback(ALCdevice *device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)device->ExtraData;
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
if(data->killNow)
|
||||
return;
|
||||
|
||||
// Set flag to stop processing headers
|
||||
data->killNow = AL_TRUE;
|
||||
althrd_join(data->thread, &i);
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveOutUnprepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0) buffer = data->WaveBuffer[i].lpData;
|
||||
data->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
|
||||
static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
|
||||
{
|
||||
const al_string *iter, *end;
|
||||
ALbyte *BufferData = NULL;
|
||||
DWORD CapturedDataSize;
|
||||
WinMMData *data = NULL;
|
||||
ALint BufferSize;
|
||||
UINT DeviceID;
|
||||
MMRESULT res;
|
||||
ALuint i;
|
||||
|
||||
if(VECTOR_SIZE(CaptureDevices) == 0)
|
||||
ProbeCaptureDevices();
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
iter = VECTOR_ITER_BEGIN(CaptureDevices);
|
||||
end = VECTOR_ITER_END(CaptureDevices);
|
||||
for(;iter != end;iter++)
|
||||
{
|
||||
if(!al_string_empty(*iter) &&
|
||||
(!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0))
|
||||
{
|
||||
DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(iter == end)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
switch(Device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
case DevFmtStereo:
|
||||
break;
|
||||
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
return ALC_INVALID_ENUM;
|
||||
}
|
||||
|
||||
switch(Device->FmtType)
|
||||
{
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
|
||||
case DevFmtByte:
|
||||
case DevFmtUShort:
|
||||
case DevFmtUInt:
|
||||
return ALC_INVALID_ENUM;
|
||||
}
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
if(!data)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
Device->ExtraData = data;
|
||||
|
||||
memset(&data->Format, 0, sizeof(WAVEFORMATEX));
|
||||
data->Format.wFormatTag = ((Device->FmtType == DevFmtFloat) ?
|
||||
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
|
||||
data->Format.nChannels = ChannelsFromDevFmt(Device->FmtChans);
|
||||
data->Format.wBitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
|
||||
data->Format.nBlockAlign = data->Format.wBitsPerSample *
|
||||
data->Format.nChannels / 8;
|
||||
data->Format.nSamplesPerSec = Device->Frequency;
|
||||
data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
|
||||
data->Format.nBlockAlign;
|
||||
data->Format.cbSize = 0;
|
||||
|
||||
if((res=waveInOpen(&data->WaveHandle.In, DeviceID, &data->Format, (DWORD_PTR)&WaveInProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
|
||||
{
|
||||
ERR("waveInOpen failed: %u\n", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
// Allocate circular memory buffer for the captured audio
|
||||
CapturedDataSize = Device->UpdateSize*Device->NumUpdates;
|
||||
|
||||
// Make sure circular buffer is at least 100ms in size
|
||||
if(CapturedDataSize < (data->Format.nSamplesPerSec / 10))
|
||||
CapturedDataSize = data->Format.nSamplesPerSec / 10;
|
||||
|
||||
data->Ring = CreateRingBuffer(data->Format.nBlockAlign, CapturedDataSize);
|
||||
if(!data->Ring)
|
||||
goto failure;
|
||||
|
||||
InitRef(&data->WaveBuffersCommitted, 0);
|
||||
|
||||
// Create 4 Buffers of 50ms each
|
||||
BufferSize = data->Format.nAvgBytesPerSec / 20;
|
||||
BufferSize -= (BufferSize % data->Format.nBlockAlign);
|
||||
|
||||
BufferData = calloc(4, BufferSize);
|
||||
if(!BufferData)
|
||||
goto failure;
|
||||
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
|
||||
data->WaveBuffer[i].dwBufferLength = BufferSize;
|
||||
data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
|
||||
(data->WaveBuffer[i-1].lpData +
|
||||
data->WaveBuffer[i-1].dwBufferLength));
|
||||
data->WaveBuffer[i].dwFlags = 0;
|
||||
data->WaveBuffer[i].dwLoops = 0;
|
||||
waveInPrepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
waveInAddBuffer(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
IncrementRef(&data->WaveBuffersCommitted);
|
||||
}
|
||||
|
||||
if(althrd_create(&data->thread, CaptureThreadProc, Device) != althrd_success)
|
||||
goto failure;
|
||||
|
||||
al_string_copy(&Device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
failure:
|
||||
if(BufferData)
|
||||
{
|
||||
for(i = 0;i < 4;i++)
|
||||
waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
free(BufferData);
|
||||
}
|
||||
|
||||
if(data->Ring)
|
||||
DestroyRingBuffer(data->Ring);
|
||||
|
||||
if(data->WaveHandle.In)
|
||||
waveInClose(data->WaveHandle.In);
|
||||
|
||||
free(data);
|
||||
Device->ExtraData = NULL;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void WinMMCloseCapture(ALCdevice *Device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
void *buffer = NULL;
|
||||
int i;
|
||||
|
||||
/* Tell the processing thread to quit and wait for it to do so. */
|
||||
data->killNow = AL_TRUE;
|
||||
PostThreadMessage(data->thread, WM_QUIT, 0, 0);
|
||||
|
||||
althrd_join(data->thread, &i);
|
||||
|
||||
/* Make sure capture is stopped and all pending buffers are flushed. */
|
||||
waveInReset(data->WaveHandle.In);
|
||||
|
||||
// Release the wave buffers
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
|
||||
if(i == 0) buffer = data->WaveBuffer[i].lpData;
|
||||
data->WaveBuffer[i].lpData = NULL;
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
DestroyRingBuffer(data->Ring);
|
||||
data->Ring = NULL;
|
||||
|
||||
// Close the Wave device
|
||||
waveInClose(data->WaveHandle.In);
|
||||
data->WaveHandle.In = 0;
|
||||
|
||||
free(data);
|
||||
Device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void WinMMStartCapture(ALCdevice *Device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
waveInStart(data->WaveHandle.In);
|
||||
}
|
||||
|
||||
static void WinMMStopCapture(ALCdevice *Device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
waveInStop(data->WaveHandle.In);
|
||||
}
|
||||
|
||||
static ALCenum WinMMCaptureSamples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
ReadRingBuffer(data->Ring, Buffer, Samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint WinMMAvailableSamples(ALCdevice *Device)
|
||||
{
|
||||
WinMMData *data = (WinMMData*)Device->ExtraData;
|
||||
return RingBufferSize(data->Ring);
|
||||
}
|
||||
|
||||
|
||||
static inline void AppendAllDevicesList2(const al_string *name)
|
||||
{
|
||||
if(!al_string_empty(*name))
|
||||
AppendAllDevicesList(al_string_get_cstr(*name));
|
||||
}
|
||||
static inline void AppendCaptureDeviceList2(const al_string *name)
|
||||
{
|
||||
if(!al_string_empty(*name))
|
||||
AppendCaptureDeviceList(al_string_get_cstr(*name));
|
||||
}
|
||||
|
||||
static const BackendFuncs WinMMFuncs = {
|
||||
WinMMOpenPlayback,
|
||||
WinMMClosePlayback,
|
||||
WinMMResetPlayback,
|
||||
WinMMStartPlayback,
|
||||
WinMMStopPlayback,
|
||||
WinMMOpenCapture,
|
||||
WinMMCloseCapture,
|
||||
WinMMStartCapture,
|
||||
WinMMStopCapture,
|
||||
WinMMCaptureSamples,
|
||||
WinMMAvailableSamples,
|
||||
ALCdevice_GetLatencyDefault
|
||||
};
|
||||
|
||||
ALCboolean alcWinMMInit(BackendFuncs *FuncList)
|
||||
{
|
||||
VECTOR_INIT(PlaybackDevices);
|
||||
VECTOR_INIT(CaptureDevices);
|
||||
|
||||
*FuncList = WinMMFuncs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alcWinMMDeinit()
|
||||
{
|
||||
clear_devlist(&PlaybackDevices);
|
||||
VECTOR_DEINIT(PlaybackDevices);
|
||||
|
||||
clear_devlist(&CaptureDevices);
|
||||
VECTOR_DEINIT(CaptureDevices);
|
||||
}
|
||||
|
||||
void alcWinMMProbe(enum DevProbe type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
ProbePlaybackDevices();
|
||||
VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
ProbeCaptureDevices();
|
||||
VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
+143
-201
@@ -1,201 +1,143 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Boris Mikhaylov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "bs2b.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* Single pole IIR filter.
|
||||
* O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1]
|
||||
*/
|
||||
|
||||
/* Lowpass filter */
|
||||
#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1))
|
||||
|
||||
/* Highboost filter */
|
||||
#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1))
|
||||
|
||||
/* Set up all data. */
|
||||
static void init(struct bs2b *bs2b)
|
||||
{
|
||||
double Fc_lo, Fc_hi;
|
||||
double G_lo, G_hi;
|
||||
double x;
|
||||
|
||||
if ((bs2b->srate > 192000) || (bs2b->srate < 2000))
|
||||
bs2b->srate = BS2B_DEFAULT_SRATE;
|
||||
|
||||
switch(bs2b->level)
|
||||
{
|
||||
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
|
||||
Fc_lo = 360.0;
|
||||
Fc_hi = 501.0;
|
||||
G_lo = 0.398107170553497;
|
||||
G_hi = 0.205671765275719;
|
||||
break;
|
||||
|
||||
case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
|
||||
Fc_lo = 500.0;
|
||||
Fc_hi = 711.0;
|
||||
G_lo = 0.459726988530872;
|
||||
G_hi = 0.228208484414988;
|
||||
break;
|
||||
|
||||
case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
|
||||
Fc_lo = 700.0;
|
||||
Fc_hi = 1021.0;
|
||||
G_lo = 0.530884444230988;
|
||||
G_hi = 0.250105790667544;
|
||||
break;
|
||||
|
||||
case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
|
||||
Fc_lo = 360.0;
|
||||
Fc_hi = 494.0;
|
||||
G_lo = 0.316227766016838;
|
||||
G_hi = 0.168236228897329;
|
||||
break;
|
||||
|
||||
case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
|
||||
Fc_lo = 500.0;
|
||||
Fc_hi = 689.0;
|
||||
G_lo = 0.354813389233575;
|
||||
G_hi = 0.187169483835901;
|
||||
break;
|
||||
|
||||
default: /* High easy crossfeed level */
|
||||
bs2b->level = BS2B_HIGH_ECLEVEL;
|
||||
|
||||
Fc_lo = 700.0;
|
||||
Fc_hi = 975.0;
|
||||
G_lo = 0.398107170553497;
|
||||
G_hi = 0.205671765275719;
|
||||
break;
|
||||
} /* switch */
|
||||
|
||||
/* $fc = $Fc / $s;
|
||||
* $d = 1 / 2 / pi / $fc;
|
||||
* $x = exp(-1 / $d);
|
||||
*/
|
||||
|
||||
x = exp(-2.0 * M_PI * Fc_lo / bs2b->srate);
|
||||
bs2b->b1_lo = x;
|
||||
bs2b->a0_lo = G_lo * (1.0 - x);
|
||||
|
||||
x = exp(-2.0 * M_PI * Fc_hi / bs2b->srate);
|
||||
bs2b->b1_hi = x;
|
||||
bs2b->a0_hi = 1.0 - G_hi * (1.0 - x);
|
||||
bs2b->a1_hi = -x;
|
||||
|
||||
bs2b->gain = 1.0 / (1.0 - G_hi + G_lo);
|
||||
} /* init */
|
||||
|
||||
/* Exported functions.
|
||||
* See descriptions in "bs2b.h"
|
||||
*/
|
||||
|
||||
void bs2b_set_level(struct bs2b *bs2b, int level)
|
||||
{
|
||||
if(level == bs2b->level)
|
||||
return;
|
||||
bs2b->level = level;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_level */
|
||||
|
||||
int bs2b_get_level(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->level;
|
||||
} /* bs2b_get_level */
|
||||
|
||||
void bs2b_set_srate(struct bs2b *bs2b, int srate)
|
||||
{
|
||||
if (srate == bs2b->srate)
|
||||
return;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_srate */
|
||||
|
||||
int bs2b_get_srate(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->srate;
|
||||
} /* bs2b_get_srate */
|
||||
|
||||
void bs2b_clear(struct bs2b *bs2b)
|
||||
{
|
||||
int loopv = sizeof(bs2b->last_sample);
|
||||
|
||||
while (loopv)
|
||||
{
|
||||
((char *)&bs2b->last_sample)[--loopv] = 0;
|
||||
}
|
||||
} /* bs2b_clear */
|
||||
|
||||
int bs2b_is_clear(struct bs2b *bs2b)
|
||||
{
|
||||
int loopv = sizeof(bs2b->last_sample);
|
||||
|
||||
while (loopv)
|
||||
{
|
||||
if (((char *)&bs2b->last_sample)[--loopv] != 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
} /* bs2b_is_clear */
|
||||
|
||||
void bs2b_cross_feed(struct bs2b *bs2b, float *sample)
|
||||
{
|
||||
/* Lowpass filter */
|
||||
bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]);
|
||||
bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]);
|
||||
|
||||
/* Highboost filter */
|
||||
bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]);
|
||||
bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]);
|
||||
bs2b->last_sample.asis[0] = sample[0];
|
||||
bs2b->last_sample.asis[1] = sample[1];
|
||||
|
||||
/* Crossfeed */
|
||||
sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1];
|
||||
sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0];
|
||||
|
||||
/* Bass boost cause allpass attenuation */
|
||||
sample[0] *= bs2b->gain;
|
||||
sample[1] *= bs2b->gain;
|
||||
|
||||
/* Clipping of overloaded samples */
|
||||
#if 0
|
||||
if (sample[0] > 1.0)
|
||||
sample[0] = 1.0;
|
||||
if (sample[0] < -1.0)
|
||||
sample[0] = -1.0;
|
||||
if (sample[1] > 1.0)
|
||||
sample[1] = 1.0;
|
||||
if (sample[1] < -1.0)
|
||||
sample[1] = -1.0;
|
||||
#endif
|
||||
} /* bs2b_cross_feed */
|
||||
/*-
|
||||
* Copyright (c) 2005 Boris Mikhaylov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bs2b.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* Set up all data. */
|
||||
static void init(struct bs2b *bs2b)
|
||||
{
|
||||
float Fc_lo, Fc_hi;
|
||||
float G_lo, G_hi;
|
||||
float x, g;
|
||||
|
||||
bs2b->srate = clampi(bs2b->srate, 2000, 192000);
|
||||
|
||||
switch(bs2b->level)
|
||||
{
|
||||
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
|
||||
Fc_lo = 360.0f;
|
||||
Fc_hi = 501.0f;
|
||||
G_lo = 0.398107170553497f;
|
||||
G_hi = 0.205671765275719f;
|
||||
break;
|
||||
|
||||
case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
|
||||
Fc_lo = 500.0f;
|
||||
Fc_hi = 711.0f;
|
||||
G_lo = 0.459726988530872f;
|
||||
G_hi = 0.228208484414988f;
|
||||
break;
|
||||
|
||||
case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
|
||||
Fc_lo = 700.0f;
|
||||
Fc_hi = 1021.0f;
|
||||
G_lo = 0.530884444230988f;
|
||||
G_hi = 0.250105790667544f;
|
||||
break;
|
||||
|
||||
case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
|
||||
Fc_lo = 360.0f;
|
||||
Fc_hi = 494.0f;
|
||||
G_lo = 0.316227766016838f;
|
||||
G_hi = 0.168236228897329f;
|
||||
break;
|
||||
|
||||
case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
|
||||
Fc_lo = 500.0f;
|
||||
Fc_hi = 689.0f;
|
||||
G_lo = 0.354813389233575f;
|
||||
G_hi = 0.187169483835901f;
|
||||
break;
|
||||
|
||||
default: /* High easy crossfeed level */
|
||||
bs2b->level = BS2B_HIGH_ECLEVEL;
|
||||
|
||||
Fc_lo = 700.0f;
|
||||
Fc_hi = 975.0f;
|
||||
G_lo = 0.398107170553497f;
|
||||
G_hi = 0.205671765275719f;
|
||||
break;
|
||||
} /* switch */
|
||||
|
||||
g = 1.0f / (1.0f - G_hi + G_lo);
|
||||
|
||||
/* $fc = $Fc / $s;
|
||||
* $d = 1 / 2 / pi / $fc;
|
||||
* $x = exp(-1 / $d);
|
||||
*/
|
||||
x = expf(-2.0f * F_PI * Fc_lo / bs2b->srate);
|
||||
bs2b->b1_lo = x;
|
||||
bs2b->a0_lo = G_lo * (1.0f - x) * g;
|
||||
|
||||
x = expf(-2.0f * F_PI * Fc_hi / bs2b->srate);
|
||||
bs2b->b1_hi = x;
|
||||
bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g;
|
||||
bs2b->a1_hi = -x * g;
|
||||
} /* init */
|
||||
|
||||
/* Exported functions.
|
||||
* See descriptions in "bs2b.h"
|
||||
*/
|
||||
|
||||
void bs2b_set_level(struct bs2b *bs2b, int level)
|
||||
{
|
||||
if(level == bs2b->level)
|
||||
return;
|
||||
bs2b->level = level;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_level */
|
||||
|
||||
int bs2b_get_level(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->level;
|
||||
} /* bs2b_get_level */
|
||||
|
||||
void bs2b_set_srate(struct bs2b *bs2b, int srate)
|
||||
{
|
||||
if (srate == bs2b->srate)
|
||||
return;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_srate */
|
||||
|
||||
int bs2b_get_srate(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->srate;
|
||||
} /* bs2b_get_srate */
|
||||
|
||||
void bs2b_clear(struct bs2b *bs2b)
|
||||
{
|
||||
memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample));
|
||||
} /* bs2b_clear */
|
||||
|
||||
extern inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict samples);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef AL_COMPAT_H
|
||||
#define AL_COMPAT_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
WCHAR *strdupW(const WCHAR *str);
|
||||
|
||||
/* Opens a file with standard I/O. The filename is expected to be UTF-8. */
|
||||
FILE *al_fopen(const char *fname, const char *mode);
|
||||
|
||||
#define HAVE_DYNLOAD 1
|
||||
|
||||
#else
|
||||
|
||||
#define al_fopen fopen
|
||||
|
||||
#if defined(HAVE_DLFCN_H) && !defined(IN_IDE_PARSER)
|
||||
#define HAVE_DYNLOAD 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
void *LoadLib(const char *name);
|
||||
void CloseLib(void *handle);
|
||||
void *GetSymbol(void *handle, const char *name);
|
||||
#endif
|
||||
|
||||
#endif /* AL_COMPAT_H */
|
||||
-551
@@ -1,551 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define _WIN32_WINNT 0x0500
|
||||
#define INITGUID
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <dsound.h>
|
||||
#include <mmreg.h>
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||
#include <ks.h>
|
||||
#include <ksmedia.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#ifndef DSSPEAKER_5POINT1
|
||||
#define DSSPEAKER_5POINT1 6
|
||||
#endif
|
||||
#ifndef DSSPEAKER_7POINT1
|
||||
#define DSSPEAKER_7POINT1 7
|
||||
#endif
|
||||
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
|
||||
|
||||
static void *ds_handle;
|
||||
static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
|
||||
static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
|
||||
|
||||
|
||||
typedef struct {
|
||||
// DirectSound Playback Device
|
||||
LPDIRECTSOUND lpDS;
|
||||
LPDIRECTSOUNDBUFFER DSpbuffer;
|
||||
LPDIRECTSOUNDBUFFER DSsbuffer;
|
||||
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
} DSoundData;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ALCchar *name;
|
||||
GUID guid;
|
||||
} DevMap;
|
||||
|
||||
static const ALCchar dsDevice[] = "DirectSound Software";
|
||||
static DevMap *DeviceList;
|
||||
static ALuint NumDevices;
|
||||
|
||||
|
||||
static ALuint DSoundProc(ALvoid *ptr)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)ptr;
|
||||
DSoundData *pData = (DSoundData*)pDevice->ExtraData;
|
||||
DSBCAPS DSBCaps;
|
||||
DWORD LastCursor = 0;
|
||||
DWORD PlayCursor;
|
||||
VOID *WritePtr1, *WritePtr2;
|
||||
DWORD WriteCnt1, WriteCnt2;
|
||||
DWORD FrameSize;
|
||||
DWORD FragSize;
|
||||
DWORD avail;
|
||||
HRESULT err;
|
||||
|
||||
memset(&DSBCaps, 0, sizeof(DSBCaps));
|
||||
DSBCaps.dwSize = sizeof(DSBCaps);
|
||||
err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
|
||||
if(FAILED(err))
|
||||
{
|
||||
AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
|
||||
aluHandleDisconnect(pDevice);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FrameSize = aluChannelsFromFormat(pDevice->Format) *
|
||||
aluBytesFromFormat(pDevice->Format);
|
||||
FragSize = pDevice->UpdateSize * FrameSize;
|
||||
|
||||
IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
|
||||
while(!pData->killNow)
|
||||
{
|
||||
// Get current play and write cursors
|
||||
IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
|
||||
avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
|
||||
|
||||
if(avail < FragSize)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
avail -= avail%FragSize;
|
||||
|
||||
// Lock output buffer
|
||||
WriteCnt1 = 0;
|
||||
WriteCnt2 = 0;
|
||||
err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
|
||||
|
||||
// If the buffer is lost, restore it, play and lock
|
||||
if(err == DSERR_BUFFERLOST)
|
||||
{
|
||||
err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
|
||||
if(SUCCEEDED(err))
|
||||
err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
|
||||
if(SUCCEEDED(err))
|
||||
err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
|
||||
}
|
||||
|
||||
// Successfully locked the output buffer
|
||||
if(SUCCEEDED(err))
|
||||
{
|
||||
// If we have an active context, mix data directly into output buffer otherwise fill with silence
|
||||
aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
|
||||
aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
|
||||
|
||||
// Unlock output buffer only when successfully locked
|
||||
IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
|
||||
}
|
||||
else
|
||||
AL_PRINT("Buffer lock error: %#lx\n", err);
|
||||
|
||||
// Update old write cursor location
|
||||
LastCursor += WriteCnt1+WriteCnt2;
|
||||
LastCursor %= DSBCaps.dwBufferBytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
DSoundData *pData = NULL;
|
||||
LPGUID guid = NULL;
|
||||
HRESULT hr;
|
||||
|
||||
if(ds_handle == NULL)
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = dsDevice;
|
||||
else if(strcmp(deviceName, dsDevice) != 0)
|
||||
{
|
||||
ALuint i;
|
||||
for(i = 0;i < NumDevices;i++)
|
||||
{
|
||||
if(strcmp(deviceName, DeviceList[i].name) == 0)
|
||||
{
|
||||
guid = &DeviceList[i].guid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == NumDevices)
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
//Initialise requested device
|
||||
|
||||
pData = calloc(1, sizeof(DSoundData));
|
||||
if(!pData)
|
||||
{
|
||||
SetALCError(ALC_OUT_OF_MEMORY);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
//DirectSound Init code
|
||||
hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
if(pData->lpDS)
|
||||
IDirectSound_Release(pData->lpDS);
|
||||
free(pData);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = pData;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void DSoundClosePlayback(ALCdevice *device)
|
||||
{
|
||||
DSoundData *pData = device->ExtraData;
|
||||
|
||||
IDirectSound_Release(pData->lpDS);
|
||||
free(pData);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean DSoundResetPlayback(ALCdevice *device)
|
||||
{
|
||||
DSoundData *pData = (DSoundData*)device->ExtraData;
|
||||
DSBUFFERDESC DSBDescription;
|
||||
WAVEFORMATEXTENSIBLE OutputType;
|
||||
DWORD frameSize = 0;
|
||||
ALenum format = 0;
|
||||
DWORD speakers;
|
||||
HRESULT hr;
|
||||
|
||||
memset(&OutputType, 0, sizeof(OutputType));
|
||||
|
||||
hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
|
||||
if(SUCCEEDED(hr) && *(GetConfigValue(NULL, "format", "")) != 0)
|
||||
{
|
||||
if(aluChannelsFromFormat(device->Format) == 1)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
|
||||
else if(aluChannelsFromFormat(device->Format) == 2)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
|
||||
else if(aluChannelsFromFormat(device->Format) == 4)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
|
||||
else if(aluChannelsFromFormat(device->Format) == 6)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
|
||||
else if(aluChannelsFromFormat(device->Format) == 8)
|
||||
speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
|
||||
else
|
||||
{
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
speakers = DSSPEAKER_CONFIG(speakers);
|
||||
if(speakers == DSSPEAKER_MONO)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_MONO8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_MONO16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_MONO_FLOAT32;
|
||||
}
|
||||
else if(speakers == DSSPEAKER_STEREO)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_STEREO8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_STEREO16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_STEREO_FLOAT32;
|
||||
}
|
||||
else if(speakers == DSSPEAKER_QUAD)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_QUAD8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_QUAD16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_QUAD32;
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
}
|
||||
else if(speakers == DSSPEAKER_5POINT1)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_51CHN8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_51CHN16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_51CHN32;
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT;
|
||||
}
|
||||
else if(speakers == DSSPEAKER_7POINT1)
|
||||
{
|
||||
if(aluBytesFromFormat(device->Format) == 1)
|
||||
format = AL_FORMAT_71CHN8;
|
||||
else if(aluBytesFromFormat(device->Format) == 2)
|
||||
format = AL_FORMAT_71CHN16;
|
||||
else if(aluBytesFromFormat(device->Format) == 4)
|
||||
format = AL_FORMAT_71CHN32;
|
||||
OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT;
|
||||
}
|
||||
else
|
||||
format = device->Format;
|
||||
frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
|
||||
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
OutputType.Format.nChannels = aluChannelsFromFormat(format);
|
||||
OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
|
||||
OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
|
||||
OutputType.Format.nSamplesPerSec = device->Frequency;
|
||||
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
|
||||
OutputType.Format.cbSize = 0;
|
||||
}
|
||||
|
||||
if(OutputType.Format.nChannels > 2 || OutputType.Format.wBitsPerSample > 16)
|
||||
{
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
||||
OutputType.Format.cbSize = 22;
|
||||
if(OutputType.Format.wBitsPerSample == 32)
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
else
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
|
||||
DSBDescription.dwSize=sizeof(DSBUFFERDESC);
|
||||
DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
|
||||
hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
|
||||
}
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
|
||||
DSBDescription.dwSize=sizeof(DSBUFFERDESC);
|
||||
DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
|
||||
DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * frameSize;
|
||||
DSBDescription.lpwfxFormat=&OutputType.Format;
|
||||
hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
|
||||
}
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
device->ExtraData = pData;
|
||||
pData->thread = StartThread(DSoundProc, device);
|
||||
if(!pData->thread)
|
||||
hr = E_FAIL;
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
if (pData->DSsbuffer)
|
||||
IDirectSoundBuffer_Release(pData->DSsbuffer);
|
||||
pData->DSsbuffer = NULL;
|
||||
if (pData->DSpbuffer)
|
||||
IDirectSoundBuffer_Release(pData->DSpbuffer);
|
||||
pData->DSpbuffer = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Format = format;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void DSoundStopPlayback(ALCdevice *device)
|
||||
{
|
||||
DSoundData *pData = device->ExtraData;
|
||||
|
||||
if(!pData->thread)
|
||||
return;
|
||||
|
||||
pData->killNow = 1;
|
||||
StopThread(pData->thread);
|
||||
pData->thread = NULL;
|
||||
|
||||
IDirectSoundBuffer_Release(pData->DSsbuffer);
|
||||
pData->DSsbuffer = NULL;
|
||||
if (pData->DSpbuffer)
|
||||
IDirectSoundBuffer_Release(pData->DSpbuffer);
|
||||
pData->DSpbuffer = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)deviceName;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void DSoundCloseCapture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void DSoundStartCapture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void DSoundStopCapture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)pBuffer;
|
||||
(void)lSamples;
|
||||
}
|
||||
|
||||
static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs DSoundFuncs = {
|
||||
DSoundOpenPlayback,
|
||||
DSoundClosePlayback,
|
||||
DSoundResetPlayback,
|
||||
DSoundStopPlayback,
|
||||
DSoundOpenCapture,
|
||||
DSoundCloseCapture,
|
||||
DSoundStartCapture,
|
||||
DSoundStopCapture,
|
||||
DSoundCaptureSamples,
|
||||
DSoundAvailableSamples
|
||||
};
|
||||
|
||||
static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
|
||||
{
|
||||
(void)data;
|
||||
(void)drvname;
|
||||
|
||||
if(guid)
|
||||
{
|
||||
char str[128];
|
||||
void *temp;
|
||||
|
||||
temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
|
||||
if(temp)
|
||||
{
|
||||
DeviceList = temp;
|
||||
|
||||
snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
|
||||
AppendAllDeviceList(str);
|
||||
|
||||
DeviceList[NumDevices].name = strdup(str);
|
||||
DeviceList[NumDevices].guid = *guid;
|
||||
NumDevices++;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void alcDSoundInit(BackendFuncs *FuncList)
|
||||
{
|
||||
if(FuncList) *FuncList = DSoundFuncs;
|
||||
|
||||
#ifdef _WIN32
|
||||
ds_handle = LoadLibraryA("dsound.dll");
|
||||
if(ds_handle == NULL)
|
||||
{
|
||||
AL_PRINT("Failed to load dsound.dll\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
|
||||
if(p##f == NULL) \
|
||||
{ \
|
||||
FreeLibrary(ds_handle); \
|
||||
ds_handle = NULL; \
|
||||
AL_PRINT("Could not load %s from dsound.dll\n", #f); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
ds_handle = (void*)0xDEADBEEF;
|
||||
#define LOAD_FUNC(f) p##f = f
|
||||
#endif
|
||||
|
||||
LOAD_FUNC(DirectSoundCreate);
|
||||
LOAD_FUNC(DirectSoundEnumerateA);
|
||||
#undef LOAD_FUNC
|
||||
}
|
||||
|
||||
void alcDSoundDeinit(void)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < NumDevices;++i)
|
||||
free(DeviceList[i].name);
|
||||
free(DeviceList);
|
||||
DeviceList = NULL;
|
||||
NumDevices = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(ds_handle)
|
||||
FreeLibrary(ds_handle);
|
||||
#endif
|
||||
ds_handle = NULL;
|
||||
}
|
||||
|
||||
void alcDSoundProbe(int type)
|
||||
{
|
||||
if(!ds_handle) alcDSoundInit(NULL);
|
||||
if(!ds_handle) return;
|
||||
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(dsDevice);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
{
|
||||
HRESULT hr;
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < NumDevices;++i)
|
||||
free(DeviceList[i].name);
|
||||
free(DeviceList);
|
||||
DeviceList = NULL;
|
||||
NumDevices = 0;
|
||||
|
||||
hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
|
||||
if(FAILED(hr))
|
||||
AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "alu.h"
|
||||
#include "alFilter.h"
|
||||
#include "alError.h"
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
|
||||
|
||||
/* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
|
||||
* or down depending on the input signal, and a resonant peak at the cutoff.
|
||||
*
|
||||
* Currently, we assume a cutoff frequency range of 500hz (no amplitude) to
|
||||
* 3khz (peak gain). Peak gain is assumed to be in normalized scale.
|
||||
*/
|
||||
|
||||
typedef struct ALautowahState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat Resonance;
|
||||
ALfloat PeakGain;
|
||||
ALfloat GainCtrl;
|
||||
ALfloat Frequency;
|
||||
|
||||
/* Samples processing */
|
||||
ALfilterState LowPass;
|
||||
} ALautowahState;
|
||||
|
||||
static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
|
||||
{
|
||||
}
|
||||
|
||||
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
|
||||
{
|
||||
state->Frequency = (ALfloat)device->Frequency;
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
|
||||
{
|
||||
ALfloat attackTime, releaseTime;
|
||||
ALfloat gain;
|
||||
|
||||
attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
|
||||
releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
|
||||
|
||||
state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime);
|
||||
state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime);
|
||||
state->PeakGain = slot->EffectProps.Autowah.PeakGain;
|
||||
state->Resonance = slot->EffectProps.Autowah.Resonance;
|
||||
|
||||
gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
|
||||
SetGains(device, gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
ALfloat gain = state->GainCtrl;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
ALfloat smp = SamplesIn[it+base];
|
||||
ALfloat alpha, w0;
|
||||
ALfloat amplitude;
|
||||
ALfloat cutoff;
|
||||
|
||||
/* Similar to compressor, we get the current amplitude of the
|
||||
* incoming signal, and attack or release to reach it. */
|
||||
amplitude = fabsf(smp);
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain*state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain*state->ReleaseRate, amplitude);
|
||||
gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
|
||||
|
||||
/* FIXME: What range does the filter cover? */
|
||||
cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
|
||||
|
||||
/* The code below is like calling ALfilterState_setParams with
|
||||
* ALfilterType_LowPass. However, instead of passing a bandwidth,
|
||||
* we use the resonance property for Q. This also inlines the call.
|
||||
*/
|
||||
w0 = F_2PI * cutoff / state->Frequency;
|
||||
|
||||
/* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
|
||||
* that Q = resonance*0.1. */
|
||||
alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
|
||||
state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f;
|
||||
state->LowPass.b[1] = 1.0f - cosf(w0);
|
||||
state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f;
|
||||
state->LowPass.a[0] = 1.0f + alpha;
|
||||
state->LowPass.a[1] = -2.0f * cosf(w0);
|
||||
state->LowPass.a[2] = 1.0f - alpha;
|
||||
|
||||
state->LowPass.b[2] /= state->LowPass.a[0];
|
||||
state->LowPass.b[1] /= state->LowPass.a[0];
|
||||
state->LowPass.b[0] /= state->LowPass.a[0];
|
||||
state->LowPass.a[2] /= state->LowPass.a[0];
|
||||
state->LowPass.a[1] /= state->LowPass.a[0];
|
||||
state->LowPass.a[0] /= state->LowPass.a[0];
|
||||
|
||||
temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
|
||||
}
|
||||
state->GainCtrl = gain;
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[it];
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
|
||||
|
||||
|
||||
typedef struct ALautowahStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALautowahStateFactory;
|
||||
|
||||
static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALautowahState *state;
|
||||
|
||||
state = ALautowahState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALautowahState, ALeffectState, state);
|
||||
|
||||
state->AttackRate = 1.0f;
|
||||
state->ReleaseRate = 1.0f;
|
||||
state->Resonance = 2.0f;
|
||||
state->PeakGain = 1.0f;
|
||||
state->GainCtrl = 1.0f;
|
||||
|
||||
ALfilterState_clear(&state->LowPass);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
|
||||
{
|
||||
static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALautowah_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Autowah.AttackTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Autowah.ReleaseTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Autowah.Resonance = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Autowah.PeakGain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALautowah_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALautowah_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
*val = props->Autowah.AttackTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
*val = props->Autowah.ReleaseTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
*val = props->Autowah.Resonance;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
*val = props->Autowah.PeakGain;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALautowah_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALautowah);
|
||||
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Mike Gorchak
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
enum ChorusWaveForm {
|
||||
CWF_Triangle = AL_CHORUS_WAVEFORM_TRIANGLE,
|
||||
CWF_Sinusoid = AL_CHORUS_WAVEFORM_SINUSOID
|
||||
};
|
||||
|
||||
typedef struct ALchorusState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer[2];
|
||||
ALuint BufferLength;
|
||||
ALuint offset;
|
||||
ALuint lfo_range;
|
||||
ALfloat lfo_scale;
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ALfloat Gain[2][MaxChannels];
|
||||
|
||||
/* effect parameters */
|
||||
enum ChorusWaveForm waveform;
|
||||
ALint delay;
|
||||
ALfloat depth;
|
||||
ALfloat feedback;
|
||||
} ALchorusState;
|
||||
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state)
|
||||
{
|
||||
free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
}
|
||||
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
|
||||
{
|
||||
ALuint maxlen;
|
||||
ALuint it;
|
||||
|
||||
maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp;
|
||||
|
||||
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
|
||||
if(!temp) return AL_FALSE;
|
||||
state->SampleBuffer[0] = temp;
|
||||
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
|
||||
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
|
||||
for(it = 0;it < state->BufferLength;it++)
|
||||
{
|
||||
state->SampleBuffer[0][it] = 0.0f;
|
||||
state->SampleBuffer[1][it] = 0.0f;
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
|
||||
switch(Slot->EffectProps.Chorus.Waveform)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM_TRIANGLE:
|
||||
state->waveform = CWF_Triangle;
|
||||
break;
|
||||
case AL_CHORUS_WAVEFORM_SINUSOID:
|
||||
state->waveform = CWF_Sinusoid;
|
||||
break;
|
||||
}
|
||||
state->depth = Slot->EffectProps.Chorus.Depth;
|
||||
state->feedback = Slot->EffectProps.Chorus.Feedback;
|
||||
state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency);
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
|
||||
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
|
||||
|
||||
phase = Slot->EffectProps.Chorus.Phase;
|
||||
rate = Slot->EffectProps.Chorus.Rate;
|
||||
if(!(rate > 0.0f))
|
||||
{
|
||||
state->lfo_scale = 0.0f;
|
||||
state->lfo_range = 1;
|
||||
state->lfo_disp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Calculate LFO coefficient */
|
||||
state->lfo_range = fastf2u(frequency/rate + 0.5f);
|
||||
switch(state->waveform)
|
||||
{
|
||||
case CWF_Triangle:
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
state->lfo_scale = F_2PI / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate lfo phase displacement */
|
||||
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
}
|
||||
|
||||
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALchorusState *state)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Func) \
|
||||
static void Process##Func(ALchorusState *state, const ALuint SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
|
||||
{ \
|
||||
const ALuint bufmask = state->BufferLength-1; \
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
|
||||
ALuint offset = state->offset; \
|
||||
const ALfloat feedback = state->feedback; \
|
||||
ALuint it; \
|
||||
\
|
||||
for(it = 0;it < SamplesToDo;it++) \
|
||||
{ \
|
||||
ALint delay_left, delay_right; \
|
||||
Func(&delay_left, &delay_right, offset, state); \
|
||||
\
|
||||
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
|
||||
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
|
||||
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
offset++; \
|
||||
} \
|
||||
state->offset = offset; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Triangle)
|
||||
DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][2];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
case CWF_Triangle:
|
||||
ProcessTriangle(state, td, SamplesIn+base, temps);
|
||||
break;
|
||||
case CWF_Sinusoid:
|
||||
ProcessSinusoid(state, td, SamplesIn+base, temps);
|
||||
break;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
|
||||
|
||||
|
||||
typedef struct ALchorusStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALchorusStateFactory;
|
||||
|
||||
static ALeffectState *ALchorusStateFactory_create(ALchorusStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALchorusState *state;
|
||||
|
||||
state = ALchorusState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALchorusState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = CWF_Triangle;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
|
||||
|
||||
|
||||
ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
|
||||
{
|
||||
static ALchorusStateFactory ChorusFactory = { { GET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Chorus.Waveform = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_PHASE:
|
||||
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Chorus.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALchorus_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Chorus.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Chorus.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Chorus.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Chorus.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALchorus_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
*val = props->Chorus.Waveform;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_PHASE:
|
||||
*val = props->Chorus.Phase;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALchorus_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
*val = props->Chorus.Rate;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
*val = props->Chorus.Depth;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
*val = props->Chorus.Feedback;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
*val = props->Chorus.Delay;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALchorus_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALchorus);
|
||||
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Anis A. Hireche
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "alError.h"
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALcompressorState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
|
||||
/* Effect parameters */
|
||||
ALboolean Enabled;
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat GainCtrl;
|
||||
} ALcompressorState;
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *UNUSED(state))
|
||||
{
|
||||
}
|
||||
|
||||
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
|
||||
{
|
||||
const ALfloat attackTime = device->Frequency * 0.2f; /* 200ms Attack */
|
||||
const ALfloat releaseTime = device->Frequency * 0.4f; /* 400ms Release */
|
||||
|
||||
state->AttackRate = 1.0f / attackTime;
|
||||
state->ReleaseRate = 1.0f / releaseTime;
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat gain;
|
||||
|
||||
state->Enabled = Slot->EffectProps.Compressor.OnOff;
|
||||
|
||||
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
|
||||
SetGains(Device, gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
|
||||
if(state->Enabled)
|
||||
{
|
||||
ALfloat output, smp, amplitude;
|
||||
ALfloat gain = state->GainCtrl;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
smp = SamplesIn[it+base];
|
||||
|
||||
amplitude = fabsf(smp);
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
|
||||
temps[it] = smp * output;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALfloat output, smp, amplitude;
|
||||
ALfloat gain = state->GainCtrl;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
smp = SamplesIn[it+base];
|
||||
|
||||
amplitude = 1.0f;
|
||||
if(amplitude > gain)
|
||||
gain = minf(gain+state->AttackRate, amplitude);
|
||||
else if(amplitude < gain)
|
||||
gain = maxf(gain-state->ReleaseRate, amplitude);
|
||||
output = 1.0f / clampf(gain, 0.5f, 2.0f);
|
||||
|
||||
temps[it] = smp * output;
|
||||
}
|
||||
|
||||
state->GainCtrl = gain;
|
||||
}
|
||||
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[it];
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
|
||||
|
||||
|
||||
typedef struct ALcompressorStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALcompressorStateFactory;
|
||||
|
||||
static ALeffectState *ALcompressorStateFactory_create(ALcompressorStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALcompressorState *state;
|
||||
|
||||
state = ALcompressorState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALcompressorState, ALeffectState, state);
|
||||
|
||||
state->Enabled = AL_TRUE;
|
||||
state->AttackRate = 0.0f;
|
||||
state->ReleaseRate = 0.0f;
|
||||
state->GainCtrl = 1.0f;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALcompressorStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void)
|
||||
{
|
||||
static ALcompressorStateFactory CompressorFactory = { { GET_VTABLE2(ALcompressorStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &CompressorFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Compressor.OnOff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALcompressor_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALcompressor_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALcompressor_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
*val = props->Compressor.OnOff;
|
||||
break;
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALcompressor_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALcompressor_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALcompressor_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALcompressor);
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2011 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALdedicatedState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat gains[MaxChannels];
|
||||
} ALdedicatedState;
|
||||
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *UNUSED(state))
|
||||
{
|
||||
}
|
||||
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
{
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat Gain;
|
||||
ALsizei s;
|
||||
|
||||
Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain;
|
||||
if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
|
||||
else if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
{
|
||||
for(s = 0;s < MaxChannels;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
state->gains[LFE] = Gain;
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
const ALfloat *gains = state->gains;
|
||||
ALuint i, c;
|
||||
|
||||
for(c = 0;c < MaxChannels;c++)
|
||||
{
|
||||
if(!(gains[c] > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
SamplesOut[c][i] = SamplesIn[i] * gains[c];
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
|
||||
|
||||
|
||||
typedef struct ALdedicatedStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALdedicatedStateFactory;
|
||||
|
||||
ALeffectState *ALdedicatedStateFactory_create(ALdedicatedStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdedicatedState *state;
|
||||
ALsizei s;
|
||||
|
||||
state = ALdedicatedState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
|
||||
|
||||
for(s = 0;s < MaxChannels;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
|
||||
|
||||
|
||||
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
|
||||
{
|
||||
static ALdedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALdedicated_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALdedicated_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
if(!(val >= 0.0f && isfinite(val)))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Dedicated.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALdedicated_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALdedicated_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALdedicated_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
*val = props->Dedicated.Gain;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALdedicated_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALdedicated);
|
||||
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Mike Gorchak
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALdistortionState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState lowpass;
|
||||
ALfilterState bandpass;
|
||||
ALfloat attenuation;
|
||||
ALfloat edge_coeff;
|
||||
} ALdistortionState;
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *UNUSED(state))
|
||||
{
|
||||
}
|
||||
|
||||
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
{
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat bandwidth;
|
||||
ALfloat cutoff;
|
||||
ALfloat edge;
|
||||
ALfloat gain;
|
||||
|
||||
/* Store distorted signal attenuation settings */
|
||||
state->attenuation = Slot->EffectProps.Distortion.Gain;
|
||||
|
||||
/* Store waveshaper edge settings */
|
||||
edge = sinf(Slot->EffectProps.Distortion.Edge * (F_PI_2));
|
||||
edge = minf(edge, 0.99f);
|
||||
state->edge_coeff = 2.0f * edge / (1.0f-edge);
|
||||
|
||||
/* Lowpass filter */
|
||||
cutoff = Slot->EffectProps.Distortion.LowpassCutoff;
|
||||
/* Bandwidth value is constant in octaves */
|
||||
bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
|
||||
ALfilterState_setParams(&state->lowpass, ALfilterType_LowPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), bandwidth);
|
||||
|
||||
/* Bandpass filter */
|
||||
cutoff = Slot->EffectProps.Distortion.EQCenter;
|
||||
/* Convert bandwidth in Hz to octaves */
|
||||
bandwidth = Slot->EffectProps.Distortion.EQBandwidth / (cutoff * 0.67f);
|
||||
ALfilterState_setParams(&state->bandpass, ALfilterType_BandPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), bandwidth);
|
||||
|
||||
gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
|
||||
SetGains(Device, gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
const ALfloat fc = state->edge_coeff;
|
||||
float oversample_buffer[64][4];
|
||||
ALuint base;
|
||||
ALuint it;
|
||||
ALuint ot;
|
||||
ALuint kt;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
|
||||
/* Perform 4x oversampling to avoid aliasing. */
|
||||
/* Oversampling greatly improves distortion */
|
||||
/* quality and allows to implement lowpass and */
|
||||
/* bandpass filters using high frequencies, at */
|
||||
/* which classic IIR filters became unstable. */
|
||||
|
||||
/* Fill oversample buffer using zero stuffing */
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
oversample_buffer[it][0] = SamplesIn[it+base];
|
||||
oversample_buffer[it][1] = 0.0f;
|
||||
oversample_buffer[it][2] = 0.0f;
|
||||
oversample_buffer[it][3] = 0.0f;
|
||||
}
|
||||
|
||||
/* First step, do lowpass filtering of original signal, */
|
||||
/* additionally perform buffer interpolation and lowpass */
|
||||
/* cutoff for oversampling (which is fortunately first */
|
||||
/* step of distortion). So combine three operations into */
|
||||
/* the one. */
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
for(ot = 0;ot < 4;ot++)
|
||||
{
|
||||
ALfloat smp;
|
||||
smp = ALfilterState_processSingle(&state->lowpass, oversample_buffer[it][ot]);
|
||||
|
||||
/* Restore signal power by multiplying sample by amount of oversampling */
|
||||
oversample_buffer[it][ot] = smp * 4.0f;
|
||||
}
|
||||
}
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
/* Second step, do distortion using waveshaper function */
|
||||
/* to emulate signal processing during tube overdriving. */
|
||||
/* Three steps of waveshaping are intended to modify */
|
||||
/* waveform without boost/clipping/attenuation process. */
|
||||
for(ot = 0;ot < 4;ot++)
|
||||
{
|
||||
ALfloat smp = oversample_buffer[it][ot];
|
||||
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
|
||||
/* Third step, do bandpass filtering of distorted signal */
|
||||
smp = ALfilterState_processSingle(&state->bandpass, smp);
|
||||
oversample_buffer[it][ot] = smp;
|
||||
}
|
||||
|
||||
/* Fourth step, final, do attenuation and perform decimation, */
|
||||
/* store only one sample out of 4. */
|
||||
temps[it] = oversample_buffer[it][0] * state->attenuation;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[it];
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
|
||||
|
||||
|
||||
typedef struct ALdistortionStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALdistortionStateFactory;
|
||||
|
||||
static ALeffectState *ALdistortionStateFactory_create(ALdistortionStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdistortionState *state;
|
||||
|
||||
state = ALdistortionState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALdistortionState, ALeffectState, state);
|
||||
|
||||
ALfilterState_clear(&state->lowpass);
|
||||
ALfilterState_clear(&state->bandpass);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
|
||||
|
||||
|
||||
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
|
||||
{
|
||||
static ALdistortionStateFactory DistortionFactory = { { GET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALdistortion_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALdistortion_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Distortion.Edge = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Distortion.Gain = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Distortion.LowpassCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Distortion.EQCenter = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Distortion.EQBandwidth = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALdistortion_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALdistortion_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALdistortion_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
*val = props->Distortion.Edge;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
*val = props->Distortion.Gain;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
*val = props->Distortion.LowpassCutoff;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
*val = props->Distortion.EQCenter;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
*val = props->Distortion.EQBandwidth;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALdistortion_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALdistortion);
|
||||
@@ -0,0 +1,293 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALechoState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer;
|
||||
ALuint BufferLength;
|
||||
|
||||
// The echo is two tap. The delay is the number of samples from before the
|
||||
// current offset
|
||||
struct {
|
||||
ALuint delay;
|
||||
} Tap[2];
|
||||
ALuint Offset;
|
||||
/* The panning gains for the two taps */
|
||||
ALfloat Gain[2][MaxChannels];
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
ALfilterState Filter;
|
||||
} ALechoState;
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
{
|
||||
free(state->SampleBuffer);
|
||||
state->SampleBuffer = NULL;
|
||||
}
|
||||
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
{
|
||||
ALuint maxlen, i;
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
|
||||
maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp;
|
||||
|
||||
temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
|
||||
if(!temp) return AL_FALSE;
|
||||
state->SampleBuffer = temp;
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
for(i = 0;i < state->BufferLength;i++)
|
||||
state->SampleBuffer[i] = 0.0f;
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALuint frequency = Device->Frequency;
|
||||
ALfloat lrpan, gain;
|
||||
ALfloat dirGain;
|
||||
|
||||
state->Tap[0].delay = fastf2u(Slot->EffectProps.Echo.Delay * frequency) + 1;
|
||||
state->Tap[1].delay = fastf2u(Slot->EffectProps.Echo.LRDelay * frequency);
|
||||
state->Tap[1].delay += state->Tap[0].delay;
|
||||
|
||||
lrpan = Slot->EffectProps.Echo.Spread;
|
||||
|
||||
state->FeedGain = Slot->EffectProps.Echo.Feedback;
|
||||
|
||||
ALfilterState_setParams(&state->Filter, ALfilterType_HighShelf,
|
||||
1.0f - Slot->EffectProps.Echo.Damping,
|
||||
LOWPASSFREQREF/frequency, 0.0f);
|
||||
|
||||
gain = Slot->Gain;
|
||||
dirGain = fabsf(lrpan);
|
||||
|
||||
/* First tap panning */
|
||||
ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]);
|
||||
|
||||
/* Second tap panning */
|
||||
ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
const ALuint mask = state->BufferLength-1;
|
||||
const ALuint tap1 = state->Tap[0].delay;
|
||||
const ALuint tap2 = state->Tap[1].delay;
|
||||
ALuint offset = state->Offset;
|
||||
ALfloat smp;
|
||||
ALuint base;
|
||||
ALuint i, k;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][2];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
/* First tap */
|
||||
temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
|
||||
/* Second tap */
|
||||
temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
|
||||
|
||||
// Apply damping and feedback gain to the second tap, and mix in the
|
||||
// new sample
|
||||
smp = ALfilterState_processSingle(&state->Filter, temps[i][1]+SamplesIn[i+base]);
|
||||
state->SampleBuffer[offset&mask] = smp * state->FeedGain;
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(k = 0;k < MaxChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][k];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][k];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][i+base] += temps[i][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
|
||||
state->Offset = offset;
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALechoState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
|
||||
|
||||
|
||||
typedef struct ALechoStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALechoStateFactory;
|
||||
|
||||
ALeffectState *ALechoStateFactory_create(ALechoStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALechoState *state;
|
||||
|
||||
state = ALechoState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALechoState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer = NULL;
|
||||
|
||||
state->Tap[0].delay = 0;
|
||||
state->Tap[1].delay = 0;
|
||||
state->Offset = 0;
|
||||
|
||||
ALfilterState_clear(&state->Filter);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALechoStateFactory_getFactory(void)
|
||||
{
|
||||
static ALechoStateFactory EchoFactory = { { GET_VTABLE2(ALechoStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALecho_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALecho_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Echo.Delay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Echo.LRDelay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Echo.Damping = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Echo.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Echo.Spread = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALecho_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALecho_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALecho_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
*val = props->Echo.Delay;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
*val = props->Echo.LRDelay;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
*val = props->Echo.Damping;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
*val = props->Echo.Feedback;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
*val = props->Echo.Spread;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALecho_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALecho);
|
||||
@@ -0,0 +1,334 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Mike Gorchak
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
/* The document "Effects Extension Guide.pdf" says that low and high *
|
||||
* frequencies are cutoff frequencies. This is not fully correct, they *
|
||||
* are corner frequencies for low and high shelf filters. If they were *
|
||||
* just cutoff frequencies, there would be no need in cutoff frequency *
|
||||
* gains, which are present. Documentation for "Creative Proteus X2" *
|
||||
* software describes 4-band equalizer functionality in a much better *
|
||||
* way. This equalizer seems to be a predecessor of OpenAL 4-band *
|
||||
* equalizer. With low and high shelf filters we are able to cutoff *
|
||||
* frequencies below and/or above corner frequencies using attenuation *
|
||||
* gains (below 1.0) and amplify all low and/or high frequencies using *
|
||||
* gains above 1.0. *
|
||||
* *
|
||||
* Low-shelf Low Mid Band High Mid Band High-shelf *
|
||||
* corner center center corner *
|
||||
* frequency frequency frequency frequency *
|
||||
* 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
|
||||
* *
|
||||
* | | | | *
|
||||
* | | | | *
|
||||
* B -----+ /--+--\ /--+--\ +----- *
|
||||
* O |\ | | | | | | /| *
|
||||
* O | \ - | - - | - / | *
|
||||
* S + | \ | | | | | | / | *
|
||||
* T | | | | | | | | | | *
|
||||
* ---------+---------------+------------------+---------------+-------- *
|
||||
* C | | | | | | | | | | *
|
||||
* U - | / | | | | | | \ | *
|
||||
* T | / - | - - | - \ | *
|
||||
* O |/ | | | | | | \| *
|
||||
* F -----+ \--+--/ \--+--/ +----- *
|
||||
* F | | | | *
|
||||
* | | | | *
|
||||
* *
|
||||
* Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
|
||||
* up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
|
||||
* octaves for two mid bands. *
|
||||
* *
|
||||
* Implementation is based on the "Cookbook formulae for audio EQ biquad *
|
||||
* filter coefficients" by Robert Bristow-Johnson *
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
|
||||
|
||||
typedef struct ALequalizerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MaxChannels];
|
||||
|
||||
/* Effect parameters */
|
||||
ALfilterState filter[4];
|
||||
} ALequalizerState;
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *UNUSED(state))
|
||||
{
|
||||
}
|
||||
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
{
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
|
||||
|
||||
SetGains(device, gain, state->Gain);
|
||||
|
||||
/* Calculate coefficients for the each type of filter */
|
||||
ALfilterState_setParams(&state->filter[0], ALfilterType_LowShelf,
|
||||
sqrtf(slot->EffectProps.Equalizer.LowGain),
|
||||
slot->EffectProps.Equalizer.LowCutoff/frequency,
|
||||
0.0f);
|
||||
|
||||
ALfilterState_setParams(&state->filter[1], ALfilterType_Peaking,
|
||||
sqrtf(slot->EffectProps.Equalizer.Mid1Gain),
|
||||
slot->EffectProps.Equalizer.Mid1Center/frequency,
|
||||
slot->EffectProps.Equalizer.Mid1Width);
|
||||
|
||||
ALfilterState_setParams(&state->filter[2], ALfilterType_Peaking,
|
||||
sqrtf(slot->EffectProps.Equalizer.Mid2Gain),
|
||||
slot->EffectProps.Equalizer.Mid2Center/frequency,
|
||||
slot->EffectProps.Equalizer.Mid2Width);
|
||||
|
||||
ALfilterState_setParams(&state->filter[3], ALfilterType_HighShelf,
|
||||
sqrtf(slot->EffectProps.Equalizer.HighGain),
|
||||
slot->EffectProps.Equalizer.HighCutoff/frequency,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
ALuint base;
|
||||
ALuint it;
|
||||
ALuint kt;
|
||||
ALuint ft;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
{
|
||||
ALfloat smp = SamplesIn[base+it];
|
||||
|
||||
for(ft = 0;ft < 4;ft++)
|
||||
smp = ALfilterState_processSingle(&state->filter[ft], smp);
|
||||
|
||||
temps[it] = smp;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[kt];
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][base+it] += gain * temps[it];
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
|
||||
|
||||
|
||||
typedef struct ALequalizerStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALequalizerStateFactory;
|
||||
|
||||
ALeffectState *ALequalizerStateFactory_create(ALequalizerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALequalizerState *state;
|
||||
int it;
|
||||
|
||||
state = ALequalizerState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALequalizerState, ALeffectState, state);
|
||||
|
||||
/* Initialize sample history only on filter creation to avoid */
|
||||
/* sound clicks if filter settings were changed in runtime. */
|
||||
for(it = 0; it < 4; it++)
|
||||
ALfilterState_clear(&state->filter[it]);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
|
||||
{
|
||||
static ALequalizerStateFactory EqualizerFactory = { { GET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALequalizer_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALequalizer_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.LowGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.LowCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.Mid1Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.Mid1Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.Mid1Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.Mid2Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.Mid2Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.Mid2Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.HighGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Equalizer.HighCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALequalizer_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
void ALequalizer_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALequalizer_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
*val = props->Equalizer.LowGain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
*val = props->Equalizer.LowCutoff;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
*val = props->Equalizer.Mid1Gain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
*val = props->Equalizer.Mid1Center;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
*val = props->Equalizer.Mid1Width;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
*val = props->Equalizer.Mid2Gain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
*val = props->Equalizer.Mid2Center;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
*val = props->Equalizer.Mid2Width;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
*val = props->Equalizer.HighGain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
*val = props->Equalizer.HighCutoff;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALequalizer_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALequalizer);
|
||||
@@ -0,0 +1,396 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Mike Gorchak
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
enum FlangerWaveForm {
|
||||
FWF_Triangle = AL_FLANGER_WAVEFORM_TRIANGLE,
|
||||
FWF_Sinusoid = AL_FLANGER_WAVEFORM_SINUSOID
|
||||
};
|
||||
|
||||
typedef struct ALflangerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer[2];
|
||||
ALuint BufferLength;
|
||||
ALuint offset;
|
||||
ALuint lfo_range;
|
||||
ALfloat lfo_scale;
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ALfloat Gain[2][MaxChannels];
|
||||
|
||||
/* effect parameters */
|
||||
enum FlangerWaveForm waveform;
|
||||
ALint delay;
|
||||
ALfloat depth;
|
||||
ALfloat feedback;
|
||||
} ALflangerState;
|
||||
|
||||
static ALvoid ALflangerState_Destruct(ALflangerState *state)
|
||||
{
|
||||
free(state->SampleBuffer[0]);
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
}
|
||||
|
||||
static ALboolean ALflangerState_deviceUpdate(ALflangerState *state, ALCdevice *Device)
|
||||
{
|
||||
ALuint maxlen;
|
||||
ALuint it;
|
||||
|
||||
maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp;
|
||||
|
||||
temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2);
|
||||
if(!temp) return AL_FALSE;
|
||||
state->SampleBuffer[0] = temp;
|
||||
state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen;
|
||||
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
|
||||
for(it = 0;it < state->BufferLength;it++)
|
||||
{
|
||||
state->SampleBuffer[0][it] = 0.0f;
|
||||
state->SampleBuffer[1][it] = 0.0f;
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALflangerState_update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat frequency = (ALfloat)Device->Frequency;
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
|
||||
switch(Slot->EffectProps.Flanger.Waveform)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM_TRIANGLE:
|
||||
state->waveform = FWF_Triangle;
|
||||
break;
|
||||
case AL_FLANGER_WAVEFORM_SINUSOID:
|
||||
state->waveform = FWF_Sinusoid;
|
||||
break;
|
||||
}
|
||||
state->depth = Slot->EffectProps.Flanger.Depth;
|
||||
state->feedback = Slot->EffectProps.Flanger.Feedback;
|
||||
state->delay = fastf2i(Slot->EffectProps.Flanger.Delay * frequency);
|
||||
|
||||
/* Gains for left and right sides */
|
||||
ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
|
||||
ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
|
||||
|
||||
phase = Slot->EffectProps.Flanger.Phase;
|
||||
rate = Slot->EffectProps.Flanger.Rate;
|
||||
if(!(rate > 0.0f))
|
||||
{
|
||||
state->lfo_scale = 0.0f;
|
||||
state->lfo_range = 1;
|
||||
state->lfo_disp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Calculate LFO coefficient */
|
||||
state->lfo_range = fastf2u(frequency/rate + 0.5f);
|
||||
switch(state->waveform)
|
||||
{
|
||||
case FWF_Triangle:
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case FWF_Sinusoid:
|
||||
state->lfo_scale = F_2PI / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate lfo phase displacement */
|
||||
state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Triangle(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = 2.0f - fabsf(2.0f - state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
}
|
||||
|
||||
static inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALuint offset, const ALflangerState *state)
|
||||
{
|
||||
ALfloat lfo_value;
|
||||
|
||||
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_left = fastf2i(lfo_value) + state->delay;
|
||||
|
||||
offset += state->lfo_disp;
|
||||
lfo_value = 1.0f + sinf(state->lfo_scale*(offset%state->lfo_range));
|
||||
lfo_value *= state->depth * state->delay;
|
||||
*delay_right = fastf2i(lfo_value) + state->delay;
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Func) \
|
||||
static void Process##Func(ALflangerState *state, const ALuint SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, ALfloat (*restrict out)[2]) \
|
||||
{ \
|
||||
const ALuint bufmask = state->BufferLength-1; \
|
||||
ALfloat *restrict leftbuf = state->SampleBuffer[0]; \
|
||||
ALfloat *restrict rightbuf = state->SampleBuffer[1]; \
|
||||
ALuint offset = state->offset; \
|
||||
const ALfloat feedback = state->feedback; \
|
||||
ALuint it; \
|
||||
\
|
||||
for(it = 0;it < SamplesToDo;it++) \
|
||||
{ \
|
||||
ALint delay_left, delay_right; \
|
||||
Func(&delay_left, &delay_right, offset, state); \
|
||||
\
|
||||
out[it][0] = leftbuf[(offset-delay_left)&bufmask]; \
|
||||
leftbuf[offset&bufmask] = (out[it][0]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
out[it][1] = rightbuf[(offset-delay_right)&bufmask]; \
|
||||
rightbuf[offset&bufmask] = (out[it][1]+SamplesIn[it]) * feedback; \
|
||||
\
|
||||
offset++; \
|
||||
} \
|
||||
state->offset = offset; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Triangle)
|
||||
DECL_TEMPLATE(Sinusoid)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static ALvoid ALflangerState_process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
ALuint it, kt;
|
||||
ALuint base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat temps[64][2];
|
||||
ALuint td = minu(SamplesToDo-base, 64);
|
||||
|
||||
switch(state->waveform)
|
||||
{
|
||||
case FWF_Triangle:
|
||||
ProcessTriangle(state, td, SamplesIn+base, temps);
|
||||
break;
|
||||
case FWF_Sinusoid:
|
||||
ProcessSinusoid(state, td, SamplesIn+base, temps);
|
||||
break;
|
||||
}
|
||||
|
||||
for(kt = 0;kt < MaxChannels;kt++)
|
||||
{
|
||||
ALfloat gain = state->Gain[0][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][0] * gain;
|
||||
}
|
||||
|
||||
gain = state->Gain[1][kt];
|
||||
if(gain > GAIN_SILENCE_THRESHOLD)
|
||||
{
|
||||
for(it = 0;it < td;it++)
|
||||
SamplesOut[kt][it+base] += temps[it][1] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALflangerState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
|
||||
|
||||
|
||||
typedef struct ALflangerStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALflangerStateFactory;
|
||||
|
||||
ALeffectState *ALflangerStateFactory_create(ALflangerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALflangerState *state;
|
||||
|
||||
state = ALflangerState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALflangerState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer[0] = NULL;
|
||||
state->SampleBuffer[1] = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = FWF_Triangle;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
|
||||
{
|
||||
static ALflangerStateFactory FlangerFactory = { { GET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Waveform = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALflanger_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Flanger.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALflanger_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
*val = props->Flanger.Waveform;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
*val = props->Flanger.Phase;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALflanger_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
*val = props->Flanger.Rate;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
*val = props->Flanger.Depth;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
*val = props->Flanger.Feedback;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
*val = props->Flanger.Delay;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALflanger_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALflanger);
|
||||
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALmodulatorState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
enum {
|
||||
SINUSOID,
|
||||
SAWTOOTH,
|
||||
SQUARE
|
||||
} Waveform;
|
||||
|
||||
ALuint index;
|
||||
ALuint step;
|
||||
|
||||
ALfloat Gain[MaxChannels];
|
||||
|
||||
ALfilterState Filter;
|
||||
} ALmodulatorState;
|
||||
|
||||
#define WAVEFORM_FRACBITS 24
|
||||
#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
|
||||
#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
|
||||
|
||||
static inline ALfloat Sin(ALuint index)
|
||||
{
|
||||
return sinf(index*(F_2PI/WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
|
||||
}
|
||||
|
||||
static inline ALfloat Saw(ALuint index)
|
||||
{
|
||||
return (ALfloat)index / WAVEFORM_FRACONE;
|
||||
}
|
||||
|
||||
static inline ALfloat Square(ALuint index)
|
||||
{
|
||||
return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(func) \
|
||||
static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
|
||||
const ALfloat *restrict SamplesIn, \
|
||||
ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \
|
||||
{ \
|
||||
const ALuint step = state->step; \
|
||||
ALuint index = state->index; \
|
||||
ALuint base; \
|
||||
\
|
||||
for(base = 0;base < SamplesToDo;) \
|
||||
{ \
|
||||
ALfloat temps[64]; \
|
||||
ALuint td = minu(SamplesToDo-base, 64); \
|
||||
ALuint i, k; \
|
||||
\
|
||||
for(i = 0;i < td;i++) \
|
||||
{ \
|
||||
ALfloat samp; \
|
||||
samp = SamplesIn[base+i]; \
|
||||
samp = ALfilterState_processSingle(&state->Filter, samp); \
|
||||
\
|
||||
index += step; \
|
||||
index &= WAVEFORM_FRACMASK; \
|
||||
temps[i] = samp * func(index); \
|
||||
} \
|
||||
\
|
||||
for(k = 0;k < MaxChannels;k++) \
|
||||
{ \
|
||||
ALfloat gain = state->Gain[k]; \
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD)) \
|
||||
continue; \
|
||||
\
|
||||
for(i = 0;i < td;i++) \
|
||||
SamplesOut[k][base+i] += gain * temps[i]; \
|
||||
} \
|
||||
\
|
||||
base += td; \
|
||||
} \
|
||||
state->index = index; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Sin)
|
||||
DECL_TEMPLATE(Saw)
|
||||
DECL_TEMPLATE(Square)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *UNUSED(state))
|
||||
{
|
||||
}
|
||||
|
||||
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *UNUSED(state), ALCdevice *UNUSED(device))
|
||||
{
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALfloat gain, cw, a;
|
||||
|
||||
if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
state->Waveform = SINUSOID;
|
||||
else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
|
||||
state->Waveform = SAWTOOTH;
|
||||
else if(Slot->EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
|
||||
state->Waveform = SQUARE;
|
||||
|
||||
state->step = fastf2u(Slot->EffectProps.Modulator.Frequency*WAVEFORM_FRACONE /
|
||||
Device->Frequency);
|
||||
if(state->step == 0) state->step = 1;
|
||||
|
||||
/* Custom filter coeffs, which match the old version instead of a low-shelf. */
|
||||
cw = cosf(F_2PI * Slot->EffectProps.Modulator.HighPassCutoff / Device->Frequency);
|
||||
a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
|
||||
|
||||
state->Filter.b[0] = a;
|
||||
state->Filter.b[1] = -a;
|
||||
state->Filter.b[2] = 0.0f;
|
||||
state->Filter.a[0] = 1.0f;
|
||||
state->Filter.a[1] = -a;
|
||||
state->Filter.a[2] = 0.0f;
|
||||
|
||||
gain = sqrtf(1.0f/Device->NumChan) * Slot->Gain;
|
||||
SetGains(Device, gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
|
||||
{
|
||||
switch(state->Waveform)
|
||||
{
|
||||
case SINUSOID:
|
||||
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
break;
|
||||
|
||||
case SAWTOOTH:
|
||||
ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
break;
|
||||
|
||||
case SQUARE:
|
||||
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
|
||||
|
||||
|
||||
typedef struct ALmodulatorStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALmodulatorStateFactory;
|
||||
|
||||
static ALeffectState *ALmodulatorStateFactory_create(ALmodulatorStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALmodulatorState *state;
|
||||
|
||||
state = ALmodulatorState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
|
||||
|
||||
state->index = 0;
|
||||
state->step = 1;
|
||||
|
||||
ALfilterState_clear(&state->Filter);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
|
||||
{
|
||||
static ALmodulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Modulator.Frequency = val;
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Modulator.HighPassCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALmodulator_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
ALmodulator_setParamf(effect, context, param, (ALfloat)val);
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
props->Modulator.Waveform = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALmodulator_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = (ALint)props->Modulator.Frequency;
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = (ALint)props->Modulator.HighPassCutoff;
|
||||
break;
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
*val = props->Modulator.Waveform;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALmodulator_getParami(effect, context, param, vals);
|
||||
}
|
||||
void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = props->Modulator.Frequency;
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = props->Modulator.HighPassCutoff;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALmodulator_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALmodulator);
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
|
||||
|
||||
typedef struct ALnullState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
} ALnullState;
|
||||
|
||||
|
||||
/* This destructs (not free!) the effect state. It's called only when the
|
||||
* effect slot is no longer used.
|
||||
*/
|
||||
static ALvoid ALnullState_Destruct(ALnullState* UNUSED(state))
|
||||
{
|
||||
}
|
||||
|
||||
/* This updates the device-dependant effect state. This is called on
|
||||
* initialization and any time the device parameters (eg. playback frequency,
|
||||
* format) have been changed.
|
||||
*/
|
||||
static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice* UNUSED(device))
|
||||
{
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
/* This updates the effect state. This is called any time the effect is
|
||||
* (re)loaded into a slot.
|
||||
*/
|
||||
static ALvoid ALnullState_update(ALnullState* UNUSED(state), ALCdevice* UNUSED(device), const ALeffectslot* UNUSED(slot))
|
||||
{
|
||||
}
|
||||
|
||||
/* This processes the effect state, for the given number of samples from the
|
||||
* input to the output buffer. The result should be added to the output buffer,
|
||||
* not replace it.
|
||||
*/
|
||||
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALuint UNUSED(samplesToDo), const ALfloat *restrict UNUSED(samplesIn), ALfloat (*restrict samplesOut)[BUFFERSIZE])
|
||||
{
|
||||
/* NOTE: Couldn't use the UNUSED macro on samplesOut due to the way GCC's
|
||||
* __attribute__ declaration interacts with the parenthesis. */
|
||||
(void)samplesOut;
|
||||
}
|
||||
|
||||
/* This allocates memory to store the object, before it gets constructed.
|
||||
* DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
|
||||
*/
|
||||
static void *ALnullState_New(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
/* This frees the memory used by the object, after it has been destructed.
|
||||
* DECLARE_DEFAULT_ALLOCATORS can be used to declate a default method.
|
||||
*/
|
||||
static void ALnullState_Delete(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/* Define the forwards and the ALeffectState vtable for this type. */
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
|
||||
|
||||
|
||||
typedef struct ALnullStateFactory {
|
||||
DERIVE_FROM_TYPE(ALeffectStateFactory);
|
||||
} ALnullStateFactory;
|
||||
|
||||
/* Creates ALeffectState objects of the appropriate type. */
|
||||
ALeffectState *ALnullStateFactory_create(ALnullStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALnullState *state;
|
||||
|
||||
state = ALnullState_New(sizeof(*state));
|
||||
if(!state) return NULL;
|
||||
/* Set vtables for inherited types. */
|
||||
SET_VTABLE2(ALnullState, ALeffectState, state);
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
/* Define the ALeffectStateFactory vtable for this type. */
|
||||
DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALnullStateFactory);
|
||||
|
||||
ALeffectStateFactory *ALnullStateFactory_getFactory(void)
|
||||
{
|
||||
static ALnullStateFactory NullFactory = { { GET_VTABLE2(ALnullStateFactory, ALeffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(ALeffectStateFactory, &NullFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALnull_setParami(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamiv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamf(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamfv(ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
|
||||
void ALnull_getParami(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamiv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamf(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamfv(const ALeffect* UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALnull);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
#ifndef AL_EVTQUEUE_H
|
||||
#define AL_EVTQUEUE_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct MidiEvent {
|
||||
ALuint64 time;
|
||||
ALuint event;
|
||||
union {
|
||||
ALuint val[2];
|
||||
struct {
|
||||
ALvoid *data;
|
||||
ALsizei size;
|
||||
} sysex;
|
||||
} param;
|
||||
} MidiEvent;
|
||||
|
||||
typedef struct EvtQueue {
|
||||
MidiEvent *events;
|
||||
ALsizei pos;
|
||||
ALsizei size;
|
||||
ALsizei maxsize;
|
||||
} EvtQueue;
|
||||
|
||||
void InitEvtQueue(EvtQueue *queue);
|
||||
void ResetEvtQueue(EvtQueue *queue);
|
||||
ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt);
|
||||
|
||||
#endif /* AL_EVTQUEUE_H */
|
||||
+814
@@ -0,0 +1,814 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2011 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __MINGW32__
|
||||
#define _WIN32_IE 0x501
|
||||
#else
|
||||
#define _WIN32_IE 0x400
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifndef AL_NO_UID_DEFS
|
||||
#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
|
||||
#define INITGUID
|
||||
#include <windows.h>
|
||||
#ifdef HAVE_GUIDDEF_H
|
||||
#include <guiddef.h>
|
||||
#else
|
||||
#include <initguid.h>
|
||||
#endif
|
||||
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
|
||||
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
|
||||
|
||||
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
|
||||
|
||||
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
|
||||
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
|
||||
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
|
||||
DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
|
||||
|
||||
#ifdef HAVE_MMDEVAPI
|
||||
#include <devpropdef.h>
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
|
||||
#endif
|
||||
#endif
|
||||
#endif /* AL_NO_UID_DEFS */
|
||||
|
||||
#ifdef HAVE_DLFCN_H
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#ifdef HAVE_INTRIN_H
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#ifdef HAVE_CPUID_H
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_SYSCONF_H
|
||||
#include <sys/sysconf.h>
|
||||
#endif
|
||||
#ifdef HAVE_FLOAT_H
|
||||
#include <float.h>
|
||||
#endif
|
||||
#ifdef HAVE_IEEEFP_H
|
||||
#include <ieeefp.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32_IE
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "atomic.h"
|
||||
#include "uintmap.h"
|
||||
#include "vector.h"
|
||||
#include "alstring.h"
|
||||
#include "compat.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
extern inline ALuint NextPowerOf2(ALuint value);
|
||||
extern inline ALint fastf2i(ALfloat f);
|
||||
extern inline ALuint fastf2u(ALfloat f);
|
||||
|
||||
|
||||
ALuint CPUCapFlags = 0;
|
||||
|
||||
|
||||
void FillCPUCaps(ALuint capfilter)
|
||||
{
|
||||
ALuint caps = 0;
|
||||
|
||||
/* FIXME: We really should get this for all available CPUs in case different
|
||||
* CPUs have different caps (is that possible on one machine?). */
|
||||
#if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
|
||||
defined(_M_IX86) || defined(_M_X64))
|
||||
union {
|
||||
unsigned int regs[4];
|
||||
char str[sizeof(unsigned int[4])];
|
||||
} cpuinf[3];
|
||||
|
||||
if(!__get_cpuid(0, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
ERR("Failed to get CPUID\n");
|
||||
else
|
||||
{
|
||||
unsigned int maxfunc = cpuinf[0].regs[0];
|
||||
unsigned int maxextfunc = 0;
|
||||
|
||||
if(__get_cpuid(0x80000000, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
maxextfunc = cpuinf[0].regs[0];
|
||||
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
|
||||
|
||||
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
|
||||
if(maxextfunc >= 0x80000004 &&
|
||||
__get_cpuid(0x80000002, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]) &&
|
||||
__get_cpuid(0x80000003, &cpuinf[1].regs[0], &cpuinf[1].regs[1], &cpuinf[1].regs[2], &cpuinf[1].regs[3]) &&
|
||||
__get_cpuid(0x80000004, &cpuinf[2].regs[0], &cpuinf[2].regs[1], &cpuinf[2].regs[2], &cpuinf[2].regs[3]))
|
||||
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
|
||||
|
||||
if(maxfunc >= 1 &&
|
||||
__get_cpuid(1, &cpuinf[0].regs[0], &cpuinf[0].regs[1], &cpuinf[0].regs[2], &cpuinf[0].regs[3]))
|
||||
{
|
||||
if((cpuinf[0].regs[3]&(1<<25)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE;
|
||||
if((cpuinf[0].regs[3]&(1<<26)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
|
||||
defined(_M_IX86) || defined(_M_X64))
|
||||
union {
|
||||
int regs[4];
|
||||
char str[sizeof(int[4])];
|
||||
} cpuinf[3];
|
||||
|
||||
(__cpuid)(cpuinf[0].regs, 0);
|
||||
if(cpuinf[0].regs[0] == 0)
|
||||
ERR("Failed to get CPUID\n");
|
||||
else
|
||||
{
|
||||
unsigned int maxfunc = cpuinf[0].regs[0];
|
||||
unsigned int maxextfunc;
|
||||
|
||||
(__cpuid)(cpuinf[0].regs, 0x80000000);
|
||||
maxextfunc = cpuinf[0].regs[0];
|
||||
|
||||
TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc, maxextfunc);
|
||||
|
||||
TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf[0].str+4, cpuinf[0].str+12, cpuinf[0].str+8);
|
||||
if(maxextfunc >= 0x80000004)
|
||||
{
|
||||
(__cpuid)(cpuinf[0].regs, 0x80000002);
|
||||
(__cpuid)(cpuinf[1].regs, 0x80000003);
|
||||
(__cpuid)(cpuinf[2].regs, 0x80000004);
|
||||
TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf[0].str, cpuinf[1].str, cpuinf[2].str);
|
||||
}
|
||||
|
||||
if(maxfunc >= 1)
|
||||
{
|
||||
(__cpuid)(cpuinf[0].regs, 1);
|
||||
if((cpuinf[0].regs[3]&(1<<25)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE;
|
||||
if((cpuinf[0].regs[3]&(1<<26)))
|
||||
{
|
||||
caps |= CPU_CAP_SSE2;
|
||||
if((cpuinf[0].regs[2]&(1<<19)))
|
||||
caps |= CPU_CAP_SSE4_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Assume support for whatever's supported if we can't check for it */
|
||||
#if defined(HAVE_SSE4_1)
|
||||
#warning "Assuming SSE 4.1 run-time support!"
|
||||
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1;
|
||||
#elif defined(HAVE_SSE2)
|
||||
#warning "Assuming SSE 2 run-time support!"
|
||||
capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
|
||||
#elif defined(HAVE_SSE)
|
||||
#warning "Assuming SSE run-time support!"
|
||||
capfilter |= CPU_CAP_SSE;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
/* Assume Neon support if compiled with it */
|
||||
caps |= CPU_CAP_NEON;
|
||||
#endif
|
||||
|
||||
TRACE("Extensions:%s%s%s%s%s\n",
|
||||
((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""),
|
||||
((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""),
|
||||
((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""),
|
||||
((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +Neon" : " -Neon") : ""),
|
||||
((!capfilter) ? " -none-" : "")
|
||||
);
|
||||
CPUCapFlags = caps & capfilter;
|
||||
}
|
||||
|
||||
|
||||
void *al_malloc(size_t alignment, size_t size)
|
||||
{
|
||||
#if defined(HAVE_ALIGNED_ALLOC)
|
||||
size = (size+(alignment-1))&~(alignment-1);
|
||||
return aligned_alloc(alignment, size);
|
||||
#elif defined(HAVE_POSIX_MEMALIGN)
|
||||
void *ret;
|
||||
if(posix_memalign(&ret, alignment, size) == 0)
|
||||
return ret;
|
||||
return NULL;
|
||||
#elif defined(HAVE__ALIGNED_MALLOC)
|
||||
return _aligned_malloc(size, alignment);
|
||||
#else
|
||||
char *ret = malloc(size+alignment);
|
||||
if(ret != NULL)
|
||||
{
|
||||
*(ret++) = 0x00;
|
||||
while(((ALintptrEXT)ret&(alignment-1)) != 0)
|
||||
*(ret++) = 0x55;
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *al_calloc(size_t alignment, size_t size)
|
||||
{
|
||||
void *ret = al_malloc(alignment, size);
|
||||
if(ret) memset(ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void al_free(void *ptr)
|
||||
{
|
||||
#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN)
|
||||
free(ptr);
|
||||
#elif defined(HAVE__ALIGNED_MALLOC)
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
if(ptr != NULL)
|
||||
{
|
||||
char *finder = ptr;
|
||||
do {
|
||||
--finder;
|
||||
} while(*finder == 0x55);
|
||||
free(finder);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SetMixerFPUMode(FPUCtl *ctl)
|
||||
{
|
||||
#ifdef HAVE_FENV_H
|
||||
fegetenv(STATIC_CAST(fenv_t, ctl));
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__asm__ __volatile__("stmxcsr %0" : "=m" (*&ctl->sse_state));
|
||||
#endif
|
||||
|
||||
#ifdef FE_TOWARDZERO
|
||||
fesetround(FE_TOWARDZERO);
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
{
|
||||
int sseState = ctl->sse_state;
|
||||
sseState |= 0x6000; /* set round-to-zero */
|
||||
sseState |= 0x8000; /* set flush-to-zero */
|
||||
if((CPUCapFlags&CPU_CAP_SSE2))
|
||||
sseState |= 0x0040; /* set denormals-are-zero */
|
||||
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE___CONTROL87_2)
|
||||
|
||||
int mode;
|
||||
__control87_2(0, 0, &ctl->state, NULL);
|
||||
__control87_2(_RC_CHOP, _MCW_RC, &mode, NULL);
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
{
|
||||
__control87_2(0, 0, NULL, &ctl->sse_state);
|
||||
__control87_2(_RC_CHOP|_DN_FLUSH, _MCW_RC|_MCW_DN, NULL, &mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE__CONTROLFP)
|
||||
|
||||
ctl->state = _controlfp(0, 0);
|
||||
(void)_controlfp(_RC_CHOP, _MCW_RC);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RestoreFPUMode(const FPUCtl *ctl)
|
||||
{
|
||||
#ifdef HAVE_FENV_H
|
||||
fesetenv(STATIC_CAST(fenv_t, ctl));
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__asm__ __volatile__("ldmxcsr %0" : : "m" (*&ctl->sse_state));
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE___CONTROL87_2)
|
||||
|
||||
int mode;
|
||||
__control87_2(ctl->state, _MCW_RC, &mode, NULL);
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
__control87_2(ctl->sse_state, _MCW_RC|_MCW_DN, NULL, &mode);
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE__CONTROLFP)
|
||||
|
||||
_controlfp(ctl->state, _MCW_RC);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static WCHAR *FromUTF8(const char *str)
|
||||
{
|
||||
WCHAR *out = NULL;
|
||||
int len;
|
||||
|
||||
if((len=MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) > 0)
|
||||
{
|
||||
out = calloc(sizeof(WCHAR), len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, -1, out, len);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void *LoadLib(const char *name)
|
||||
{
|
||||
HANDLE hdl = NULL;
|
||||
WCHAR *wname;
|
||||
|
||||
wname = FromUTF8(name);
|
||||
if(!wname)
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", name);
|
||||
else
|
||||
{
|
||||
hdl = LoadLibraryW(wname);
|
||||
free(wname);
|
||||
}
|
||||
return hdl;
|
||||
}
|
||||
void CloseLib(void *handle)
|
||||
{ FreeLibrary((HANDLE)handle); }
|
||||
void *GetSymbol(void *handle, const char *name)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = (void*)GetProcAddress((HANDLE)handle, name);
|
||||
if(ret == NULL)
|
||||
ERR("Failed to load %s\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
WCHAR *strdupW(const WCHAR *str)
|
||||
{
|
||||
const WCHAR *n;
|
||||
WCHAR *ret;
|
||||
size_t len;
|
||||
|
||||
n = str;
|
||||
while(*n) n++;
|
||||
len = n - str;
|
||||
|
||||
ret = calloc(sizeof(WCHAR), len+1);
|
||||
if(ret != NULL)
|
||||
memcpy(ret, str, sizeof(WCHAR)*len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
FILE *al_fopen(const char *fname, const char *mode)
|
||||
{
|
||||
WCHAR *wname=NULL, *wmode=NULL;
|
||||
FILE *file = NULL;
|
||||
|
||||
wname = FromUTF8(fname);
|
||||
wmode = FromUTF8(mode);
|
||||
if(!wname)
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
|
||||
else if(!wmode)
|
||||
ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode);
|
||||
else
|
||||
file = _wfopen(wname, wmode);
|
||||
|
||||
free(wname);
|
||||
free(wmode);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HAVE_DLFCN_H
|
||||
|
||||
void *LoadLib(const char *name)
|
||||
{
|
||||
const char *err;
|
||||
void *handle;
|
||||
|
||||
dlerror();
|
||||
handle = dlopen(name, RTLD_NOW);
|
||||
if((err=dlerror()) != NULL)
|
||||
handle = NULL;
|
||||
return handle;
|
||||
}
|
||||
void CloseLib(void *handle)
|
||||
{ dlclose(handle); }
|
||||
void *GetSymbol(void *handle, const char *name)
|
||||
{
|
||||
const char *err;
|
||||
void *sym;
|
||||
|
||||
dlerror();
|
||||
sym = dlsym(handle, name);
|
||||
if((err=dlerror()) != NULL)
|
||||
{
|
||||
WARN("Failed to load %s: %s\n", name, err);
|
||||
sym = NULL;
|
||||
}
|
||||
return sym;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
void al_print(const char *type, const char *func, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(LogFile, "AL lib: %s %s: ", type, func);
|
||||
vfprintf(LogFile, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fflush(LogFile);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static inline int is_slash(int c)
|
||||
{ return (c == '\\' || c == '/'); }
|
||||
|
||||
FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
{
|
||||
static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
|
||||
WCHAR *wname=NULL, *wsubdir=NULL;
|
||||
FILE *f;
|
||||
int i;
|
||||
|
||||
/* If the path is absolute, open it directly. */
|
||||
if(fname[0] != '\0' && fname[1] == ':' && is_slash(fname[2]))
|
||||
{
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If it's relative, try the current directory first before the data directories. */
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
|
||||
wname = FromUTF8(fname);
|
||||
wsubdir = FromUTF8(subdir);
|
||||
if(!wname)
|
||||
ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname);
|
||||
else if(!wsubdir)
|
||||
ERR("Failed to convert UTF-8 subdir: \"%s\"\n", subdir);
|
||||
else for(i = 0;i < 2;i++)
|
||||
{
|
||||
WCHAR buffer[PATH_MAX];
|
||||
size_t len;
|
||||
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) == FALSE)
|
||||
continue;
|
||||
|
||||
len = lstrlenW(buffer);
|
||||
if(len > 0 && is_slash(buffer[len-1]))
|
||||
buffer[--len] = '\0';
|
||||
_snwprintf(buffer+len, PATH_MAX-len, L"/%ls/%ls", wsubdir, wname);
|
||||
len = lstrlenW(buffer);
|
||||
while(len > 0)
|
||||
{
|
||||
--len;
|
||||
if(buffer[len] == '/')
|
||||
buffer[len] = '\\';
|
||||
}
|
||||
|
||||
if((f=_wfopen(buffer, L"rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %ls\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %ls\n", buffer);
|
||||
}
|
||||
free(wname);
|
||||
free(wsubdir);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
FILE *OpenDataFile(const char *fname, const char *subdir)
|
||||
{
|
||||
char buffer[PATH_MAX] = "";
|
||||
const char *str, *next;
|
||||
FILE *f;
|
||||
|
||||
if(fname[0] == '/')
|
||||
{
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if((f=al_fopen(fname, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", fname);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", fname);
|
||||
|
||||
if((str=getenv("XDG_DATA_HOME")) != NULL && str[0] != '\0')
|
||||
snprintf(buffer, sizeof(buffer), "%s/%s/%s", str, subdir, fname);
|
||||
else if((str=getenv("HOME")) != NULL && str[0] != '\0')
|
||||
snprintf(buffer, sizeof(buffer), "%s/.local/share/%s/%s", str, subdir, fname);
|
||||
if(buffer[0])
|
||||
{
|
||||
if((f=al_fopen(buffer, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", buffer);
|
||||
}
|
||||
|
||||
if((str=getenv("XDG_DATA_DIRS")) == NULL || str[0] == '\0')
|
||||
str = "/usr/local/share/:/usr/share/";
|
||||
|
||||
next = str;
|
||||
while((str=next) != NULL && str[0] != '\0')
|
||||
{
|
||||
size_t len;
|
||||
next = strchr(str, ':');
|
||||
|
||||
if(!next)
|
||||
len = strlen(str);
|
||||
else
|
||||
{
|
||||
len = next - str;
|
||||
next++;
|
||||
}
|
||||
|
||||
if(len > sizeof(buffer)-1)
|
||||
len = sizeof(buffer)-1;
|
||||
strncpy(buffer, str, len);
|
||||
buffer[len] = '\0';
|
||||
snprintf(buffer+len, sizeof(buffer)-len, "/%s/%s", subdir, fname);
|
||||
|
||||
if((f=al_fopen(buffer, "rb")) != NULL)
|
||||
{
|
||||
TRACE("Opened %s\n", buffer);
|
||||
return f;
|
||||
}
|
||||
WARN("Could not open %s\n", buffer);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void SetRTPriority(void)
|
||||
{
|
||||
ALboolean failed = AL_FALSE;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(RTPrioLevel > 0)
|
||||
failed = !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
#elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
|
||||
if(RTPrioLevel > 0)
|
||||
{
|
||||
struct sched_param param;
|
||||
/* Use the minimum real-time priority possible for now (on Linux this
|
||||
* should be 1 for SCHED_RR) */
|
||||
param.sched_priority = sched_get_priority_min(SCHED_RR);
|
||||
failed = !!pthread_setschedparam(pthread_self(), SCHED_RR, ¶m);
|
||||
}
|
||||
#else
|
||||
/* Real-time priority not available */
|
||||
failed = (RTPrioLevel>0);
|
||||
#endif
|
||||
if(failed)
|
||||
ERR("Failed to set priority level for thread\n");
|
||||
}
|
||||
|
||||
|
||||
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact)
|
||||
{
|
||||
vector_ *vecptr = (vector_*)ptr;
|
||||
if(obj_count < 0)
|
||||
return AL_FALSE;
|
||||
if((*vecptr ? (*vecptr)->Capacity : 0) < obj_count)
|
||||
{
|
||||
ALsizei old_size = (*vecptr ? (*vecptr)->Size : 0);
|
||||
void *temp;
|
||||
|
||||
/* Use the next power-of-2 size if we don't need to allocate the exact
|
||||
* amount. This is preferred when regularly increasing the vector since
|
||||
* it means fewer reallocations. Though it means it also wastes some
|
||||
* memory. */
|
||||
if(exact == AL_FALSE)
|
||||
{
|
||||
obj_count = NextPowerOf2((ALuint)obj_count);
|
||||
if(obj_count < 0) return AL_FALSE;
|
||||
}
|
||||
|
||||
/* Need to be explicit with the caller type's base size, because it
|
||||
* could have extra padding before the start of the array (that is,
|
||||
* sizeof(*vector_) may not equal base_size). */
|
||||
temp = realloc(*vecptr, base_size + obj_size*obj_count);
|
||||
if(temp == NULL) return AL_FALSE;
|
||||
|
||||
*vecptr = temp;
|
||||
(*vecptr)->Capacity = obj_count;
|
||||
(*vecptr)->Size = old_size;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count)
|
||||
{
|
||||
vector_ *vecptr = (vector_*)ptr;
|
||||
if(obj_count < 0)
|
||||
return AL_FALSE;
|
||||
if(*vecptr || obj_count > 0)
|
||||
{
|
||||
if(!vector_reserve((char*)vecptr, base_size, obj_size, obj_count, AL_TRUE))
|
||||
return AL_FALSE;
|
||||
(*vecptr)->Size = obj_count;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend)
|
||||
{
|
||||
vector_ *vecptr = (vector_*)ptr;
|
||||
if(datstart != datend)
|
||||
{
|
||||
ptrdiff_t ins_elem = (*vecptr ? ((char*)ins_pos - ((char*)(*vecptr) + base_size)) :
|
||||
((char*)ins_pos - (char*)NULL)) /
|
||||
obj_size;
|
||||
ptrdiff_t numins = ((const char*)datend - (const char*)datstart) / obj_size;
|
||||
|
||||
assert(numins > 0);
|
||||
if(INT_MAX-VECTOR_SIZE(*vecptr) <= numins ||
|
||||
!vector_reserve((char*)vecptr, base_size, obj_size, VECTOR_SIZE(*vecptr)+numins, AL_TRUE))
|
||||
return AL_FALSE;
|
||||
|
||||
/* NOTE: ins_pos may have been invalidated if *vecptr moved. Use ins_elem instead. */
|
||||
if(ins_elem < (*vecptr)->Size)
|
||||
{
|
||||
memmove((char*)(*vecptr) + base_size + ((ins_elem+numins)*obj_size),
|
||||
(char*)(*vecptr) + base_size + ((ins_elem )*obj_size),
|
||||
((*vecptr)->Size-ins_elem)*obj_size);
|
||||
}
|
||||
memcpy((char*)(*vecptr) + base_size + (ins_elem*obj_size),
|
||||
datstart, numins*obj_size);
|
||||
(*vecptr)->Size += (ALsizei)numins;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
extern inline void al_string_deinit(al_string *str);
|
||||
extern inline ALsizei al_string_length(const_al_string str);
|
||||
extern inline ALboolean al_string_empty(const_al_string str);
|
||||
extern inline const al_string_char_type *al_string_get_cstr(const_al_string str);
|
||||
|
||||
void al_string_clear(al_string *str)
|
||||
{
|
||||
/* Reserve one more character than the total size of the string. This is to
|
||||
* ensure we have space to add a null terminator in the string data so it
|
||||
* can be used as a C-style string. */
|
||||
VECTOR_RESERVE(*str, 1);
|
||||
VECTOR_RESIZE(*str, 0);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
static inline int al_string_compare(const al_string_char_type *str1, ALsizei str1len,
|
||||
const al_string_char_type *str2, ALsizei str2len)
|
||||
{
|
||||
ALsizei complen = mini(str1len, str2len);
|
||||
int ret = memcmp(str1, str2, complen);
|
||||
if(ret == 0)
|
||||
{
|
||||
if(str1len > str2len) return 1;
|
||||
if(str1len < str2len) return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
int al_string_cmp(const_al_string str1, const_al_string str2)
|
||||
{
|
||||
return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
|
||||
&VECTOR_FRONT(str2), al_string_length(str2));
|
||||
}
|
||||
int al_string_cmp_cstr(const_al_string str1, const al_string_char_type *str2)
|
||||
{
|
||||
return al_string_compare(&VECTOR_FRONT(str1), al_string_length(str1),
|
||||
str2, (ALsizei)strlen(str2));
|
||||
}
|
||||
|
||||
void al_string_copy(al_string *str, const_al_string from)
|
||||
{
|
||||
ALsizei len = VECTOR_SIZE(from);
|
||||
VECTOR_RESERVE(*str, len+1);
|
||||
VECTOR_RESIZE(*str, 0);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), VECTOR_ITER_BEGIN(from), VECTOR_ITER_BEGIN(from)+len);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
void al_string_copy_cstr(al_string *str, const al_string_char_type *from)
|
||||
{
|
||||
size_t len = strlen(from);
|
||||
VECTOR_RESERVE(*str, len+1);
|
||||
VECTOR_RESIZE(*str, 0);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
void al_string_append_char(al_string *str, const al_string_char_type c)
|
||||
{
|
||||
VECTOR_RESERVE(*str, al_string_length(*str)+2);
|
||||
VECTOR_PUSH_BACK(*str, c);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
|
||||
void al_string_append_cstr(al_string *str, const al_string_char_type *from)
|
||||
{
|
||||
size_t len = strlen(from);
|
||||
if(len != 0)
|
||||
{
|
||||
VECTOR_RESERVE(*str, al_string_length(*str)+len+1);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, from+len);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void al_string_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to)
|
||||
{
|
||||
if(to != from)
|
||||
{
|
||||
VECTOR_RESERVE(*str, al_string_length(*str)+(to-from)+1);
|
||||
VECTOR_INSERT(*str, VECTOR_ITER_END(*str), from, to);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void al_string_copy_wcstr(al_string *str, const wchar_t *from)
|
||||
{
|
||||
int len;
|
||||
if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
|
||||
{
|
||||
VECTOR_RESERVE(*str, len);
|
||||
VECTOR_RESIZE(*str, len-1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str), len, NULL, NULL);
|
||||
*VECTOR_ITER_END(*str) = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+820
@@ -0,0 +1,820 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2011 by Chris Robinson
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alSource.h"
|
||||
#include "alu.h"
|
||||
#include "hrtf.h"
|
||||
|
||||
|
||||
/* Current data set limits defined by the makehrtf utility. */
|
||||
#define MIN_IR_SIZE (8)
|
||||
#define MAX_IR_SIZE (128)
|
||||
#define MOD_IR_SIZE (8)
|
||||
|
||||
#define MIN_EV_COUNT (5)
|
||||
#define MAX_EV_COUNT (128)
|
||||
|
||||
#define MIN_AZ_COUNT (1)
|
||||
#define MAX_AZ_COUNT (128)
|
||||
|
||||
struct Hrtf {
|
||||
ALuint sampleRate;
|
||||
ALuint irSize;
|
||||
ALubyte evCount;
|
||||
|
||||
const ALubyte *azCount;
|
||||
const ALushort *evOffset;
|
||||
const ALshort *coeffs;
|
||||
const ALubyte *delays;
|
||||
|
||||
struct Hrtf *next;
|
||||
};
|
||||
|
||||
static const ALchar magicMarker00[8] = "MinPHR00";
|
||||
static const ALchar magicMarker01[8] = "MinPHR01";
|
||||
|
||||
/* First value for pass-through coefficients (remaining are 0), used for omni-
|
||||
* directional sounds. */
|
||||
static const ALfloat PassthruCoeff = 32767.0f * 0.707106781187f/*sqrt(0.5)*/;
|
||||
|
||||
static struct Hrtf *LoadedHrtfs = NULL;
|
||||
|
||||
/* Calculate the elevation indices given the polar elevation in radians.
|
||||
* This will return two indices between 0 and (evcount - 1) and an
|
||||
* interpolation factor between 0.0 and 1.0.
|
||||
*/
|
||||
static void CalcEvIndices(ALuint evcount, ALfloat ev, ALuint *evidx, ALfloat *evmu)
|
||||
{
|
||||
ev = (F_PI_2 + ev) * (evcount-1) / F_PI;
|
||||
evidx[0] = fastf2u(ev);
|
||||
evidx[1] = minu(evidx[0] + 1, evcount-1);
|
||||
*evmu = ev - evidx[0];
|
||||
}
|
||||
|
||||
/* Calculate the azimuth indices given the polar azimuth in radians. This
|
||||
* will return two indices between 0 and (azcount - 1) and an interpolation
|
||||
* factor between 0.0 and 1.0.
|
||||
*/
|
||||
static void CalcAzIndices(ALuint azcount, ALfloat az, ALuint *azidx, ALfloat *azmu)
|
||||
{
|
||||
az = (F_2PI + az) * azcount / (F_2PI);
|
||||
azidx[0] = fastf2u(az) % azcount;
|
||||
azidx[1] = (azidx[0] + 1) % azcount;
|
||||
*azmu = az - floorf(az);
|
||||
}
|
||||
|
||||
/* Calculates the normalized HRTF transition factor (delta) from the changes
|
||||
* in gain and listener to source angle between updates. The result is a
|
||||
* normalized delta factor that can be used to calculate moving HRIR stepping
|
||||
* values.
|
||||
*/
|
||||
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3])
|
||||
{
|
||||
ALfloat gainChange, angleChange, change;
|
||||
|
||||
// Calculate the normalized dB gain change.
|
||||
newGain = maxf(newGain, 0.0001f);
|
||||
oldGain = maxf(oldGain, 0.0001f);
|
||||
gainChange = fabsf(log10f(newGain / oldGain) / log10f(0.0001f));
|
||||
|
||||
// Calculate the angle change only when there is enough gain to notice it.
|
||||
angleChange = 0.0f;
|
||||
if(gainChange > 0.0001f || newGain > 0.0001f)
|
||||
{
|
||||
// No angle change when the directions are equal or degenerate (when
|
||||
// both have zero length).
|
||||
if(newdir[0] != olddir[0] || newdir[1] != olddir[1] || newdir[2] != olddir[2])
|
||||
{
|
||||
ALfloat dotp = olddir[0]*newdir[0] + olddir[1]*newdir[1] + olddir[2]*newdir[2];
|
||||
angleChange = acosf(clampf(dotp, -1.0f, 1.0f)) / F_PI;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the largest of the two changes for the delta factor, and apply a
|
||||
// significance shaping function to it.
|
||||
change = maxf(angleChange * 25.0f, gainChange) * 2.0f;
|
||||
return minf(change, 1.0f);
|
||||
}
|
||||
|
||||
/* Calculates static HRIR coefficients and delays for the given polar
|
||||
* elevation and azimuth in radians. Linear interpolation is used to
|
||||
* increase the apparent resolution of the HRIR data set. The coefficients
|
||||
* are also normalized and attenuated by the specified gain.
|
||||
*/
|
||||
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays)
|
||||
{
|
||||
ALuint evidx[2], lidx[4], ridx[4];
|
||||
ALfloat mu[3], blend[4];
|
||||
ALuint i;
|
||||
|
||||
/* Claculate elevation indices and interpolation factor. */
|
||||
CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
|
||||
|
||||
for(i = 0;i < 2;i++)
|
||||
{
|
||||
ALuint azcount = Hrtf->azCount[evidx[i]];
|
||||
ALuint evoffset = Hrtf->evOffset[evidx[i]];
|
||||
ALuint azidx[2];
|
||||
|
||||
/* Calculate azimuth indices and interpolation factor for this elevation. */
|
||||
CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
|
||||
|
||||
/* Calculate a set of linear HRIR indices for left and right channels. */
|
||||
lidx[i*2 + 0] = evoffset + azidx[0];
|
||||
lidx[i*2 + 1] = evoffset + azidx[1];
|
||||
ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
|
||||
ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
|
||||
}
|
||||
|
||||
/* Calculate 4 blending weights for 2D bilinear interpolation. */
|
||||
blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
|
||||
blend[1] = ( mu[0]) * (1.0f-mu[2]);
|
||||
blend[2] = (1.0f-mu[1]) * ( mu[2]);
|
||||
blend[3] = ( mu[1]) * ( mu[2]);
|
||||
|
||||
/* Calculate the HRIR delays using linear interpolation. */
|
||||
delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
|
||||
Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
|
||||
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
|
||||
/* Calculate the sample offsets for the HRIR indices. */
|
||||
lidx[0] *= Hrtf->irSize;
|
||||
lidx[1] *= Hrtf->irSize;
|
||||
lidx[2] *= Hrtf->irSize;
|
||||
lidx[3] *= Hrtf->irSize;
|
||||
ridx[0] *= Hrtf->irSize;
|
||||
ridx[1] *= Hrtf->irSize;
|
||||
ridx[2] *= Hrtf->irSize;
|
||||
ridx[3] *= Hrtf->irSize;
|
||||
|
||||
/* Calculate the normalized and attenuated HRIR coefficients using linear
|
||||
* interpolation when there is enough gain to warrant it. Zero the
|
||||
* coefficients if gain is too low.
|
||||
*/
|
||||
if(gain > 0.0001f)
|
||||
{
|
||||
ALfloat c;
|
||||
|
||||
gain *= 1.0f/32767.0f;
|
||||
|
||||
i = 0;
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
|
||||
for(i = 1;i < Hrtf->irSize;i++)
|
||||
{
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
coeffs[i][0] = 0.0f;
|
||||
coeffs[i][1] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates the moving HRIR target coefficients, target delays, and
|
||||
* stepping values for the given polar elevation and azimuth in radians.
|
||||
* Linear interpolation is used to increase the apparent resolution of the
|
||||
* HRIR data set. The coefficients are also normalized and attenuated by the
|
||||
* specified gain. Stepping resolution and count is determined using the
|
||||
* given delta factor between 0.0 and 1.0.
|
||||
*/
|
||||
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep)
|
||||
{
|
||||
ALuint evidx[2], lidx[4], ridx[4];
|
||||
ALfloat mu[3], blend[4];
|
||||
ALfloat left, right;
|
||||
ALfloat step;
|
||||
ALuint i;
|
||||
|
||||
/* Claculate elevation indices and interpolation factor. */
|
||||
CalcEvIndices(Hrtf->evCount, elevation, evidx, &mu[2]);
|
||||
|
||||
for(i = 0;i < 2;i++)
|
||||
{
|
||||
ALuint azcount = Hrtf->azCount[evidx[i]];
|
||||
ALuint evoffset = Hrtf->evOffset[evidx[i]];
|
||||
ALuint azidx[2];
|
||||
|
||||
/* Calculate azimuth indices and interpolation factor for this elevation. */
|
||||
CalcAzIndices(azcount, azimuth, azidx, &mu[i]);
|
||||
|
||||
/* Calculate a set of linear HRIR indices for left and right channels. */
|
||||
lidx[i*2 + 0] = evoffset + azidx[0];
|
||||
lidx[i*2 + 1] = evoffset + azidx[1];
|
||||
ridx[i*2 + 0] = evoffset + ((azcount-azidx[0]) % azcount);
|
||||
ridx[i*2 + 1] = evoffset + ((azcount-azidx[1]) % azcount);
|
||||
}
|
||||
|
||||
// Calculate the stepping parameters.
|
||||
delta = maxf(floorf(delta*(Hrtf->sampleRate*0.015f) + 0.5f), 1.0f);
|
||||
step = 1.0f / delta;
|
||||
|
||||
/* Calculate 4 blending weights for 2D bilinear interpolation. */
|
||||
blend[0] = (1.0f-mu[0]) * (1.0f-mu[2]);
|
||||
blend[1] = ( mu[0]) * (1.0f-mu[2]);
|
||||
blend[2] = (1.0f-mu[1]) * ( mu[2]);
|
||||
blend[3] = ( mu[1]) * ( mu[2]);
|
||||
|
||||
/* Calculate the HRIR delays using linear interpolation. Then calculate
|
||||
* the delay stepping values using the target and previous running
|
||||
* delays.
|
||||
*/
|
||||
left = (ALfloat)(delays[0] - (delayStep[0] * counter));
|
||||
right = (ALfloat)(delays[1] - (delayStep[1] * counter));
|
||||
|
||||
delays[0] = fastf2u((Hrtf->delays[lidx[0]]*blend[0] + Hrtf->delays[lidx[1]]*blend[1] +
|
||||
Hrtf->delays[lidx[2]]*blend[2] + Hrtf->delays[lidx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
delays[1] = fastf2u((Hrtf->delays[ridx[0]]*blend[0] + Hrtf->delays[ridx[1]]*blend[1] +
|
||||
Hrtf->delays[ridx[2]]*blend[2] + Hrtf->delays[ridx[3]]*blend[3]) *
|
||||
dirfact + 0.5f) << HRTFDELAY_BITS;
|
||||
|
||||
delayStep[0] = fastf2i(step * (delays[0] - left));
|
||||
delayStep[1] = fastf2i(step * (delays[1] - right));
|
||||
|
||||
/* Calculate the sample offsets for the HRIR indices. */
|
||||
lidx[0] *= Hrtf->irSize;
|
||||
lidx[1] *= Hrtf->irSize;
|
||||
lidx[2] *= Hrtf->irSize;
|
||||
lidx[3] *= Hrtf->irSize;
|
||||
ridx[0] *= Hrtf->irSize;
|
||||
ridx[1] *= Hrtf->irSize;
|
||||
ridx[2] *= Hrtf->irSize;
|
||||
ridx[3] *= Hrtf->irSize;
|
||||
|
||||
/* Calculate the normalized and attenuated target HRIR coefficients using
|
||||
* linear interpolation when there is enough gain to warrant it. Zero
|
||||
* the target coefficients if gain is too low. Then calculate the
|
||||
* coefficient stepping values using the target and previous running
|
||||
* coefficients.
|
||||
*/
|
||||
if(gain > 0.0001f)
|
||||
{
|
||||
ALfloat c;
|
||||
|
||||
gain *= 1.0f/32767.0f;
|
||||
|
||||
i = 0;
|
||||
left = coeffs[i][0] - (coeffStep[i][0] * counter);
|
||||
right = coeffs[i][1] - (coeffStep[i][1] * counter);
|
||||
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(PassthruCoeff, c, dirfact) * gain;
|
||||
|
||||
coeffStep[i][0] = step * (coeffs[i][0] - left);
|
||||
coeffStep[i][1] = step * (coeffs[i][1] - right);
|
||||
|
||||
for(i = 1;i < Hrtf->irSize;i++)
|
||||
{
|
||||
left = coeffs[i][0] - (coeffStep[i][0] * counter);
|
||||
right = coeffs[i][1] - (coeffStep[i][1] * counter);
|
||||
|
||||
c = (Hrtf->coeffs[lidx[0]+i]*blend[0] + Hrtf->coeffs[lidx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[lidx[2]+i]*blend[2] + Hrtf->coeffs[lidx[3]+i]*blend[3]);
|
||||
coeffs[i][0] = lerp(0.0f, c, dirfact) * gain;
|
||||
c = (Hrtf->coeffs[ridx[0]+i]*blend[0] + Hrtf->coeffs[ridx[1]+i]*blend[1] +
|
||||
Hrtf->coeffs[ridx[2]+i]*blend[2] + Hrtf->coeffs[ridx[3]+i]*blend[3]);
|
||||
coeffs[i][1] = lerp(0.0f, c, dirfact) * gain;
|
||||
|
||||
coeffStep[i][0] = step * (coeffs[i][0] - left);
|
||||
coeffStep[i][1] = step * (coeffs[i][1] - right);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < Hrtf->irSize;i++)
|
||||
{
|
||||
left = coeffs[i][0] - (coeffStep[i][0] * counter);
|
||||
right = coeffs[i][1] - (coeffStep[i][1] * counter);
|
||||
|
||||
coeffs[i][0] = 0.0f;
|
||||
coeffs[i][1] = 0.0f;
|
||||
|
||||
coeffStep[i][0] = step * -left;
|
||||
coeffStep[i][1] = step * -right;
|
||||
}
|
||||
}
|
||||
|
||||
/* The stepping count is the number of samples necessary for the HRIR to
|
||||
* complete its transition. The mixer will only apply stepping for this
|
||||
* many samples.
|
||||
*/
|
||||
return fastf2u(delta);
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf00(FILE *f, ALuint deviceRate)
|
||||
{
|
||||
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1;
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0, irCount = 0;
|
||||
ALushort irSize = 0;
|
||||
ALubyte evCount = 0;
|
||||
ALubyte *azCount = NULL;
|
||||
ALushort *evOffset = NULL;
|
||||
ALshort *coeffs = NULL;
|
||||
ALubyte *delays = NULL;
|
||||
ALuint i, j;
|
||||
|
||||
rate = fgetc(f);
|
||||
rate |= fgetc(f)<<8;
|
||||
rate |= fgetc(f)<<16;
|
||||
rate |= fgetc(f)<<24;
|
||||
|
||||
irCount = fgetc(f);
|
||||
irCount |= fgetc(f)<<8;
|
||||
|
||||
irSize = fgetc(f);
|
||||
irSize |= fgetc(f)<<8;
|
||||
|
||||
evCount = fgetc(f);
|
||||
|
||||
if(rate != deviceRate)
|
||||
{
|
||||
ERR("HRIR rate does not match device rate: rate=%d (%d)\n",
|
||||
rate, deviceRate);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
|
||||
irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
|
||||
{
|
||||
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
|
||||
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(failed)
|
||||
return NULL;
|
||||
|
||||
azCount = malloc(sizeof(azCount[0])*evCount);
|
||||
evOffset = malloc(sizeof(evOffset[0])*evCount);
|
||||
if(azCount == NULL || evOffset == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
evOffset[0] = fgetc(f);
|
||||
evOffset[0] |= fgetc(f)<<8;
|
||||
for(i = 1;i < evCount;i++)
|
||||
{
|
||||
evOffset[i] = fgetc(f);
|
||||
evOffset[i] |= fgetc(f)<<8;
|
||||
if(evOffset[i] <= evOffset[i-1])
|
||||
{
|
||||
ERR("Invalid evOffset: evOffset[%d]=%d (last=%d)\n",
|
||||
i, evOffset[i], evOffset[i-1]);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
azCount[i-1] = evOffset[i] - evOffset[i-1];
|
||||
if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
|
||||
{
|
||||
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
|
||||
i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
if(irCount <= evOffset[i-1])
|
||||
{
|
||||
ERR("Invalid evOffset: evOffset[%d]=%d (irCount=%d)\n",
|
||||
i-1, evOffset[i-1], irCount);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
azCount[i-1] = irCount - evOffset[i-1];
|
||||
if(azCount[i-1] < MIN_AZ_COUNT || azCount[i-1] > MAX_AZ_COUNT)
|
||||
{
|
||||
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
|
||||
i-1, azCount[i-1], MIN_AZ_COUNT, MAX_AZ_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
|
||||
delays = malloc(sizeof(delays[0])*irCount);
|
||||
if(coeffs == NULL || delays == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
for(i = 0;i < irCount*irSize;i+=irSize)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
{
|
||||
ALshort coeff;
|
||||
coeff = fgetc(f);
|
||||
coeff |= fgetc(f)<<8;
|
||||
coeffs[i+j] = coeff;
|
||||
}
|
||||
}
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i] = fgetc(f);
|
||||
if(delays[i] > maxDelay)
|
||||
{
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(feof(f))
|
||||
{
|
||||
ERR("Premature end of data\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
Hrtf = malloc(sizeof(struct Hrtf));
|
||||
if(Hrtf == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
Hrtf->sampleRate = rate;
|
||||
Hrtf->irSize = irSize;
|
||||
Hrtf->evCount = evCount;
|
||||
Hrtf->azCount = azCount;
|
||||
Hrtf->evOffset = evOffset;
|
||||
Hrtf->coeffs = coeffs;
|
||||
Hrtf->delays = delays;
|
||||
Hrtf->next = NULL;
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
free(azCount);
|
||||
free(evOffset);
|
||||
free(coeffs);
|
||||
free(delays);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf01(FILE *f, ALuint deviceRate)
|
||||
{
|
||||
const ALubyte maxDelay = SRC_HISTORY_LENGTH-1;
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
ALboolean failed = AL_FALSE;
|
||||
ALuint rate = 0, irCount = 0;
|
||||
ALubyte irSize = 0, evCount = 0;
|
||||
ALubyte *azCount = NULL;
|
||||
ALushort *evOffset = NULL;
|
||||
ALshort *coeffs = NULL;
|
||||
ALubyte *delays = NULL;
|
||||
ALuint i, j;
|
||||
|
||||
rate = fgetc(f);
|
||||
rate |= fgetc(f)<<8;
|
||||
rate |= fgetc(f)<<16;
|
||||
rate |= fgetc(f)<<24;
|
||||
|
||||
irSize = fgetc(f);
|
||||
|
||||
evCount = fgetc(f);
|
||||
|
||||
if(rate != deviceRate)
|
||||
{
|
||||
ERR("HRIR rate does not match device rate: rate=%d (%d)\n",
|
||||
rate, deviceRate);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
|
||||
{
|
||||
ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
|
||||
irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
|
||||
{
|
||||
ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
|
||||
evCount, MIN_EV_COUNT, MAX_EV_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(failed)
|
||||
return NULL;
|
||||
|
||||
azCount = malloc(sizeof(azCount[0])*evCount);
|
||||
evOffset = malloc(sizeof(evOffset[0])*evCount);
|
||||
if(azCount == NULL || evOffset == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
for(i = 0;i < evCount;i++)
|
||||
{
|
||||
azCount[i] = fgetc(f);
|
||||
if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT)
|
||||
{
|
||||
ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
|
||||
i, azCount[i], MIN_AZ_COUNT, MAX_AZ_COUNT);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
evOffset[0] = 0;
|
||||
irCount = azCount[0];
|
||||
for(i = 1;i < evCount;i++)
|
||||
{
|
||||
evOffset[i] = evOffset[i-1] + azCount[i-1];
|
||||
irCount += azCount[i];
|
||||
}
|
||||
|
||||
coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
|
||||
delays = malloc(sizeof(delays[0])*irCount);
|
||||
if(coeffs == NULL || delays == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
for(i = 0;i < irCount*irSize;i+=irSize)
|
||||
{
|
||||
for(j = 0;j < irSize;j++)
|
||||
{
|
||||
ALshort coeff;
|
||||
coeff = fgetc(f);
|
||||
coeff |= fgetc(f)<<8;
|
||||
coeffs[i+j] = coeff;
|
||||
}
|
||||
}
|
||||
for(i = 0;i < irCount;i++)
|
||||
{
|
||||
delays[i] = fgetc(f);
|
||||
if(delays[i] > maxDelay)
|
||||
{
|
||||
ERR("Invalid delays[%d]: %d (%d)\n", i, delays[i], maxDelay);
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(feof(f))
|
||||
{
|
||||
ERR("Premature end of data\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
Hrtf = malloc(sizeof(struct Hrtf));
|
||||
if(Hrtf == NULL)
|
||||
{
|
||||
ERR("Out of memory.\n");
|
||||
failed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!failed)
|
||||
{
|
||||
Hrtf->sampleRate = rate;
|
||||
Hrtf->irSize = irSize;
|
||||
Hrtf->evCount = evCount;
|
||||
Hrtf->azCount = azCount;
|
||||
Hrtf->evOffset = evOffset;
|
||||
Hrtf->coeffs = coeffs;
|
||||
Hrtf->delays = delays;
|
||||
Hrtf->next = NULL;
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
free(azCount);
|
||||
free(evOffset);
|
||||
free(coeffs);
|
||||
free(delays);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct Hrtf *LoadHrtf(ALuint deviceRate)
|
||||
{
|
||||
const char *fnamelist = "default-%r.mhr";
|
||||
|
||||
ConfigValueStr(NULL, "hrtf_tables", &fnamelist);
|
||||
while(*fnamelist != '\0')
|
||||
{
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
char fname[PATH_MAX];
|
||||
const char *next;
|
||||
ALchar magic[8];
|
||||
ALuint i;
|
||||
FILE *f;
|
||||
|
||||
i = 0;
|
||||
while(isspace(*fnamelist) || *fnamelist == ',')
|
||||
fnamelist++;
|
||||
next = fnamelist;
|
||||
while(*(fnamelist=next) != '\0' && *fnamelist != ',')
|
||||
{
|
||||
next = strpbrk(fnamelist, "%,");
|
||||
while(fnamelist != next && *fnamelist && i < sizeof(fname))
|
||||
fname[i++] = *(fnamelist++);
|
||||
|
||||
if(!next || *next == ',')
|
||||
break;
|
||||
|
||||
/* *next == '%' */
|
||||
next++;
|
||||
if(*next == 'r')
|
||||
{
|
||||
int wrote = snprintf(&fname[i], sizeof(fname)-i, "%u", deviceRate);
|
||||
i += minu(wrote, sizeof(fname)-i);
|
||||
next++;
|
||||
}
|
||||
else if(*next == '%')
|
||||
{
|
||||
if(i < sizeof(fname))
|
||||
fname[i++] = '%';
|
||||
next++;
|
||||
}
|
||||
else
|
||||
ERR("Invalid marker '%%%c'\n", *next);
|
||||
}
|
||||
i = minu(i, sizeof(fname)-1);
|
||||
fname[i] = '\0';
|
||||
while(i > 0 && isspace(fname[i-1]))
|
||||
i--;
|
||||
fname[i] = '\0';
|
||||
|
||||
if(fname[0] == '\0')
|
||||
continue;
|
||||
|
||||
TRACE("Loading %s...\n", fname);
|
||||
f = OpenDataFile(fname, "openal/hrtf");
|
||||
if(f == NULL)
|
||||
{
|
||||
ERR("Could not open %s\n", fname);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(fread(magic, 1, sizeof(magic), f) != sizeof(magic))
|
||||
ERR("Failed to read header from %s\n", fname);
|
||||
else
|
||||
{
|
||||
if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0)
|
||||
{
|
||||
TRACE("Detected data set format v0\n");
|
||||
Hrtf = LoadHrtf00(f, deviceRate);
|
||||
}
|
||||
else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0)
|
||||
{
|
||||
TRACE("Detected data set format v1\n");
|
||||
Hrtf = LoadHrtf01(f, deviceRate);
|
||||
}
|
||||
else
|
||||
ERR("Invalid header in %s: \"%.8s\"\n", fname, magic);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
f = NULL;
|
||||
|
||||
if(Hrtf)
|
||||
{
|
||||
Hrtf->next = LoadedHrtfs;
|
||||
LoadedHrtfs = Hrtf;
|
||||
TRACE("Loaded HRTF support for format: %s %uhz\n",
|
||||
DevFmtChannelsString(DevFmtStereo), Hrtf->sampleRate);
|
||||
return Hrtf;
|
||||
}
|
||||
|
||||
ERR("Failed to load %s\n", fname);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate)
|
||||
{
|
||||
if(chans == DevFmtStereo)
|
||||
{
|
||||
struct Hrtf *Hrtf = LoadedHrtfs;
|
||||
while(Hrtf != NULL)
|
||||
{
|
||||
if(srate == Hrtf->sampleRate)
|
||||
return Hrtf;
|
||||
Hrtf = Hrtf->next;
|
||||
}
|
||||
|
||||
Hrtf = LoadHrtf(srate);
|
||||
if(Hrtf != NULL)
|
||||
return Hrtf;
|
||||
}
|
||||
ERR("Incompatible format: %s %uhz\n", DevFmtChannelsString(chans), srate);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate)
|
||||
{
|
||||
const struct Hrtf *hrtf = LoadedHrtfs;
|
||||
while(hrtf != NULL)
|
||||
{
|
||||
if(*srate == hrtf->sampleRate)
|
||||
break;
|
||||
hrtf = hrtf->next;
|
||||
}
|
||||
|
||||
if(hrtf == NULL)
|
||||
{
|
||||
hrtf = LoadHrtf(*srate);
|
||||
if(hrtf == NULL) return ALC_FALSE;
|
||||
}
|
||||
|
||||
*chans = DevFmtStereo;
|
||||
*srate = hrtf->sampleRate;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void FreeHrtfs(void)
|
||||
{
|
||||
struct Hrtf *Hrtf = NULL;
|
||||
|
||||
while((Hrtf=LoadedHrtfs) != NULL)
|
||||
{
|
||||
LoadedHrtfs = Hrtf->next;
|
||||
free((void*)Hrtf->azCount);
|
||||
free((void*)Hrtf->evOffset);
|
||||
free((void*)Hrtf->coeffs);
|
||||
free((void*)Hrtf->delays);
|
||||
free(Hrtf);
|
||||
}
|
||||
}
|
||||
|
||||
ALuint GetHrtfIrSize (const struct Hrtf *Hrtf)
|
||||
{
|
||||
return Hrtf->irSize;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#ifndef ALC_HRTF_H
|
||||
#define ALC_HRTF_H
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
enum DevFmtChannels;
|
||||
|
||||
struct Hrtf;
|
||||
|
||||
#define HRIR_BITS (7)
|
||||
#define HRIR_LENGTH (1<<HRIR_BITS)
|
||||
#define HRIR_MASK (HRIR_LENGTH-1)
|
||||
#define HRTFDELAY_BITS (20)
|
||||
#define HRTFDELAY_FRACONE (1<<HRTFDELAY_BITS)
|
||||
#define HRTFDELAY_MASK (HRTFDELAY_FRACONE-1)
|
||||
|
||||
const struct Hrtf *GetHrtf(enum DevFmtChannels chans, ALCuint srate);
|
||||
ALCboolean FindHrtfFormat(enum DevFmtChannels *chans, ALCuint *srate);
|
||||
|
||||
void FreeHrtfs(void);
|
||||
|
||||
ALuint GetHrtfIrSize(const struct Hrtf *Hrtf);
|
||||
ALfloat CalcHrtfDelta(ALfloat oldGain, ALfloat newGain, const ALfloat olddir[3], const ALfloat newdir[3]);
|
||||
void GetLerpedHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat (*coeffs)[2], ALuint *delays);
|
||||
ALuint GetMovingHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat dirfact, ALfloat gain, ALfloat delta, ALint counter, ALfloat (*coeffs)[2], ALuint *delays, ALfloat (*coeffStep)[2], ALint *delayStep);
|
||||
|
||||
#endif /* ALC_HRTF_H */
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
#include "alMidi.h"
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "alThunk.h"
|
||||
#include "evtqueue.h"
|
||||
#include "rwlock.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
extern inline ALboolean IsValidCtrlInput(int cc);
|
||||
|
||||
extern inline size_t Reader_read(Reader *self, void *buf, size_t len);
|
||||
|
||||
|
||||
/* MIDI events */
|
||||
#define SYSEX_EVENT (0xF0)
|
||||
|
||||
|
||||
void InitEvtQueue(EvtQueue *queue)
|
||||
{
|
||||
queue->events = NULL;
|
||||
queue->maxsize = 0;
|
||||
queue->size = 0;
|
||||
queue->pos = 0;
|
||||
}
|
||||
|
||||
void ResetEvtQueue(EvtQueue *queue)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < queue->size;i++)
|
||||
{
|
||||
if(queue->events[i].event == SYSEX_EVENT)
|
||||
{
|
||||
free(queue->events[i].param.sysex.data);
|
||||
queue->events[i].param.sysex.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free(queue->events);
|
||||
queue->events = NULL;
|
||||
queue->maxsize = 0;
|
||||
queue->size = 0;
|
||||
queue->pos = 0;
|
||||
}
|
||||
|
||||
ALenum InsertEvtQueue(EvtQueue *queue, const MidiEvent *evt)
|
||||
{
|
||||
ALsizei pos;
|
||||
|
||||
if(queue->maxsize == queue->size)
|
||||
{
|
||||
if(queue->pos > 0)
|
||||
{
|
||||
/* Queue has some stale entries, remove them to make space for more
|
||||
* events. */
|
||||
for(pos = 0;pos < queue->pos;pos++)
|
||||
{
|
||||
if(queue->events[pos].event == SYSEX_EVENT)
|
||||
{
|
||||
free(queue->events[pos].param.sysex.data);
|
||||
queue->events[pos].param.sysex.data = NULL;
|
||||
}
|
||||
}
|
||||
memmove(&queue->events[0], &queue->events[queue->pos],
|
||||
(queue->size-queue->pos)*sizeof(queue->events[0]));
|
||||
queue->size -= queue->pos;
|
||||
queue->pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Queue is full, double the allocated space. */
|
||||
void *temp = NULL;
|
||||
ALsizei newsize;
|
||||
|
||||
newsize = (queue->maxsize ? (queue->maxsize<<1) : 16);
|
||||
if(newsize > queue->maxsize)
|
||||
temp = realloc(queue->events, newsize * sizeof(queue->events[0]));
|
||||
if(!temp)
|
||||
return AL_OUT_OF_MEMORY;
|
||||
|
||||
queue->events = temp;
|
||||
queue->maxsize = newsize;
|
||||
}
|
||||
}
|
||||
|
||||
pos = queue->pos;
|
||||
if(queue->size > 0)
|
||||
{
|
||||
ALsizei high = queue->size - 1;
|
||||
while(pos < high)
|
||||
{
|
||||
ALsizei mid = pos + (high-pos)/2;
|
||||
if(queue->events[mid].time < evt->time)
|
||||
pos = mid + 1;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
while(pos < queue->size && queue->events[pos].time <= evt->time)
|
||||
pos++;
|
||||
|
||||
if(pos < queue->size)
|
||||
memmove(&queue->events[pos+1], &queue->events[pos],
|
||||
(queue->size-pos)*sizeof(queue->events[0]));
|
||||
}
|
||||
|
||||
queue->events[pos] = *evt;
|
||||
queue->size++;
|
||||
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void MidiSynth_Construct(MidiSynth *self, ALCdevice *device)
|
||||
{
|
||||
InitEvtQueue(&self->EventQueue);
|
||||
|
||||
RWLockInit(&self->Lock);
|
||||
|
||||
self->Soundfonts = NULL;
|
||||
self->NumSoundfonts = 0;
|
||||
|
||||
self->Gain = 1.0f;
|
||||
self->State = AL_INITIAL;
|
||||
|
||||
self->ClockBase = 0;
|
||||
self->SamplesDone = 0;
|
||||
self->SampleRate = device->Frequency;
|
||||
}
|
||||
|
||||
void MidiSynth_Destruct(MidiSynth *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumSoundfonts;i++)
|
||||
DecrementRef(&self->Soundfonts[i]->ref);
|
||||
free(self->Soundfonts);
|
||||
self->Soundfonts = NULL;
|
||||
self->NumSoundfonts = 0;
|
||||
|
||||
ResetEvtQueue(&self->EventQueue);
|
||||
}
|
||||
|
||||
|
||||
ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
|
||||
{
|
||||
ALCdevice *device = context->Device;
|
||||
ALsoundfont **sfonts;
|
||||
ALsizei i;
|
||||
|
||||
if(self->State != AL_INITIAL && self->State != AL_STOPPED)
|
||||
return AL_INVALID_OPERATION;
|
||||
|
||||
sfonts = calloc(1, count * sizeof(sfonts[0]));
|
||||
if(!sfonts) return AL_OUT_OF_MEMORY;
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
if(ids[i] == 0)
|
||||
sfonts[i] = ALsoundfont_getDefSoundfont(context);
|
||||
else if(!(sfonts[i]=LookupSfont(device, ids[i])))
|
||||
{
|
||||
free(sfonts);
|
||||
return AL_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
IncrementRef(&sfonts[i]->ref);
|
||||
sfonts = ExchangePtr((XchgPtr*)&self->Soundfonts, sfonts);
|
||||
count = ExchangeInt(&self->NumSoundfonts, count);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
DecrementRef(&sfonts[i]->ref);
|
||||
free(sfonts);
|
||||
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
extern inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain);
|
||||
extern inline ALfloat MidiSynth_getGain(const MidiSynth *self);
|
||||
extern inline void MidiSynth_setState(MidiSynth *self, ALenum state);
|
||||
extern inline ALenum MidiSynth_getState(const MidiSynth *self);
|
||||
|
||||
void MidiSynth_stop(MidiSynth *self)
|
||||
{
|
||||
ResetEvtQueue(&self->EventQueue);
|
||||
|
||||
self->ClockBase = 0;
|
||||
self->SamplesDone = 0;
|
||||
}
|
||||
|
||||
extern inline void MidiSynth_reset(MidiSynth *self);
|
||||
extern inline ALuint64 MidiSynth_getTime(const MidiSynth *self);
|
||||
extern inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self);
|
||||
|
||||
void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate)
|
||||
{
|
||||
if(self->SampleRate != srate)
|
||||
{
|
||||
self->ClockBase += self->SamplesDone * MIDI_CLOCK_RES / self->SampleRate;
|
||||
self->SamplesDone = 0;
|
||||
self->SampleRate = srate;
|
||||
}
|
||||
}
|
||||
|
||||
extern inline void MidiSynth_update(MidiSynth *self, ALCdevice *device);
|
||||
|
||||
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2)
|
||||
{
|
||||
MidiEvent entry;
|
||||
entry.time = time;
|
||||
entry.event = event;
|
||||
entry.param.val[0] = param1;
|
||||
entry.param.val[1] = param2;
|
||||
return InsertEvtQueue(&self->EventQueue, &entry);
|
||||
}
|
||||
|
||||
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size)
|
||||
{
|
||||
MidiEvent entry;
|
||||
ALenum err;
|
||||
|
||||
entry.time = time;
|
||||
entry.event = SYSEX_EVENT;
|
||||
entry.param.sysex.size = size;
|
||||
entry.param.sysex.data = malloc(size);
|
||||
if(!entry.param.sysex.data)
|
||||
return AL_OUT_OF_MEMORY;
|
||||
memcpy(entry.param.sysex.data, data, size);
|
||||
|
||||
err = InsertEvtQueue(&self->EventQueue, &entry);
|
||||
if(err != AL_NO_ERROR)
|
||||
free(entry.param.sysex.data);
|
||||
return err;
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
#ifndef AL_MIDI_BASE_H
|
||||
#define AL_MIDI_BASE_H
|
||||
|
||||
#include "alMain.h"
|
||||
#include "atomic.h"
|
||||
#include "evtqueue.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ALsoundfont;
|
||||
|
||||
typedef size_t (*ReaderCb)(void *ptr, size_t size, void *stream);
|
||||
typedef struct Reader {
|
||||
ReaderCb cb;
|
||||
void *ptr;
|
||||
int error;
|
||||
} Reader;
|
||||
inline size_t Reader_read(Reader *self, void *buf, size_t len)
|
||||
{
|
||||
size_t got = (!self->error) ? self->cb(buf, len, self->ptr) : 0;
|
||||
if(got < len) self->error = 1;
|
||||
return got;
|
||||
}
|
||||
#define READERR(x_) ((x_)->error)
|
||||
|
||||
ALboolean loadSf2(Reader *stream, struct ALsoundfont *sfont, ALCcontext *context);
|
||||
|
||||
|
||||
#define MIDI_CLOCK_RES U64(1000000000)
|
||||
|
||||
|
||||
struct MidiSynthVtable;
|
||||
|
||||
typedef struct MidiSynth {
|
||||
EvtQueue EventQueue;
|
||||
|
||||
ALuint64 ClockBase;
|
||||
ALuint SamplesDone;
|
||||
ALuint SampleRate;
|
||||
|
||||
/* NOTE: This rwlock is for the state and soundfont. The EventQueue and
|
||||
* related must instead use the device lock as they're used in the mixer
|
||||
* thread.
|
||||
*/
|
||||
RWLock Lock;
|
||||
|
||||
struct ALsoundfont **Soundfonts;
|
||||
ALsizei NumSoundfonts;
|
||||
|
||||
volatile ALfloat Gain;
|
||||
volatile ALenum State;
|
||||
|
||||
const struct MidiSynthVtable *vtbl;
|
||||
} MidiSynth;
|
||||
|
||||
void MidiSynth_Construct(MidiSynth *self, ALCdevice *device);
|
||||
void MidiSynth_Destruct(MidiSynth *self);
|
||||
ALenum MidiSynth_selectSoundfonts(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
|
||||
inline void MidiSynth_setGain(MidiSynth *self, ALfloat gain) { self->Gain = gain; }
|
||||
inline ALfloat MidiSynth_getGain(const MidiSynth *self) { return self->Gain; }
|
||||
inline void MidiSynth_setState(MidiSynth *self, ALenum state) { ExchangeInt(&self->State, state); }
|
||||
inline ALenum MidiSynth_getState(const MidiSynth *self) { return self->State; }
|
||||
void MidiSynth_stop(MidiSynth *self);
|
||||
inline void MidiSynth_reset(MidiSynth *self) { MidiSynth_stop(self); }
|
||||
inline ALuint64 MidiSynth_getTime(const MidiSynth *self)
|
||||
{ return self->ClockBase + (self->SamplesDone*MIDI_CLOCK_RES/self->SampleRate); }
|
||||
inline ALuint64 MidiSynth_getNextEvtTime(const MidiSynth *self)
|
||||
{
|
||||
if(self->EventQueue.pos == self->EventQueue.size)
|
||||
return UINT64_MAX;
|
||||
return self->EventQueue.events[self->EventQueue.pos].time;
|
||||
}
|
||||
void MidiSynth_setSampleRate(MidiSynth *self, ALuint srate);
|
||||
inline void MidiSynth_update(MidiSynth *self, ALCdevice *device)
|
||||
{ MidiSynth_setSampleRate(self, device->Frequency); }
|
||||
ALenum MidiSynth_insertEvent(MidiSynth *self, ALuint64 time, ALuint event, ALsizei param1, ALsizei param2);
|
||||
ALenum MidiSynth_insertSysExEvent(MidiSynth *self, ALuint64 time, const ALbyte *data, ALsizei size);
|
||||
|
||||
|
||||
struct MidiSynthVtable {
|
||||
void (*const Destruct)(MidiSynth *self);
|
||||
|
||||
ALenum (*const selectSoundfonts)(MidiSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
|
||||
|
||||
void (*const setGain)(MidiSynth *self, ALfloat gain);
|
||||
|
||||
void (*const stop)(MidiSynth *self);
|
||||
void (*const reset)(MidiSynth *self);
|
||||
|
||||
void (*const update)(MidiSynth *self, ALCdevice *device);
|
||||
void (*const process)(MidiSynth *self, ALuint samples, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
|
||||
void (*const Delete)(void *ptr);
|
||||
};
|
||||
|
||||
#define DEFINE_MIDISYNTH_VTABLE(T) \
|
||||
DECLARE_THUNK(T, MidiSynth, void, Destruct) \
|
||||
DECLARE_THUNK3(T, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*) \
|
||||
DECLARE_THUNK1(T, MidiSynth, void, setGain, ALfloat) \
|
||||
DECLARE_THUNK(T, MidiSynth, void, stop) \
|
||||
DECLARE_THUNK(T, MidiSynth, void, reset) \
|
||||
DECLARE_THUNK1(T, MidiSynth, void, update, ALCdevice*) \
|
||||
DECLARE_THUNK2(T, MidiSynth, void, process, ALuint, ALfloatBUFFERSIZE*restrict) \
|
||||
static void T##_MidiSynth_Delete(void *ptr) \
|
||||
{ T##_Delete(STATIC_UPCAST(T, MidiSynth, (MidiSynth*)ptr)); } \
|
||||
\
|
||||
static const struct MidiSynthVtable T##_MidiSynth_vtable = { \
|
||||
T##_MidiSynth_Destruct, \
|
||||
\
|
||||
T##_MidiSynth_selectSoundfonts, \
|
||||
T##_MidiSynth_setGain, \
|
||||
T##_MidiSynth_stop, \
|
||||
T##_MidiSynth_reset, \
|
||||
T##_MidiSynth_update, \
|
||||
T##_MidiSynth_process, \
|
||||
\
|
||||
T##_MidiSynth_Delete, \
|
||||
}
|
||||
|
||||
|
||||
MidiSynth *SSynth_create(ALCdevice *device);
|
||||
MidiSynth *FSynth_create(ALCdevice *device);
|
||||
MidiSynth *DSynth_create(ALCdevice *device);
|
||||
|
||||
MidiSynth *SynthCreate(ALCdevice *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* AL_MIDI_BASE_H */
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "evtqueue.h"
|
||||
#include "rwlock.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
typedef struct DSynth {
|
||||
DERIVE_FROM_TYPE(MidiSynth);
|
||||
} DSynth;
|
||||
|
||||
static void DSynth_Construct(DSynth *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(DSynth, MidiSynth, void, Destruct)
|
||||
static DECLARE_FORWARD3(DSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
|
||||
static DECLARE_FORWARD1(DSynth, MidiSynth, void, setGain, ALfloat)
|
||||
static DECLARE_FORWARD(DSynth, MidiSynth, void, stop)
|
||||
static DECLARE_FORWARD(DSynth, MidiSynth, void, reset)
|
||||
static DECLARE_FORWARD1(DSynth, MidiSynth, void, update, ALCdevice*)
|
||||
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
DECLARE_DEFAULT_ALLOCATORS(DSynth)
|
||||
DEFINE_MIDISYNTH_VTABLE(DSynth);
|
||||
|
||||
|
||||
static void DSynth_Construct(DSynth *self, ALCdevice *device)
|
||||
{
|
||||
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
|
||||
SET_VTABLE2(DSynth, MidiSynth, self);
|
||||
}
|
||||
|
||||
|
||||
static void DSynth_processQueue(DSynth *self, ALuint64 time)
|
||||
{
|
||||
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
|
||||
|
||||
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
|
||||
queue->pos++;
|
||||
}
|
||||
|
||||
static void DSynth_process(DSynth *self, ALuint SamplesToDo, ALfloatBUFFERSIZE*restrict UNUSED(DryBuffer))
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALuint64 curtime;
|
||||
|
||||
if(synth->State != AL_PLAYING)
|
||||
return;
|
||||
|
||||
synth->SamplesDone += SamplesToDo;
|
||||
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
|
||||
synth->SamplesDone %= synth->SampleRate;
|
||||
|
||||
curtime = MidiSynth_getTime(synth);
|
||||
DSynth_processQueue(self, maxi64(curtime-1, 0));
|
||||
}
|
||||
|
||||
|
||||
MidiSynth *DSynth_create(ALCdevice *device)
|
||||
{
|
||||
DSynth *synth = DSynth_New(sizeof(*synth));
|
||||
if(!synth)
|
||||
{
|
||||
ERR("Failed to allocate DSynth\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(synth, 0, sizeof(*synth));
|
||||
DSynth_Construct(synth, device);
|
||||
return STATIC_CAST(MidiSynth, synth);
|
||||
}
|
||||
@@ -0,0 +1,930 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "alMidi.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "evtqueue.h"
|
||||
#include "rwlock.h"
|
||||
|
||||
#ifdef HAVE_FLUIDSYNTH
|
||||
|
||||
#include <fluidsynth.h>
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define FLUID_FUNCS(MAGIC) \
|
||||
MAGIC(new_fluid_synth); \
|
||||
MAGIC(delete_fluid_synth); \
|
||||
MAGIC(new_fluid_settings); \
|
||||
MAGIC(delete_fluid_settings); \
|
||||
MAGIC(fluid_settings_setint); \
|
||||
MAGIC(fluid_settings_setnum); \
|
||||
MAGIC(fluid_synth_noteon); \
|
||||
MAGIC(fluid_synth_noteoff); \
|
||||
MAGIC(fluid_synth_program_change); \
|
||||
MAGIC(fluid_synth_pitch_bend); \
|
||||
MAGIC(fluid_synth_channel_pressure); \
|
||||
MAGIC(fluid_synth_cc); \
|
||||
MAGIC(fluid_synth_sysex); \
|
||||
MAGIC(fluid_synth_bank_select); \
|
||||
MAGIC(fluid_synth_set_channel_type); \
|
||||
MAGIC(fluid_synth_all_sounds_off); \
|
||||
MAGIC(fluid_synth_system_reset); \
|
||||
MAGIC(fluid_synth_set_gain); \
|
||||
MAGIC(fluid_synth_set_sample_rate); \
|
||||
MAGIC(fluid_synth_write_float); \
|
||||
MAGIC(fluid_synth_add_sfloader); \
|
||||
MAGIC(fluid_synth_sfload); \
|
||||
MAGIC(fluid_synth_sfunload); \
|
||||
MAGIC(fluid_synth_alloc_voice); \
|
||||
MAGIC(fluid_synth_start_voice); \
|
||||
MAGIC(fluid_voice_gen_set); \
|
||||
MAGIC(fluid_voice_add_mod); \
|
||||
MAGIC(fluid_mod_set_source1); \
|
||||
MAGIC(fluid_mod_set_source2); \
|
||||
MAGIC(fluid_mod_set_amount); \
|
||||
MAGIC(fluid_mod_set_dest);
|
||||
|
||||
void *fsynth_handle = NULL;
|
||||
#define DECL_FUNC(x) __typeof(x) *p##x
|
||||
FLUID_FUNCS(DECL_FUNC)
|
||||
#undef DECL_FUNC
|
||||
|
||||
#define new_fluid_synth pnew_fluid_synth
|
||||
#define delete_fluid_synth pdelete_fluid_synth
|
||||
#define new_fluid_settings pnew_fluid_settings
|
||||
#define delete_fluid_settings pdelete_fluid_settings
|
||||
#define fluid_settings_setint pfluid_settings_setint
|
||||
#define fluid_settings_setnum pfluid_settings_setnum
|
||||
#define fluid_synth_noteon pfluid_synth_noteon
|
||||
#define fluid_synth_noteoff pfluid_synth_noteoff
|
||||
#define fluid_synth_program_change pfluid_synth_program_change
|
||||
#define fluid_synth_pitch_bend pfluid_synth_pitch_bend
|
||||
#define fluid_synth_channel_pressure pfluid_synth_channel_pressure
|
||||
#define fluid_synth_cc pfluid_synth_cc
|
||||
#define fluid_synth_sysex pfluid_synth_sysex
|
||||
#define fluid_synth_bank_select pfluid_synth_bank_select
|
||||
#define fluid_synth_set_channel_type pfluid_synth_set_channel_type
|
||||
#define fluid_synth_all_sounds_off pfluid_synth_all_sounds_off
|
||||
#define fluid_synth_system_reset pfluid_synth_system_reset
|
||||
#define fluid_synth_set_gain pfluid_synth_set_gain
|
||||
#define fluid_synth_set_sample_rate pfluid_synth_set_sample_rate
|
||||
#define fluid_synth_write_float pfluid_synth_write_float
|
||||
#define fluid_synth_add_sfloader pfluid_synth_add_sfloader
|
||||
#define fluid_synth_sfload pfluid_synth_sfload
|
||||
#define fluid_synth_sfunload pfluid_synth_sfunload
|
||||
#define fluid_synth_alloc_voice pfluid_synth_alloc_voice
|
||||
#define fluid_synth_start_voice pfluid_synth_start_voice
|
||||
#define fluid_voice_gen_set pfluid_voice_gen_set
|
||||
#define fluid_voice_add_mod pfluid_voice_add_mod
|
||||
#define fluid_mod_set_source1 pfluid_mod_set_source1
|
||||
#define fluid_mod_set_source2 pfluid_mod_set_source2
|
||||
#define fluid_mod_set_amount pfluid_mod_set_amount
|
||||
#define fluid_mod_set_dest pfluid_mod_set_dest
|
||||
|
||||
static ALboolean LoadFSynth(void)
|
||||
{
|
||||
ALboolean ret = AL_TRUE;
|
||||
if(!fsynth_handle)
|
||||
{
|
||||
fsynth_handle = LoadLib("libfluidsynth.so.1");
|
||||
if(!fsynth_handle) return AL_FALSE;
|
||||
|
||||
#define LOAD_FUNC(x) do { \
|
||||
p##x = GetSymbol(fsynth_handle, #x); \
|
||||
if(!p##x) ret = AL_FALSE; \
|
||||
} while(0)
|
||||
FLUID_FUNCS(LOAD_FUNC)
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if(ret == AL_FALSE)
|
||||
{
|
||||
CloseLib(fsynth_handle);
|
||||
fsynth_handle = NULL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static inline ALboolean LoadFSynth(void) { return AL_TRUE; }
|
||||
#endif
|
||||
|
||||
|
||||
/* MIDI events */
|
||||
#define SYSEX_EVENT (0xF0)
|
||||
|
||||
/* MIDI controllers */
|
||||
#define CTRL_BANKSELECT_MSB (0)
|
||||
#define CTRL_BANKSELECT_LSB (32)
|
||||
#define CTRL_ALLNOTESOFF (123)
|
||||
|
||||
|
||||
static int getModInput(ALenum input)
|
||||
{
|
||||
switch(input)
|
||||
{
|
||||
case AL_ONE_SOFT: return FLUID_MOD_NONE;
|
||||
case AL_NOTEON_VELOCITY_SOFT: return FLUID_MOD_VELOCITY;
|
||||
case AL_NOTEON_KEY_SOFT: return FLUID_MOD_KEY;
|
||||
case AL_KEYPRESSURE_SOFT: return FLUID_MOD_KEYPRESSURE;
|
||||
case AL_CHANNELPRESSURE_SOFT: return FLUID_MOD_CHANNELPRESSURE;
|
||||
case AL_PITCHBEND_SOFT: return FLUID_MOD_PITCHWHEEL;
|
||||
case AL_PITCHBEND_SENSITIVITY_SOFT: return FLUID_MOD_PITCHWHEELSENS;
|
||||
}
|
||||
return input&0x7F;
|
||||
}
|
||||
|
||||
static int getModFlags(ALenum input, ALenum type, ALenum form)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case AL_UNORM_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE; break;
|
||||
case AL_UNORM_REV_SOFT: ret |= FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE; break;
|
||||
case AL_SNORM_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE; break;
|
||||
case AL_SNORM_REV_SOFT: ret |= FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE; break;
|
||||
}
|
||||
switch(form)
|
||||
{
|
||||
case AL_LINEAR_SOFT: ret |= FLUID_MOD_LINEAR; break;
|
||||
case AL_CONCAVE_SOFT: ret |= FLUID_MOD_CONCAVE; break;
|
||||
case AL_CONVEX_SOFT: ret |= FLUID_MOD_CONVEX; break;
|
||||
case AL_SWITCH_SOFT: ret |= FLUID_MOD_SWITCH; break;
|
||||
}
|
||||
/* Source input values less than 128 correspond to a MIDI continuous
|
||||
* controller. Otherwise, it's a general controller. */
|
||||
if(input < 128) ret |= FLUID_MOD_CC;
|
||||
else ret |= FLUID_MOD_GC;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum fluid_gen_type getModDest(ALenum gen)
|
||||
{
|
||||
switch(gen)
|
||||
{
|
||||
case AL_MOD_LFO_TO_PITCH_SOFT: return GEN_MODLFOTOPITCH;
|
||||
case AL_VIBRATO_LFO_TO_PITCH_SOFT: return GEN_VIBLFOTOPITCH;
|
||||
case AL_MOD_ENV_TO_PITCH_SOFT: return GEN_MODENVTOPITCH;
|
||||
case AL_FILTER_CUTOFF_SOFT: return GEN_FILTERFC;
|
||||
case AL_FILTER_RESONANCE_SOFT: return GEN_FILTERQ;
|
||||
case AL_MOD_LFO_TO_FILTER_CUTOFF_SOFT: return GEN_MODLFOTOFILTERFC;
|
||||
case AL_MOD_ENV_TO_FILTER_CUTOFF_SOFT: return GEN_MODENVTOFILTERFC;
|
||||
case AL_MOD_LFO_TO_VOLUME_SOFT: return GEN_MODLFOTOVOL;
|
||||
case AL_CHORUS_SEND_SOFT: return GEN_CHORUSSEND;
|
||||
case AL_REVERB_SEND_SOFT: return GEN_REVERBSEND;
|
||||
case AL_PAN_SOFT: return GEN_PAN;
|
||||
case AL_MOD_LFO_DELAY_SOFT: return GEN_MODLFODELAY;
|
||||
case AL_MOD_LFO_FREQUENCY_SOFT: return GEN_MODLFOFREQ;
|
||||
case AL_VIBRATO_LFO_DELAY_SOFT: return GEN_VIBLFODELAY;
|
||||
case AL_VIBRATO_LFO_FREQUENCY_SOFT: return GEN_VIBLFOFREQ;
|
||||
case AL_MOD_ENV_DELAYTIME_SOFT: return GEN_MODENVDELAY;
|
||||
case AL_MOD_ENV_ATTACKTIME_SOFT: return GEN_MODENVATTACK;
|
||||
case AL_MOD_ENV_HOLDTIME_SOFT: return GEN_MODENVHOLD;
|
||||
case AL_MOD_ENV_DECAYTIME_SOFT: return GEN_MODENVDECAY;
|
||||
case AL_MOD_ENV_SUSTAINVOLUME_SOFT: return GEN_MODENVSUSTAIN;
|
||||
case AL_MOD_ENV_RELEASETIME_SOFT: return GEN_MODENVRELEASE;
|
||||
case AL_MOD_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOMODENVHOLD;
|
||||
case AL_MOD_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOMODENVDECAY;
|
||||
case AL_VOLUME_ENV_DELAYTIME_SOFT: return GEN_VOLENVDELAY;
|
||||
case AL_VOLUME_ENV_ATTACKTIME_SOFT: return GEN_VOLENVATTACK;
|
||||
case AL_VOLUME_ENV_HOLDTIME_SOFT: return GEN_VOLENVHOLD;
|
||||
case AL_VOLUME_ENV_DECAYTIME_SOFT: return GEN_VOLENVDECAY;
|
||||
case AL_VOLUME_ENV_SUSTAINVOLUME_SOFT: return GEN_VOLENVSUSTAIN;
|
||||
case AL_VOLUME_ENV_RELEASETIME_SOFT: return GEN_VOLENVRELEASE;
|
||||
case AL_VOLUME_ENV_KEY_TO_HOLDTIME_SOFT: return GEN_KEYTOVOLENVHOLD;
|
||||
case AL_VOLUME_ENV_KEY_TO_DECAYTIME_SOFT: return GEN_KEYTOVOLENVDECAY;
|
||||
case AL_ATTENUATION_SOFT: return GEN_ATTENUATION;
|
||||
case AL_TUNING_COARSE_SOFT: return GEN_COARSETUNE;
|
||||
case AL_TUNING_FINE_SOFT: return GEN_FINETUNE;
|
||||
case AL_TUNING_SCALE_SOFT: return GEN_SCALETUNE;
|
||||
}
|
||||
ERR("Unhandled generator: 0x%04x\n", gen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getSf2LoopMode(ALenum mode)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
case AL_NONE: return 0;
|
||||
case AL_LOOP_CONTINUOUS_SOFT: return 1;
|
||||
case AL_LOOP_UNTIL_RELEASE_SOFT: return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getSampleType(ALenum type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AL_MONO_SOFT: return FLUID_SAMPLETYPE_MONO;
|
||||
case AL_RIGHT_SOFT: return FLUID_SAMPLETYPE_RIGHT;
|
||||
case AL_LEFT_SOFT: return FLUID_SAMPLETYPE_LEFT;
|
||||
}
|
||||
return FLUID_SAMPLETYPE_MONO;
|
||||
}
|
||||
|
||||
typedef struct FSample {
|
||||
DERIVE_FROM_TYPE(fluid_sample_t);
|
||||
|
||||
ALfontsound *Sound;
|
||||
|
||||
fluid_mod_t *Mods;
|
||||
ALsizei NumMods;
|
||||
} FSample;
|
||||
|
||||
static void FSample_Construct(FSample *self, ALfontsound *sound)
|
||||
{
|
||||
fluid_sample_t *sample = STATIC_CAST(fluid_sample_t, self);
|
||||
memset(sample->name, 0, sizeof(sample->name));
|
||||
sample->start = sound->Start;
|
||||
sample->end = sound->End;
|
||||
sample->loopstart = sound->LoopStart;
|
||||
sample->loopend = sound->LoopEnd;
|
||||
sample->samplerate = sound->SampleRate;
|
||||
sample->origpitch = sound->PitchKey;
|
||||
sample->pitchadj = sound->PitchCorrection;
|
||||
sample->sampletype = getSampleType(sound->SampleType);
|
||||
sample->valid = !!sound->Buffer;
|
||||
sample->data = sound->Buffer ? sound->Buffer->data : NULL;
|
||||
|
||||
sample->amplitude_that_reaches_noise_floor_is_valid = 0;
|
||||
sample->amplitude_that_reaches_noise_floor = 0.0;
|
||||
|
||||
sample->refcount = 0;
|
||||
|
||||
sample->notify = NULL;
|
||||
|
||||
sample->userdata = self;
|
||||
|
||||
self->Sound = sound;
|
||||
|
||||
self->NumMods = 0;
|
||||
self->Mods = calloc(sound->ModulatorMap.size*4, sizeof(fluid_mod_t[4]));
|
||||
if(self->Mods)
|
||||
{
|
||||
ALsizei i, j, k;
|
||||
|
||||
for(i = j = 0;i < sound->ModulatorMap.size;i++)
|
||||
{
|
||||
ALsfmodulator *mod = sound->ModulatorMap.array[i].value;
|
||||
for(k = 0;k < 4;k++,mod++)
|
||||
{
|
||||
if(mod->Dest == AL_NONE)
|
||||
continue;
|
||||
fluid_mod_set_source1(&self->Mods[j], getModInput(mod->Source[0].Input),
|
||||
getModFlags(mod->Source[0].Input, mod->Source[0].Type,
|
||||
mod->Source[0].Form));
|
||||
fluid_mod_set_source2(&self->Mods[j], getModInput(mod->Source[1].Input),
|
||||
getModFlags(mod->Source[1].Input, mod->Source[1].Type,
|
||||
mod->Source[1].Form));
|
||||
fluid_mod_set_amount(&self->Mods[j], mod->Amount);
|
||||
fluid_mod_set_dest(&self->Mods[j], getModDest(mod->Dest));
|
||||
self->Mods[j++].next = NULL;
|
||||
}
|
||||
}
|
||||
self->NumMods = j;
|
||||
}
|
||||
}
|
||||
|
||||
static void FSample_Destruct(FSample *self)
|
||||
{
|
||||
free(self->Mods);
|
||||
self->Mods = NULL;
|
||||
self->NumMods = 0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct FPreset {
|
||||
DERIVE_FROM_TYPE(fluid_preset_t);
|
||||
|
||||
char Name[16];
|
||||
|
||||
int Preset;
|
||||
int Bank;
|
||||
|
||||
FSample *Samples;
|
||||
ALsizei NumSamples;
|
||||
} FPreset;
|
||||
|
||||
static char* FPreset_getName(fluid_preset_t *preset);
|
||||
static int FPreset_getPreset(fluid_preset_t *preset);
|
||||
static int FPreset_getBank(fluid_preset_t *preset);
|
||||
static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int velocity);
|
||||
|
||||
static void FPreset_Construct(FPreset *self, ALsfpreset *preset, fluid_sfont_t *parent)
|
||||
{
|
||||
STATIC_CAST(fluid_preset_t, self)->data = self;
|
||||
STATIC_CAST(fluid_preset_t, self)->sfont = parent;
|
||||
STATIC_CAST(fluid_preset_t, self)->free = NULL;
|
||||
STATIC_CAST(fluid_preset_t, self)->get_name = FPreset_getName;
|
||||
STATIC_CAST(fluid_preset_t, self)->get_banknum = FPreset_getBank;
|
||||
STATIC_CAST(fluid_preset_t, self)->get_num = FPreset_getPreset;
|
||||
STATIC_CAST(fluid_preset_t, self)->noteon = FPreset_noteOn;
|
||||
STATIC_CAST(fluid_preset_t, self)->notify = NULL;
|
||||
|
||||
memset(self->Name, 0, sizeof(self->Name));
|
||||
self->Preset = preset->Preset;
|
||||
self->Bank = preset->Bank;
|
||||
|
||||
self->NumSamples = 0;
|
||||
self->Samples = calloc(1, preset->NumSounds * sizeof(self->Samples[0]));
|
||||
if(self->Samples)
|
||||
{
|
||||
ALsizei i;
|
||||
self->NumSamples = preset->NumSounds;
|
||||
for(i = 0;i < self->NumSamples;i++)
|
||||
FSample_Construct(&self->Samples[i], preset->Sounds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void FPreset_Destruct(FPreset *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumSamples;i++)
|
||||
FSample_Destruct(&self->Samples[i]);
|
||||
free(self->Samples);
|
||||
self->Samples = NULL;
|
||||
self->NumSamples = 0;
|
||||
}
|
||||
|
||||
static ALboolean FPreset_canDelete(FPreset *self)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < self->NumSamples;i++)
|
||||
{
|
||||
if(fluid_sample_refcount(STATIC_CAST(fluid_sample_t, &self->Samples[i])) != 0)
|
||||
return AL_FALSE;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static char* FPreset_getName(fluid_preset_t *preset)
|
||||
{
|
||||
return ((FPreset*)preset->data)->Name;
|
||||
}
|
||||
|
||||
static int FPreset_getPreset(fluid_preset_t *preset)
|
||||
{
|
||||
return ((FPreset*)preset->data)->Preset;
|
||||
}
|
||||
|
||||
static int FPreset_getBank(fluid_preset_t *preset)
|
||||
{
|
||||
return ((FPreset*)preset->data)->Bank;
|
||||
}
|
||||
|
||||
static int FPreset_noteOn(fluid_preset_t *preset, fluid_synth_t *synth, int channel, int key, int vel)
|
||||
{
|
||||
FPreset *self = ((FPreset*)preset->data);
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumSamples;i++)
|
||||
{
|
||||
FSample *sample = &self->Samples[i];
|
||||
ALfontsound *sound = sample->Sound;
|
||||
fluid_voice_t *voice;
|
||||
ALsizei m;
|
||||
|
||||
if(!(key >= sound->MinKey && key <= sound->MaxKey && vel >= sound->MinVelocity && vel <= sound->MaxVelocity))
|
||||
continue;
|
||||
|
||||
voice = fluid_synth_alloc_voice(synth, STATIC_CAST(fluid_sample_t, sample), channel, key, vel);
|
||||
if(voice == NULL) return FLUID_FAILED;
|
||||
|
||||
fluid_voice_gen_set(voice, GEN_MODLFOTOPITCH, sound->ModLfoToPitch);
|
||||
fluid_voice_gen_set(voice, GEN_VIBLFOTOPITCH, sound->VibratoLfoToPitch);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVTOPITCH, sound->ModEnvToPitch);
|
||||
fluid_voice_gen_set(voice, GEN_FILTERFC, sound->FilterCutoff);
|
||||
fluid_voice_gen_set(voice, GEN_FILTERQ, sound->FilterQ);
|
||||
fluid_voice_gen_set(voice, GEN_MODLFOTOFILTERFC, sound->ModLfoToFilterCutoff);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVTOFILTERFC, sound->ModEnvToFilterCutoff);
|
||||
fluid_voice_gen_set(voice, GEN_MODLFOTOVOL, sound->ModLfoToVolume);
|
||||
fluid_voice_gen_set(voice, GEN_CHORUSSEND, sound->ChorusSend);
|
||||
fluid_voice_gen_set(voice, GEN_REVERBSEND, sound->ReverbSend);
|
||||
fluid_voice_gen_set(voice, GEN_PAN, sound->Pan);
|
||||
fluid_voice_gen_set(voice, GEN_MODLFODELAY, sound->ModLfo.Delay);
|
||||
fluid_voice_gen_set(voice, GEN_MODLFOFREQ, sound->ModLfo.Frequency);
|
||||
fluid_voice_gen_set(voice, GEN_VIBLFODELAY, sound->VibratoLfo.Delay);
|
||||
fluid_voice_gen_set(voice, GEN_VIBLFOFREQ, sound->VibratoLfo.Frequency);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVDELAY, sound->ModEnv.DelayTime);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVATTACK, sound->ModEnv.AttackTime);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVHOLD, sound->ModEnv.HoldTime);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVDECAY, sound->ModEnv.DecayTime);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVSUSTAIN, sound->ModEnv.SustainAttn);
|
||||
fluid_voice_gen_set(voice, GEN_MODENVRELEASE, sound->ModEnv.ReleaseTime);
|
||||
fluid_voice_gen_set(voice, GEN_KEYTOMODENVHOLD, sound->ModEnv.KeyToHoldTime);
|
||||
fluid_voice_gen_set(voice, GEN_KEYTOMODENVDECAY, sound->ModEnv.KeyToDecayTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVDELAY, sound->VolEnv.DelayTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVATTACK, sound->VolEnv.AttackTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVHOLD, sound->VolEnv.HoldTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVDECAY, sound->VolEnv.DecayTime);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVSUSTAIN, sound->VolEnv.SustainAttn);
|
||||
fluid_voice_gen_set(voice, GEN_VOLENVRELEASE, sound->VolEnv.ReleaseTime);
|
||||
fluid_voice_gen_set(voice, GEN_KEYTOVOLENVHOLD, sound->VolEnv.KeyToHoldTime);
|
||||
fluid_voice_gen_set(voice, GEN_KEYTOVOLENVDECAY, sound->VolEnv.KeyToDecayTime);
|
||||
fluid_voice_gen_set(voice, GEN_ATTENUATION, sound->Attenuation);
|
||||
fluid_voice_gen_set(voice, GEN_COARSETUNE, sound->CoarseTuning);
|
||||
fluid_voice_gen_set(voice, GEN_FINETUNE, sound->FineTuning);
|
||||
fluid_voice_gen_set(voice, GEN_SAMPLEMODE, getSf2LoopMode(sound->LoopMode));
|
||||
fluid_voice_gen_set(voice, GEN_SCALETUNE, sound->TuningScale);
|
||||
fluid_voice_gen_set(voice, GEN_EXCLUSIVECLASS, sound->ExclusiveClass);
|
||||
for(m = 0;m < sample->NumMods;m++)
|
||||
fluid_voice_add_mod(voice, &sample->Mods[m], FLUID_VOICE_OVERWRITE);
|
||||
|
||||
fluid_synth_start_voice(synth, voice);
|
||||
}
|
||||
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
|
||||
typedef struct FSfont {
|
||||
DERIVE_FROM_TYPE(fluid_sfont_t);
|
||||
|
||||
char Name[16];
|
||||
|
||||
FPreset *Presets;
|
||||
ALsizei NumPresets;
|
||||
|
||||
ALsizei CurrentPos;
|
||||
} FSfont;
|
||||
|
||||
static int FSfont_free(fluid_sfont_t *sfont);
|
||||
static char* FSfont_getName(fluid_sfont_t *sfont);
|
||||
static fluid_preset_t* FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum);
|
||||
static void FSfont_iterStart(fluid_sfont_t *sfont);
|
||||
static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset);
|
||||
|
||||
static void FSfont_Construct(FSfont *self, ALsoundfont *sfont)
|
||||
{
|
||||
STATIC_CAST(fluid_sfont_t, self)->data = self;
|
||||
STATIC_CAST(fluid_sfont_t, self)->id = FLUID_FAILED;
|
||||
STATIC_CAST(fluid_sfont_t, self)->free = FSfont_free;
|
||||
STATIC_CAST(fluid_sfont_t, self)->get_name = FSfont_getName;
|
||||
STATIC_CAST(fluid_sfont_t, self)->get_preset = FSfont_getPreset;
|
||||
STATIC_CAST(fluid_sfont_t, self)->iteration_start = FSfont_iterStart;
|
||||
STATIC_CAST(fluid_sfont_t, self)->iteration_next = FSfont_iterNext;
|
||||
|
||||
memset(self->Name, 0, sizeof(self->Name));
|
||||
self->CurrentPos = 0;
|
||||
self->NumPresets = 0;
|
||||
self->Presets = calloc(1, sfont->NumPresets * sizeof(self->Presets[0]));
|
||||
if(self->Presets)
|
||||
{
|
||||
ALsizei i;
|
||||
self->NumPresets = sfont->NumPresets;
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
FPreset_Construct(&self->Presets[i], sfont->Presets[i], STATIC_CAST(fluid_sfont_t, self));
|
||||
}
|
||||
}
|
||||
|
||||
static void FSfont_Destruct(FSfont *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
FPreset_Destruct(&self->Presets[i]);
|
||||
free(self->Presets);
|
||||
self->Presets = NULL;
|
||||
self->NumPresets = 0;
|
||||
self->CurrentPos = 0;
|
||||
}
|
||||
|
||||
static int FSfont_free(fluid_sfont_t *sfont)
|
||||
{
|
||||
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
{
|
||||
if(!FPreset_canDelete(&self->Presets[i]))
|
||||
return 1;
|
||||
}
|
||||
|
||||
FSfont_Destruct(self);
|
||||
free(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* FSfont_getName(fluid_sfont_t *sfont)
|
||||
{
|
||||
return STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->Name;
|
||||
}
|
||||
|
||||
static fluid_preset_t *FSfont_getPreset(fluid_sfont_t *sfont, unsigned int bank, unsigned int prenum)
|
||||
{
|
||||
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
{
|
||||
FPreset *preset = &self->Presets[i];
|
||||
if(preset->Bank == (int)bank && preset->Preset == (int)prenum)
|
||||
return STATIC_CAST(fluid_preset_t, preset);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void FSfont_iterStart(fluid_sfont_t *sfont)
|
||||
{
|
||||
STATIC_UPCAST(FSfont, fluid_sfont_t, sfont)->CurrentPos = 0;
|
||||
}
|
||||
|
||||
static int FSfont_iterNext(fluid_sfont_t *sfont, fluid_preset_t *preset)
|
||||
{
|
||||
FSfont *self = STATIC_UPCAST(FSfont, fluid_sfont_t, sfont);
|
||||
if(self->CurrentPos >= self->NumPresets)
|
||||
return 0;
|
||||
*preset = *STATIC_CAST(fluid_preset_t, &self->Presets[self->CurrentPos++]);
|
||||
preset->free = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
typedef struct FSynth {
|
||||
DERIVE_FROM_TYPE(MidiSynth);
|
||||
DERIVE_FROM_TYPE(fluid_sfloader_t);
|
||||
|
||||
fluid_settings_t *Settings;
|
||||
fluid_synth_t *Synth;
|
||||
int *FontIDs;
|
||||
ALsizei NumFontIDs;
|
||||
|
||||
ALboolean ForceGM2BankSelect;
|
||||
ALfloat GainScale;
|
||||
} FSynth;
|
||||
|
||||
static void FSynth_Construct(FSynth *self, ALCdevice *device);
|
||||
static void FSynth_Destruct(FSynth *self);
|
||||
static ALboolean FSynth_init(FSynth *self, ALCdevice *device);
|
||||
static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids);
|
||||
static void FSynth_setGain(FSynth *self, ALfloat gain);
|
||||
static void FSynth_stop(FSynth *self);
|
||||
static void FSynth_reset(FSynth *self);
|
||||
static void FSynth_update(FSynth *self, ALCdevice *device);
|
||||
static void FSynth_processQueue(FSynth *self, ALuint64 time);
|
||||
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
DECLARE_DEFAULT_ALLOCATORS(FSynth)
|
||||
DEFINE_MIDISYNTH_VTABLE(FSynth);
|
||||
|
||||
static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename);
|
||||
|
||||
|
||||
static void FSynth_Construct(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
|
||||
SET_VTABLE2(FSynth, MidiSynth, self);
|
||||
|
||||
STATIC_CAST(fluid_sfloader_t, self)->data = self;
|
||||
STATIC_CAST(fluid_sfloader_t, self)->free = NULL;
|
||||
STATIC_CAST(fluid_sfloader_t, self)->load = FSynth_loadSfont;
|
||||
|
||||
self->Settings = NULL;
|
||||
self->Synth = NULL;
|
||||
self->FontIDs = NULL;
|
||||
self->NumFontIDs = 0;
|
||||
self->ForceGM2BankSelect = AL_FALSE;
|
||||
self->GainScale = 0.2f;
|
||||
}
|
||||
|
||||
static void FSynth_Destruct(FSynth *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < self->NumFontIDs;i++)
|
||||
fluid_synth_sfunload(self->Synth, self->FontIDs[i], 0);
|
||||
free(self->FontIDs);
|
||||
self->FontIDs = NULL;
|
||||
self->NumFontIDs = 0;
|
||||
|
||||
if(self->Synth != NULL)
|
||||
delete_fluid_synth(self->Synth);
|
||||
self->Synth = NULL;
|
||||
|
||||
if(self->Settings != NULL)
|
||||
delete_fluid_settings(self->Settings);
|
||||
self->Settings = NULL;
|
||||
|
||||
MidiSynth_Destruct(STATIC_CAST(MidiSynth, self));
|
||||
}
|
||||
|
||||
static ALboolean FSynth_init(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
ALfloat vol;
|
||||
|
||||
if(ConfigValueFloat("midi", "volume", &vol))
|
||||
{
|
||||
if(!(vol <= 0.0f))
|
||||
{
|
||||
ERR("MIDI volume %f clamped to 0\n", vol);
|
||||
vol = 0.0f;
|
||||
}
|
||||
self->GainScale = powf(10.0f, vol / 20.0f);
|
||||
}
|
||||
|
||||
self->Settings = new_fluid_settings();
|
||||
if(!self->Settings)
|
||||
{
|
||||
ERR("Failed to create FluidSettings\n");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
fluid_settings_setint(self->Settings, "synth.polyphony", 256);
|
||||
fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale);
|
||||
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
|
||||
|
||||
self->Synth = new_fluid_synth(self->Settings);
|
||||
if(!self->Synth)
|
||||
{
|
||||
ERR("Failed to create FluidSynth\n");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
fluid_synth_add_sfloader(self->Synth, STATIC_CAST(fluid_sfloader_t, self));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
static fluid_sfont_t *FSynth_loadSfont(fluid_sfloader_t *loader, const char *filename)
|
||||
{
|
||||
FSynth *self = STATIC_UPCAST(FSynth, fluid_sfloader_t, loader);
|
||||
FSfont *sfont;
|
||||
int idx;
|
||||
|
||||
if(!filename || sscanf(filename, "_al_internal %d", &idx) != 1)
|
||||
return NULL;
|
||||
if(idx < 0 || idx >= STATIC_CAST(MidiSynth, self)->NumSoundfonts)
|
||||
{
|
||||
ERR("Received invalid soundfont index %d (max: %d)\n", idx, STATIC_CAST(MidiSynth, self)->NumSoundfonts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sfont = calloc(1, sizeof(sfont[0]));
|
||||
if(!sfont) return NULL;
|
||||
|
||||
FSfont_Construct(sfont, STATIC_CAST(MidiSynth, self)->Soundfonts[idx]);
|
||||
return STATIC_CAST(fluid_sfont_t, sfont);
|
||||
}
|
||||
|
||||
static ALenum FSynth_selectSoundfonts(FSynth *self, ALCcontext *context, ALsizei count, const ALuint *ids)
|
||||
{
|
||||
int *fontid;
|
||||
ALenum ret;
|
||||
ALsizei i;
|
||||
|
||||
ret = MidiSynth_selectSoundfonts(STATIC_CAST(MidiSynth, self), context, count, ids);
|
||||
if(ret != AL_NO_ERROR) return ret;
|
||||
|
||||
ALCdevice_Lock(context->Device);
|
||||
for(i = 0;i < 16;i++)
|
||||
fluid_synth_all_sounds_off(self->Synth, i);
|
||||
ALCdevice_Unlock(context->Device);
|
||||
|
||||
fontid = malloc(count * sizeof(fontid[0]));
|
||||
if(fontid)
|
||||
{
|
||||
for(i = 0;i < STATIC_CAST(MidiSynth, self)->NumSoundfonts;i++)
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "_al_internal %d", i);
|
||||
|
||||
fontid[i] = fluid_synth_sfload(self->Synth, name, 0);
|
||||
if(fontid[i] == FLUID_FAILED)
|
||||
ERR("Failed to load selected soundfont %d\n", i);
|
||||
}
|
||||
|
||||
fontid = ExchangePtr((XchgPtr*)&self->FontIDs, fontid);
|
||||
count = ExchangeInt(&self->NumFontIDs, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Failed to allocate space for %d font IDs!\n", count);
|
||||
fontid = ExchangePtr((XchgPtr*)&self->FontIDs, NULL);
|
||||
count = ExchangeInt(&self->NumFontIDs, 0);
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
fluid_synth_sfunload(self->Synth, fontid[i], 0);
|
||||
free(fontid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_setGain(FSynth *self, ALfloat gain)
|
||||
{
|
||||
fluid_settings_setnum(self->Settings, "synth.gain", self->GainScale * gain);
|
||||
fluid_synth_set_gain(self->Synth, self->GainScale * gain);
|
||||
MidiSynth_setGain(STATIC_CAST(MidiSynth, self), gain);
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_stop(FSynth *self)
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALuint64 curtime;
|
||||
ALsizei chan;
|
||||
|
||||
/* Make sure all pending events are processed. */
|
||||
curtime = MidiSynth_getTime(synth);
|
||||
FSynth_processQueue(self, curtime);
|
||||
|
||||
/* All notes off */
|
||||
for(chan = 0;chan < 16;chan++)
|
||||
fluid_synth_cc(self->Synth, chan, CTRL_ALLNOTESOFF, 0);
|
||||
|
||||
MidiSynth_stop(STATIC_CAST(MidiSynth, self));
|
||||
}
|
||||
|
||||
static void FSynth_reset(FSynth *self)
|
||||
{
|
||||
/* Reset to power-up status. */
|
||||
fluid_synth_system_reset(self->Synth);
|
||||
|
||||
MidiSynth_reset(STATIC_CAST(MidiSynth, self));
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_update(FSynth *self, ALCdevice *device)
|
||||
{
|
||||
fluid_settings_setnum(self->Settings, "synth.sample-rate", device->Frequency);
|
||||
fluid_synth_set_sample_rate(self->Synth, device->Frequency);
|
||||
MidiSynth_update(STATIC_CAST(MidiSynth, self), device);
|
||||
}
|
||||
|
||||
|
||||
static void FSynth_processQueue(FSynth *self, ALuint64 time)
|
||||
{
|
||||
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
|
||||
|
||||
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
|
||||
{
|
||||
const MidiEvent *evt = &queue->events[queue->pos];
|
||||
|
||||
if(evt->event == SYSEX_EVENT)
|
||||
{
|
||||
static const ALbyte gm2_on[] = { 0x7E, 0x7F, 0x09, 0x03 };
|
||||
static const ALbyte gm2_off[] = { 0x7E, 0x7F, 0x09, 0x02 };
|
||||
int handled = 0;
|
||||
|
||||
fluid_synth_sysex(self->Synth, evt->param.sysex.data, evt->param.sysex.size, NULL, NULL, &handled, 0);
|
||||
if(!handled && evt->param.sysex.size >= (ALsizei)sizeof(gm2_on))
|
||||
{
|
||||
if(memcmp(evt->param.sysex.data, gm2_on, sizeof(gm2_on)) == 0)
|
||||
self->ForceGM2BankSelect = AL_TRUE;
|
||||
else if(memcmp(evt->param.sysex.data, gm2_off, sizeof(gm2_off)) == 0)
|
||||
self->ForceGM2BankSelect = AL_FALSE;
|
||||
}
|
||||
}
|
||||
else switch((evt->event&0xF0))
|
||||
{
|
||||
case AL_NOTEOFF_SOFT:
|
||||
fluid_synth_noteoff(self->Synth, (evt->event&0x0F), evt->param.val[0]);
|
||||
break;
|
||||
case AL_NOTEON_SOFT:
|
||||
fluid_synth_noteon(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
|
||||
break;
|
||||
case AL_KEYPRESSURE_SOFT:
|
||||
break;
|
||||
|
||||
case AL_CONTROLLERCHANGE_SOFT:
|
||||
if(self->ForceGM2BankSelect)
|
||||
{
|
||||
int chan = (evt->event&0x0F);
|
||||
if(evt->param.val[0] == CTRL_BANKSELECT_MSB)
|
||||
{
|
||||
if(evt->param.val[1] == 120 && (chan == 9 || chan == 10))
|
||||
fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_DRUM);
|
||||
else if(evt->param.val[1] == 121)
|
||||
fluid_synth_set_channel_type(self->Synth, chan, CHANNEL_TYPE_MELODIC);
|
||||
break;
|
||||
}
|
||||
if(evt->param.val[0] == CTRL_BANKSELECT_LSB)
|
||||
{
|
||||
fluid_synth_bank_select(self->Synth, chan, evt->param.val[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fluid_synth_cc(self->Synth, (evt->event&0x0F), evt->param.val[0], evt->param.val[1]);
|
||||
break;
|
||||
case AL_PROGRAMCHANGE_SOFT:
|
||||
fluid_synth_program_change(self->Synth, (evt->event&0x0F), evt->param.val[0]);
|
||||
break;
|
||||
|
||||
case AL_CHANNELPRESSURE_SOFT:
|
||||
fluid_synth_channel_pressure(self->Synth, (evt->event&0x0F), evt->param.val[0]);
|
||||
break;
|
||||
|
||||
case AL_PITCHBEND_SOFT:
|
||||
fluid_synth_pitch_bend(self->Synth, (evt->event&0x0F), (evt->param.val[0]&0x7F) |
|
||||
((evt->param.val[1]&0x7F)<<7));
|
||||
break;
|
||||
}
|
||||
|
||||
queue->pos++;
|
||||
}
|
||||
}
|
||||
|
||||
static void FSynth_process(FSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALenum state = synth->State;
|
||||
ALuint64 curtime;
|
||||
ALuint total = 0;
|
||||
|
||||
if(state == AL_INITIAL)
|
||||
return;
|
||||
if(state != AL_PLAYING)
|
||||
{
|
||||
fluid_synth_write_float(self->Synth, SamplesToDo, DryBuffer[FrontLeft], 0, 1,
|
||||
DryBuffer[FrontRight], 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
curtime = MidiSynth_getTime(synth);
|
||||
while(total < SamplesToDo)
|
||||
{
|
||||
ALuint64 time, diff;
|
||||
ALint tonext;
|
||||
|
||||
time = MidiSynth_getNextEvtTime(synth);
|
||||
diff = maxu64(time, curtime) - curtime;
|
||||
if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
|
||||
{
|
||||
/* If there's no pending event, or if it's more than 1 second
|
||||
* away, do as many samples as we can. */
|
||||
tonext = INT_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Figure out how many samples until the next event. */
|
||||
tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
|
||||
tonext -= total;
|
||||
}
|
||||
|
||||
if(tonext > 0)
|
||||
{
|
||||
ALuint todo = minu(tonext, SamplesToDo-total);
|
||||
fluid_synth_write_float(self->Synth, todo, DryBuffer[FrontLeft], total, 1,
|
||||
DryBuffer[FrontRight], total, 1);
|
||||
total += todo;
|
||||
tonext -= todo;
|
||||
}
|
||||
if(total < SamplesToDo && tonext <= 0)
|
||||
FSynth_processQueue(self, time);
|
||||
}
|
||||
|
||||
synth->SamplesDone += SamplesToDo;
|
||||
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
|
||||
synth->SamplesDone %= synth->SampleRate;
|
||||
}
|
||||
|
||||
|
||||
MidiSynth *FSynth_create(ALCdevice *device)
|
||||
{
|
||||
FSynth *synth;
|
||||
|
||||
if(!LoadFSynth())
|
||||
return NULL;
|
||||
|
||||
synth = FSynth_New(sizeof(*synth));
|
||||
if(!synth)
|
||||
{
|
||||
ERR("Failed to allocate FSynth\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(synth, 0, sizeof(*synth));
|
||||
FSynth_Construct(synth, device);
|
||||
|
||||
if(FSynth_init(synth, device) == AL_FALSE)
|
||||
{
|
||||
DELETE_OBJ(STATIC_CAST(MidiSynth, synth));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return STATIC_CAST(MidiSynth, synth);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
MidiSynth *FSynth_create(ALCdevice* UNUSED(device))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
+1377
File diff suppressed because it is too large
Load Diff
+140
@@ -0,0 +1,140 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alError.h"
|
||||
#include "evtqueue.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
|
||||
typedef struct SSynth {
|
||||
DERIVE_FROM_TYPE(MidiSynth);
|
||||
} SSynth;
|
||||
|
||||
static void SSynth_mixSamples(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
|
||||
static void SSynth_Construct(SSynth *self, ALCdevice *device);
|
||||
static void SSynth_Destruct(SSynth *self);
|
||||
static DECLARE_FORWARD3(SSynth, MidiSynth, ALenum, selectSoundfonts, ALCcontext*, ALsizei, const ALuint*)
|
||||
static DECLARE_FORWARD1(SSynth, MidiSynth, void, setGain, ALfloat)
|
||||
static DECLARE_FORWARD(SSynth, MidiSynth, void, stop)
|
||||
static DECLARE_FORWARD(SSynth, MidiSynth, void, reset)
|
||||
static void SSynth_update(SSynth *self, ALCdevice *device);
|
||||
static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE]);
|
||||
DECLARE_DEFAULT_ALLOCATORS(SSynth)
|
||||
DEFINE_MIDISYNTH_VTABLE(SSynth);
|
||||
|
||||
|
||||
static void SSynth_Construct(SSynth *self, ALCdevice *device)
|
||||
{
|
||||
MidiSynth_Construct(STATIC_CAST(MidiSynth, self), device);
|
||||
SET_VTABLE2(SSynth, MidiSynth, self);
|
||||
}
|
||||
|
||||
static void SSynth_Destruct(SSynth* UNUSED(self))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void SSynth_update(SSynth* UNUSED(self), ALCdevice* UNUSED(device))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void SSynth_mixSamples(SSynth* UNUSED(self), ALuint UNUSED(SamplesToDo), ALfloatBUFFERSIZE *restrict UNUSED(DryBuffer))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void SSynth_processQueue(SSynth *self, ALuint64 time)
|
||||
{
|
||||
EvtQueue *queue = &STATIC_CAST(MidiSynth, self)->EventQueue;
|
||||
|
||||
while(queue->pos < queue->size && queue->events[queue->pos].time <= time)
|
||||
queue->pos++;
|
||||
}
|
||||
|
||||
static void SSynth_process(SSynth *self, ALuint SamplesToDo, ALfloat (*restrict DryBuffer)[BUFFERSIZE])
|
||||
{
|
||||
MidiSynth *synth = STATIC_CAST(MidiSynth, self);
|
||||
ALenum state = synth->State;
|
||||
ALuint64 curtime;
|
||||
ALuint total = 0;
|
||||
|
||||
if(state == AL_INITIAL)
|
||||
return;
|
||||
if(state != AL_PLAYING)
|
||||
{
|
||||
SSynth_mixSamples(self, SamplesToDo, DryBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
curtime = MidiSynth_getTime(synth);
|
||||
while(total < SamplesToDo)
|
||||
{
|
||||
ALuint64 time, diff;
|
||||
ALint tonext;
|
||||
|
||||
time = MidiSynth_getNextEvtTime(synth);
|
||||
diff = maxu64(time, curtime) - curtime;
|
||||
if(diff >= MIDI_CLOCK_RES || time == UINT64_MAX)
|
||||
{
|
||||
/* If there's no pending event, or if it's more than 1 second
|
||||
* away, do as many samples as we can. */
|
||||
tonext = INT_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Figure out how many samples until the next event. */
|
||||
tonext = (ALint)((diff*synth->SampleRate + (MIDI_CLOCK_RES-1)) / MIDI_CLOCK_RES);
|
||||
tonext -= total;
|
||||
/* For efficiency reasons, try to mix a multiple of 64 samples
|
||||
* (~1ms @ 44.1khz) before processing the next event. */
|
||||
tonext = (tonext+63) & ~63;
|
||||
}
|
||||
|
||||
if(tonext > 0)
|
||||
{
|
||||
ALuint todo = mini(tonext, SamplesToDo-total);
|
||||
SSynth_mixSamples(self, todo, DryBuffer);
|
||||
total += todo;
|
||||
tonext -= todo;
|
||||
}
|
||||
if(total < SamplesToDo && tonext <= 0)
|
||||
SSynth_processQueue(self, time);
|
||||
}
|
||||
|
||||
synth->SamplesDone += SamplesToDo;
|
||||
synth->ClockBase += (synth->SamplesDone/synth->SampleRate) * MIDI_CLOCK_RES;
|
||||
synth->SamplesDone %= synth->SampleRate;
|
||||
}
|
||||
|
||||
|
||||
MidiSynth *SSynth_create(ALCdevice *device)
|
||||
{
|
||||
SSynth *synth;
|
||||
|
||||
/* This option is temporary. Once this synth is in a more usable state, a
|
||||
* more generic selector should be used. */
|
||||
if(!GetConfigValueBool("midi", "internal-synth", 0))
|
||||
{
|
||||
TRACE("Not using internal MIDI synth\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
synth = SSynth_New(sizeof(*synth));
|
||||
if(!synth)
|
||||
{
|
||||
ERR("Failed to allocate SSynth\n");
|
||||
return NULL;
|
||||
}
|
||||
SSynth_Construct(synth, device);
|
||||
return STATIC_CAST(MidiSynth, synth);
|
||||
}
|
||||
+510
@@ -0,0 +1,510 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alSource.h"
|
||||
#include "alBuffer.h"
|
||||
#include "alListener.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
|
||||
|
||||
|
||||
static inline HrtfMixerFunc SelectHrtfMixer(void)
|
||||
{
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixHrtf_SSE;
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixHrtf_Neon;
|
||||
#endif
|
||||
|
||||
return MixHrtf_C;
|
||||
}
|
||||
|
||||
static inline MixerFunc SelectMixer(void)
|
||||
{
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Mix_SSE;
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Mix_Neon;
|
||||
#endif
|
||||
|
||||
return Mix_C;
|
||||
}
|
||||
|
||||
static inline ResamplerFunc SelectResampler(enum Resampler Resampler, ALuint increment)
|
||||
{
|
||||
if(increment == FRACTIONONE)
|
||||
return Resample_copy32_C;
|
||||
switch(Resampler)
|
||||
{
|
||||
case PointResampler:
|
||||
return Resample_point32_C;
|
||||
case LinearResampler:
|
||||
#ifdef HAVE_SSE4_1
|
||||
if((CPUCapFlags&CPU_CAP_SSE4_1))
|
||||
return Resample_lerp32_SSE41;
|
||||
#endif
|
||||
#ifdef HAVE_SSE2
|
||||
if((CPUCapFlags&CPU_CAP_SSE2))
|
||||
return Resample_lerp32_SSE2;
|
||||
#endif
|
||||
return Resample_lerp32_C;
|
||||
case CubicResampler:
|
||||
return Resample_cubic32_C;
|
||||
case ResamplerMax:
|
||||
/* Shouldn't happen */
|
||||
break;
|
||||
}
|
||||
|
||||
return Resample_point32_C;
|
||||
}
|
||||
|
||||
|
||||
static inline ALfloat Sample_ALbyte(ALbyte val)
|
||||
{ return val * (1.0f/127.0f); }
|
||||
|
||||
static inline ALfloat Sample_ALshort(ALshort val)
|
||||
{ return val * (1.0f/32767.0f); }
|
||||
|
||||
static inline ALfloat Sample_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static void Load_##T(ALfloat *dst, const T *src, ALuint srcstep, ALuint samples)\
|
||||
{ \
|
||||
ALuint i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
dst[i] = Sample_##T(src[i*srcstep]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALuint srcstep, enum FmtType srctype, ALuint samples)
|
||||
{
|
||||
switch(srctype)
|
||||
{
|
||||
case FmtByte:
|
||||
Load_ALbyte(dst, src, srcstep, samples);
|
||||
break;
|
||||
case FmtShort:
|
||||
Load_ALshort(dst, src, srcstep, samples);
|
||||
break;
|
||||
case FmtFloat:
|
||||
Load_ALfloat(dst, src, srcstep, samples);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void SilenceSamples(ALfloat *dst, ALuint samples)
|
||||
{
|
||||
ALuint i;
|
||||
for(i = 0;i < samples;i++)
|
||||
dst[i] = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
|
||||
ALfloat *restrict dst, const ALfloat *restrict src,
|
||||
ALuint numsamples, enum ActiveFilters type)
|
||||
{
|
||||
ALuint i;
|
||||
switch(type)
|
||||
{
|
||||
case AF_None:
|
||||
break;
|
||||
|
||||
case AF_LowPass:
|
||||
ALfilterState_process(lpfilter, dst, src, numsamples);
|
||||
return dst;
|
||||
case AF_HighPass:
|
||||
ALfilterState_process(hpfilter, dst, src, numsamples);
|
||||
return dst;
|
||||
|
||||
case AF_BandPass:
|
||||
for(i = 0;i < numsamples;)
|
||||
{
|
||||
ALfloat temp[64];
|
||||
ALuint todo = minu(64, numsamples-i);
|
||||
|
||||
ALfilterState_process(lpfilter, temp, src+i, todo);
|
||||
ALfilterState_process(hpfilter, dst+i, temp, todo);
|
||||
i += todo;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
ALvoid MixSource(ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo)
|
||||
{
|
||||
MixerFunc Mix;
|
||||
HrtfMixerFunc HrtfMix;
|
||||
ResamplerFunc Resample;
|
||||
ALsource *Source = src->Source;
|
||||
ALbufferlistitem *BufferListItem;
|
||||
ALuint DataPosInt, DataPosFrac;
|
||||
ALboolean Looping;
|
||||
ALuint increment;
|
||||
enum Resampler Resampler;
|
||||
ALenum State;
|
||||
ALuint OutPos;
|
||||
ALuint NumChannels;
|
||||
ALuint SampleSize;
|
||||
ALint64 DataSize64;
|
||||
ALuint chan, j;
|
||||
|
||||
/* Get source info */
|
||||
State = Source->state;
|
||||
BufferListItem = ATOMIC_LOAD(&Source->current_buffer);
|
||||
DataPosInt = Source->position;
|
||||
DataPosFrac = Source->position_fraction;
|
||||
Looping = Source->Looping;
|
||||
increment = src->Step;
|
||||
Resampler = (increment==FRACTIONONE) ? PointResampler : Source->Resampler;
|
||||
NumChannels = Source->NumChannels;
|
||||
SampleSize = Source->SampleSize;
|
||||
|
||||
Mix = SelectMixer();
|
||||
HrtfMix = SelectHrtfMixer();
|
||||
Resample = SelectResampler(Resampler, increment);
|
||||
|
||||
OutPos = 0;
|
||||
do {
|
||||
const ALuint BufferPrePadding = ResamplerPrePadding[Resampler];
|
||||
const ALuint BufferPadding = ResamplerPadding[Resampler];
|
||||
ALuint SrcBufferSize, DstBufferSize;
|
||||
|
||||
/* Figure out how many buffer samples will be needed */
|
||||
DataSize64 = SamplesToDo-OutPos;
|
||||
DataSize64 *= increment;
|
||||
DataSize64 += DataPosFrac+FRACTIONMASK;
|
||||
DataSize64 >>= FRACTIONBITS;
|
||||
DataSize64 += BufferPadding+BufferPrePadding;
|
||||
|
||||
SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
|
||||
|
||||
/* Figure out how many samples we can actually mix from this. */
|
||||
DataSize64 = SrcBufferSize;
|
||||
DataSize64 -= BufferPadding+BufferPrePadding;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
DstBufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
|
||||
DstBufferSize = minu(DstBufferSize, (SamplesToDo-OutPos));
|
||||
|
||||
/* Some mixers like having a multiple of 4, so try to give that unless
|
||||
* this is the last update. */
|
||||
if(OutPos+DstBufferSize < SamplesToDo)
|
||||
DstBufferSize &= ~3;
|
||||
|
||||
for(chan = 0;chan < NumChannels;chan++)
|
||||
{
|
||||
const ALfloat *ResampledData;
|
||||
ALfloat *SrcData = Device->SourceData;
|
||||
ALuint SrcDataSize = 0;
|
||||
|
||||
if(Source->SourceType == AL_STATIC)
|
||||
{
|
||||
const ALbuffer *ALBuffer = BufferListItem->buffer;
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
ALuint DataSize;
|
||||
ALuint pos;
|
||||
|
||||
/* If current pos is beyond the loop range, do not loop */
|
||||
if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
|
||||
{
|
||||
Looping = AL_FALSE;
|
||||
|
||||
if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
DataSize = BufferPrePadding - DataPosInt;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
/* Copy what's left to play in the source buffer, and clear the
|
||||
* rest of the temp buffer */
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
|
||||
SrcDataSize += SrcBufferSize - SrcDataSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALuint LoopStart = ALBuffer->LoopStart;
|
||||
ALuint LoopEnd = ALBuffer->LoopEnd;
|
||||
|
||||
if(DataPosInt >= LoopStart)
|
||||
{
|
||||
pos = DataPosInt-LoopStart;
|
||||
while(pos < BufferPrePadding)
|
||||
pos += LoopEnd-LoopStart;
|
||||
pos -= BufferPrePadding;
|
||||
pos += LoopStart;
|
||||
}
|
||||
else if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
DataSize = BufferPrePadding - DataPosInt;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
/* Copy what's left of this loop iteration, then copy repeats
|
||||
* of the loop section */
|
||||
DataSize = LoopEnd - pos;
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(pos*NumChannels + chan)*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
DataSize = LoopEnd-LoopStart;
|
||||
while(SrcBufferSize > SrcDataSize)
|
||||
{
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[SrcDataSize], &Data[(LoopStart*NumChannels + chan)*SampleSize],
|
||||
NumChannels, ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Crawl the buffer queue to fill in the temp buffer */
|
||||
ALbufferlistitem *tmpiter = BufferListItem;
|
||||
ALuint pos;
|
||||
|
||||
if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
pos = BufferPrePadding - DataPosInt;
|
||||
while(pos > 0)
|
||||
{
|
||||
ALbufferlistitem *prev;
|
||||
if((prev=tmpiter->prev) != NULL)
|
||||
tmpiter = prev;
|
||||
else if(Looping)
|
||||
{
|
||||
while(tmpiter->next)
|
||||
tmpiter = tmpiter->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos);
|
||||
|
||||
SilenceSamples(&SrcData[SrcDataSize], DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
|
||||
pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(tmpiter->buffer)
|
||||
{
|
||||
if((ALuint)tmpiter->buffer->SampleLen > pos)
|
||||
{
|
||||
pos = tmpiter->buffer->SampleLen - pos;
|
||||
break;
|
||||
}
|
||||
pos -= tmpiter->buffer->SampleLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(tmpiter && SrcBufferSize > SrcDataSize)
|
||||
{
|
||||
const ALbuffer *ALBuffer;
|
||||
if((ALBuffer=tmpiter->buffer) != NULL)
|
||||
{
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
ALuint DataSize = ALBuffer->SampleLen;
|
||||
|
||||
/* Skip the data already played */
|
||||
if(DataSize <= pos)
|
||||
pos -= DataSize;
|
||||
else
|
||||
{
|
||||
Data += (pos*NumChannels + chan)*SampleSize;
|
||||
DataSize -= pos;
|
||||
pos -= pos;
|
||||
|
||||
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
|
||||
LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
|
||||
ALBuffer->FmtType, DataSize);
|
||||
SrcDataSize += DataSize;
|
||||
}
|
||||
}
|
||||
tmpiter = tmpiter->next;
|
||||
if(!tmpiter && Looping)
|
||||
tmpiter = ATOMIC_LOAD(&Source->queue);
|
||||
else if(!tmpiter)
|
||||
{
|
||||
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
|
||||
SrcDataSize += SrcBufferSize - SrcDataSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now resample, then filter and mix to the appropriate outputs. */
|
||||
ResampledData = Resample(
|
||||
&SrcData[BufferPrePadding], DataPosFrac, increment,
|
||||
Device->ResampledData, DstBufferSize
|
||||
);
|
||||
{
|
||||
DirectParams *parms = &src->Direct;
|
||||
const ALfloat *samples;
|
||||
|
||||
samples = DoFilters(
|
||||
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
|
||||
Device->FilteredData, ResampledData, DstBufferSize,
|
||||
parms->Filters[chan].ActiveType
|
||||
);
|
||||
if(!src->IsHrtf)
|
||||
Mix(samples, MaxChannels, parms->OutBuffer, parms->Mix.Gains[chan],
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
else
|
||||
HrtfMix(parms->OutBuffer, samples, parms->Counter, src->Offset,
|
||||
OutPos, parms->Mix.Hrtf.IrSize, &parms->Mix.Hrtf.Params[chan],
|
||||
&parms->Mix.Hrtf.State[chan], DstBufferSize);
|
||||
}
|
||||
|
||||
for(j = 0;j < Device->NumAuxSends;j++)
|
||||
{
|
||||
SendParams *parms = &src->Send[j];
|
||||
const ALfloat *samples;
|
||||
|
||||
if(!parms->OutBuffer)
|
||||
continue;
|
||||
|
||||
samples = DoFilters(
|
||||
&parms->Filters[chan].LowPass, &parms->Filters[chan].HighPass,
|
||||
Device->FilteredData, ResampledData, DstBufferSize,
|
||||
parms->Filters[chan].ActiveType
|
||||
);
|
||||
Mix(samples, 1, parms->OutBuffer, &parms->Gain,
|
||||
parms->Counter, OutPos, DstBufferSize);
|
||||
}
|
||||
}
|
||||
/* Update positions */
|
||||
DataPosFrac += increment*DstBufferSize;
|
||||
DataPosInt += DataPosFrac>>FRACTIONBITS;
|
||||
DataPosFrac &= FRACTIONMASK;
|
||||
|
||||
OutPos += DstBufferSize;
|
||||
src->Offset += DstBufferSize;
|
||||
src->Direct.Counter = maxu(src->Direct.Counter, DstBufferSize) - DstBufferSize;
|
||||
for(j = 0;j < Device->NumAuxSends;j++)
|
||||
src->Send[j].Counter = maxu(src->Send[j].Counter, DstBufferSize) - DstBufferSize;
|
||||
|
||||
/* Handle looping sources */
|
||||
while(1)
|
||||
{
|
||||
const ALbuffer *ALBuffer;
|
||||
ALuint DataSize = 0;
|
||||
ALuint LoopStart = 0;
|
||||
ALuint LoopEnd = 0;
|
||||
|
||||
if((ALBuffer=BufferListItem->buffer) != NULL)
|
||||
{
|
||||
DataSize = ALBuffer->SampleLen;
|
||||
LoopStart = ALBuffer->LoopStart;
|
||||
LoopEnd = ALBuffer->LoopEnd;
|
||||
if(LoopEnd > DataPosInt)
|
||||
break;
|
||||
}
|
||||
|
||||
if(Looping && Source->SourceType == AL_STATIC)
|
||||
{
|
||||
assert(LoopEnd > LoopStart);
|
||||
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
|
||||
break;
|
||||
}
|
||||
|
||||
if(DataSize > DataPosInt)
|
||||
break;
|
||||
|
||||
if(!(BufferListItem=BufferListItem->next))
|
||||
{
|
||||
if(Looping)
|
||||
BufferListItem = ATOMIC_LOAD(&Source->queue);
|
||||
else
|
||||
{
|
||||
State = AL_STOPPED;
|
||||
BufferListItem = NULL;
|
||||
DataPosInt = 0;
|
||||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DataPosInt -= DataSize;
|
||||
}
|
||||
} while(State == AL_PLAYING && OutPos < SamplesToDo);
|
||||
|
||||
/* Update source info */
|
||||
Source->state = State;
|
||||
ATOMIC_STORE(&Source->current_buffer, BufferListItem);
|
||||
Source->position = DataPosInt;
|
||||
Source->position_fraction = DataPosFrac;
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alSource.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
|
||||
|
||||
static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
|
||||
{ return vals[0]; }
|
||||
static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
|
||||
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat cubic32(const ALfloat *vals, ALuint frac)
|
||||
{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac * (1.0f/FRACTIONONE)); }
|
||||
|
||||
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
|
||||
ALuint increment, ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
assert(increment==FRACTIONONE);
|
||||
#if defined(HAVE_SSE) || defined(HAVE_NEON)
|
||||
/* Avoid copying the source data if it's aligned like the destination. */
|
||||
if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
|
||||
return src;
|
||||
#endif
|
||||
memcpy(dst, src, numsamples*sizeof(ALfloat));
|
||||
return dst;
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Sampler) \
|
||||
const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \
|
||||
ALuint increment, ALfloat *restrict dst, ALuint numsamples) \
|
||||
{ \
|
||||
ALuint i; \
|
||||
for(i = 0;i < numsamples;i++) \
|
||||
{ \
|
||||
dst[i] = Sampler(src, frac); \
|
||||
\
|
||||
frac += increment; \
|
||||
src += frac>>FRACTIONBITS; \
|
||||
frac &= FRACTIONMASK; \
|
||||
} \
|
||||
return dst; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(point32)
|
||||
DECL_TEMPLATE(lerp32)
|
||||
DECL_TEMPLATE(cubic32)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples)
|
||||
{
|
||||
ALuint i;
|
||||
for(i = 0;i < numsamples;i++)
|
||||
*(dst++) = ALfilterState_processSingle(filter, *(src++));
|
||||
}
|
||||
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
const ALuint off = (Offset+c)&HRIR_MASK;
|
||||
Values[off][0] += Coeffs[c][0] * left;
|
||||
Values[off][1] += Coeffs[c][1] * right;
|
||||
Coeffs[c][0] += CoeffStep[c][0];
|
||||
Coeffs[c][1] += CoeffStep[c][1];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
const ALuint off = (Offset+c)&HRIR_MASK;
|
||||
Values[off][0] += Coeffs[c][0] * left;
|
||||
Values[off][1] += Coeffs[c][1] * right;
|
||||
}
|
||||
}
|
||||
|
||||
#define SUFFIX C
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
|
||||
|
||||
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
{
|
||||
ALfloat gain, step;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
{
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef MIXER_DEFS_H
|
||||
#define MIXER_DEFS_H
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
struct MixGains;
|
||||
|
||||
struct HrtfParams;
|
||||
struct HrtfState;
|
||||
|
||||
/* C resamplers */
|
||||
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||
|
||||
|
||||
/* C mixers */
|
||||
void MixHrtf_C(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
|
||||
ALuint BufferSize);
|
||||
void Mix_C(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
|
||||
/* SSE mixers */
|
||||
void MixHrtf_SSE(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
|
||||
ALuint BufferSize);
|
||||
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
|
||||
/* SSE resamplers */
|
||||
inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
pos_arr[0] = 0;
|
||||
frac_arr[0] = frac;
|
||||
for(i = 1;i < size;i++)
|
||||
{
|
||||
ALuint frac_tmp = frac_arr[i-1] + increment;
|
||||
pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
|
||||
frac_arr[i] = frac_tmp&FRACTIONMASK;
|
||||
}
|
||||
}
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples);
|
||||
|
||||
/* Neon mixers */
|
||||
void MixHrtf_Neon(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
const struct HrtfParams *hrtfparams, struct HrtfState *hrtfstate,
|
||||
ALuint BufferSize);
|
||||
void Mix_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
struct MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
|
||||
#endif /* MIXER_DEFS_H */
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alSource.h"
|
||||
|
||||
#include "hrtf.h"
|
||||
#include "mixer_defs.h"
|
||||
#include "align.h"
|
||||
|
||||
|
||||
#define REAL_MERGE(a,b) a##b
|
||||
#define MERGE(a,b) REAL_MERGE(a,b)
|
||||
|
||||
#define MixHrtf MERGE(MixHrtf_,SUFFIX)
|
||||
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint irSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
ALfloat left, ALfloat right);
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint irSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right);
|
||||
|
||||
|
||||
void MixHrtf(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos, const ALuint IrSize,
|
||||
const HrtfParams *hrtfparams, HrtfState *hrtfstate, ALuint BufferSize)
|
||||
{
|
||||
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
|
||||
ALuint Delay[2];
|
||||
ALfloat left, right;
|
||||
ALuint pos;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
Coeffs[c][0] = hrtfparams->Coeffs[c][0] - (hrtfparams->CoeffStep[c][0]*Counter);
|
||||
Coeffs[c][1] = hrtfparams->Coeffs[c][1] - (hrtfparams->CoeffStep[c][1]*Counter);
|
||||
}
|
||||
Delay[0] = hrtfparams->Delay[0] - (hrtfparams->DelayStep[0]*Counter);
|
||||
Delay[1] = hrtfparams->Delay[1] - (hrtfparams->DelayStep[1]*Counter);
|
||||
|
||||
for(pos = 0;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
|
||||
left = lerp(hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
|
||||
hrtfstate->History[(Offset-(Delay[0]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
|
||||
(Delay[0]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
|
||||
right = lerp(hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS))&SRC_HISTORY_MASK],
|
||||
hrtfstate->History[(Offset-(Delay[1]>>HRTFDELAY_BITS)-1)&SRC_HISTORY_MASK],
|
||||
(Delay[1]&HRTFDELAY_MASK)*(1.0f/HRTFDELAY_FRACONE));
|
||||
|
||||
Delay[0] += hrtfparams->DelayStep[0];
|
||||
Delay[1] += hrtfparams->DelayStep[1];
|
||||
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
|
||||
Offset++;
|
||||
|
||||
ApplyCoeffsStep(Offset, hrtfstate->Values, IrSize, Coeffs, hrtfparams->CoeffStep, left, right);
|
||||
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
OutPos++;
|
||||
}
|
||||
|
||||
Delay[0] >>= HRTFDELAY_BITS;
|
||||
Delay[1] >>= HRTFDELAY_BITS;
|
||||
for(;pos < BufferSize;pos++)
|
||||
{
|
||||
hrtfstate->History[Offset&SRC_HISTORY_MASK] = data[pos];
|
||||
left = hrtfstate->History[(Offset-Delay[0])&SRC_HISTORY_MASK];
|
||||
right = hrtfstate->History[(Offset-Delay[1])&SRC_HISTORY_MASK];
|
||||
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
|
||||
Offset++;
|
||||
|
||||
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
|
||||
OutBuffer[FrontLeft][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
OutBuffer[FrontRight][OutPos] += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
OutPos++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef MixHrtf
|
||||
|
||||
#undef MERGE
|
||||
#undef REAL_MERGE
|
||||
@@ -0,0 +1,118 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "hrtf.h"
|
||||
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
float32x4_t leftright4;
|
||||
{
|
||||
float32x2_t leftright2 = vdup_n_f32(0.0);
|
||||
leftright2 = vset_lane_f32(left, leftright2, 0);
|
||||
leftright2 = vset_lane_f32(right, leftright2, 1);
|
||||
leftright4 = vcombine_f32(leftright2, leftright2);
|
||||
}
|
||||
for(c = 0;c < IrSize;c += 2)
|
||||
{
|
||||
const ALuint o0 = (Offset+c)&HRIR_MASK;
|
||||
const ALuint o1 = (o0+1)&HRIR_MASK;
|
||||
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
|
||||
vld1_f32((float32_t*)&Values[o1][0]));
|
||||
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
|
||||
float32x4_t deltas = vld1q_f32(&CoeffStep[c][0]);
|
||||
|
||||
vals = vmlaq_f32(vals, coefs, leftright4);
|
||||
coefs = vaddq_f32(coefs, deltas);
|
||||
|
||||
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
|
||||
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
|
||||
vst1q_f32(&Coeffs[c][0], coefs);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
float32x4_t leftright4;
|
||||
{
|
||||
float32x2_t leftright2 = vdup_n_f32(0.0);
|
||||
leftright2 = vset_lane_f32(left, leftright2, 0);
|
||||
leftright2 = vset_lane_f32(right, leftright2, 1);
|
||||
leftright4 = vcombine_f32(leftright2, leftright2);
|
||||
}
|
||||
for(c = 0;c < IrSize;c += 2)
|
||||
{
|
||||
const ALuint o0 = (Offset+c)&HRIR_MASK;
|
||||
const ALuint o1 = (o0+1)&HRIR_MASK;
|
||||
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
|
||||
vld1_f32((float32_t*)&Values[o1][0]));
|
||||
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
|
||||
|
||||
vals = vmlaq_f32(vals, coefs, leftright4);
|
||||
|
||||
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
|
||||
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define SUFFIX Neon
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
|
||||
|
||||
void MixDirect_Neon(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
{
|
||||
ALfloat gain, step;
|
||||
float32x4_t gain4;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
{
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
for(;pos < BufferSize && (pos&3) != 0;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
gain4 = vdupq_n_f32(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
const float32x4_t val4 = vld1q_f32(&data[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = vaddq_f32(dry4, vmulq_f32(val4, gain4));
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef IN_IDE_PARSER
|
||||
/* KDevelop's parser won't recognize these defines that get added by the -msse
|
||||
* switch used to compile this source. Without them, xmmintrin.h fails to
|
||||
* declare anything. */
|
||||
#define __MMX__
|
||||
#define __SSE__
|
||||
#endif
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "alSource.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
static inline void ApplyCoeffsStep(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
const ALfloat (*restrict CoeffStep)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
|
||||
__m128 coeffs, deltas, imp0, imp1;
|
||||
__m128 vals = _mm_setzero_ps();
|
||||
ALuint i;
|
||||
|
||||
if((Offset&1))
|
||||
{
|
||||
const ALuint o0 = Offset&HRIR_MASK;
|
||||
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[0][0]);
|
||||
deltas = _mm_load_ps(&CoeffStep[0][0]);
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
|
||||
imp0 = _mm_mul_ps(lrlr, coeffs);
|
||||
coeffs = _mm_add_ps(coeffs, deltas);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Coeffs[0][0], coeffs);
|
||||
_mm_storel_pi((__m64*)&Values[o0][0], vals);
|
||||
for(i = 1;i < IrSize-1;i += 2)
|
||||
{
|
||||
const ALuint o2 = (Offset+i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
|
||||
deltas = _mm_load_ps(&CoeffStep[i+1][0]);
|
||||
vals = _mm_load_ps(&Values[o2][0]);
|
||||
imp1 = _mm_mul_ps(lrlr, coeffs);
|
||||
coeffs = _mm_add_ps(coeffs, deltas);
|
||||
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Coeffs[i+1][0], coeffs);
|
||||
_mm_store_ps(&Values[o2][0], vals);
|
||||
imp0 = imp1;
|
||||
}
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
|
||||
imp0 = _mm_movehl_ps(imp0, imp0);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_storel_pi((__m64*)&Values[o1][0], vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < IrSize;i += 2)
|
||||
{
|
||||
const ALuint o = (Offset + i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i][0]);
|
||||
deltas = _mm_load_ps(&CoeffStep[i][0]);
|
||||
vals = _mm_load_ps(&Values[o][0]);
|
||||
imp0 = _mm_mul_ps(lrlr, coeffs);
|
||||
coeffs = _mm_add_ps(coeffs, deltas);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Coeffs[i][0], coeffs);
|
||||
_mm_store_ps(&Values[o][0], vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ApplyCoeffs(ALuint Offset, ALfloat (*restrict Values)[2],
|
||||
const ALuint IrSize,
|
||||
ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
|
||||
__m128 vals = _mm_setzero_ps();
|
||||
__m128 coeffs;
|
||||
ALuint i;
|
||||
|
||||
if((Offset&1))
|
||||
{
|
||||
const ALuint o0 = Offset&HRIR_MASK;
|
||||
const ALuint o1 = (Offset+IrSize-1)&HRIR_MASK;
|
||||
__m128 imp0, imp1;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[0][0]);
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
|
||||
imp0 = _mm_mul_ps(lrlr, coeffs);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_storel_pi((__m64*)&Values[o0][0], vals);
|
||||
for(i = 1;i < IrSize-1;i += 2)
|
||||
{
|
||||
const ALuint o2 = (Offset+i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
|
||||
vals = _mm_load_ps(&Values[o2][0]);
|
||||
imp1 = _mm_mul_ps(lrlr, coeffs);
|
||||
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Values[o2][0], vals);
|
||||
imp0 = imp1;
|
||||
}
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
|
||||
imp0 = _mm_movehl_ps(imp0, imp0);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_storel_pi((__m64*)&Values[o1][0], vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < IrSize;i += 2)
|
||||
{
|
||||
const ALuint o = (Offset + i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i][0]);
|
||||
vals = _mm_load_ps(&Values[o][0]);
|
||||
vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
|
||||
_mm_store_ps(&Values[o][0], vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define SUFFIX SSE
|
||||
#include "mixer_inc.c"
|
||||
#undef SUFFIX
|
||||
|
||||
|
||||
void Mix_SSE(const ALfloat *data, ALuint OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
MixGains *Gains, ALuint Counter, ALuint OutPos, ALuint BufferSize)
|
||||
{
|
||||
ALfloat gain, step;
|
||||
__m128 gain4, step4;
|
||||
ALuint c;
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALuint pos = 0;
|
||||
gain = Gains[c].Current;
|
||||
step = Gains[c].Step;
|
||||
if(step != 1.0f && Counter > 0)
|
||||
{
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(BufferSize-pos > 3 && Counter-pos > 3)
|
||||
{
|
||||
gain4 = _mm_setr_ps(
|
||||
gain,
|
||||
gain * step,
|
||||
gain * step * step,
|
||||
gain * step * step * step
|
||||
);
|
||||
step4 = _mm_set1_ps(step * step * step * step);
|
||||
do {
|
||||
const __m128 val4 = _mm_load_ps(&data[pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
gain4 = _mm_mul_ps(gain4, step4);
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
pos += 4;
|
||||
} while(BufferSize-pos > 3 && Counter-pos > 3);
|
||||
gain = _mm_cvtss_f32(gain4);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(;pos < BufferSize && pos < Counter;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
gain *= step;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = Gains[c].Target;
|
||||
Gains[c].Current = gain;
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
for(;pos < BufferSize && (pos&3) != 0;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
|
||||
if(!(gain > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
gain4 = _mm_set1_ps(gain);
|
||||
for(;BufferSize-pos > 3;pos += 4)
|
||||
{
|
||||
const __m128 val4 = _mm_load_ps(&data[pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } pos_;
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALuint pos;
|
||||
ALuint i;
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
|
||||
|
||||
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
|
||||
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
|
||||
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
|
||||
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
|
||||
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
|
||||
|
||||
_mm_store_ps(&dst[i], out);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
|
||||
_mm_store_ps(pos_.f, _mm_castsi128_ps(pos4));
|
||||
}
|
||||
|
||||
pos = pos_.i[0];
|
||||
frac = _mm_cvtsi128_si32(frac4);
|
||||
|
||||
for(;i < numsamples;i++)
|
||||
{
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#include <smmintrin.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "mixer_defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } pos_;
|
||||
alignas(16) union { ALuint i[4]; float f[4]; } frac_;
|
||||
__m128i frac4, pos4;
|
||||
ALuint pos;
|
||||
ALuint i;
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_.i, pos_.i, 4);
|
||||
|
||||
frac4 = _mm_castps_si128(_mm_load_ps(frac_.f));
|
||||
pos4 = _mm_castps_si128(_mm_load_ps(pos_.f));
|
||||
|
||||
for(i = 0;numsamples-i > 3;i += 4)
|
||||
{
|
||||
const __m128 val1 = _mm_setr_ps(src[pos_.i[0]], src[pos_.i[1]], src[pos_.i[2]], src[pos_.i[3]]);
|
||||
const __m128 val2 = _mm_setr_ps(src[pos_.i[0]+1], src[pos_.i[1]+1], src[pos_.i[2]+1], src[pos_.i[3]+1]);
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
|
||||
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
|
||||
|
||||
_mm_store_ps(&dst[i], out);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
|
||||
pos_.i[0] = _mm_extract_epi32(pos4, 0);
|
||||
pos_.i[1] = _mm_extract_epi32(pos4, 1);
|
||||
pos_.i[2] = _mm_extract_epi32(pos4, 2);
|
||||
pos_.i[3] = _mm_extract_epi32(pos4, 3);
|
||||
}
|
||||
|
||||
pos = pos_.i[0];
|
||||
frac = _mm_cvtsi128_si32(frac4);
|
||||
|
||||
for(;i < numsamples;i++)
|
||||
{
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
@@ -1,488 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
/*
|
||||
* The OSS documentation talks about SOUND_MIXER_READ, but the header
|
||||
* only contains MIXER_READ. Play safe. Same for WRITE.
|
||||
*/
|
||||
#ifndef SOUND_MIXER_READ
|
||||
#define SOUND_MIXER_READ MIXER_READ
|
||||
#endif
|
||||
#ifndef SOUND_MIXER_WRITE
|
||||
#define SOUND_MIXER_WRITE MIXER_WRITE
|
||||
#endif
|
||||
|
||||
static const ALCchar oss_device[] = "OSS Software";
|
||||
static const ALCchar oss_device_capture[] = "OSS Capture";
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
|
||||
RingBuffer *ring;
|
||||
int doCapture;
|
||||
} oss_data;
|
||||
|
||||
|
||||
static int log2i(ALCuint x)
|
||||
{
|
||||
int y = 0;
|
||||
while (x > 1)
|
||||
{
|
||||
x >>= 1;
|
||||
y++;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
static ALuint OSSProc(ALvoid *ptr)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)ptr;
|
||||
oss_data *data = (oss_data*)pDevice->ExtraData;
|
||||
ALint frameSize;
|
||||
int wrote;
|
||||
|
||||
frameSize = aluChannelsFromFormat(pDevice->Format) *
|
||||
aluBytesFromFormat(pDevice->Format);
|
||||
|
||||
while(!data->killNow && !pDevice->Connected)
|
||||
{
|
||||
ALint len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
|
||||
aluMixData(pDevice, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
{
|
||||
wrote = write(data->fd, WritePtr, len);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
AL_PRINT("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(pDevice);
|
||||
len = 0;
|
||||
}
|
||||
else
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALuint OSSCaptureProc(ALvoid *ptr)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)ptr;
|
||||
oss_data *data = (oss_data*)pDevice->ExtraData;
|
||||
int frameSize;
|
||||
int amt;
|
||||
|
||||
frameSize = aluBytesFromFormat(pDevice->Format);
|
||||
frameSize *= aluChannelsFromFormat(pDevice->Format);
|
||||
|
||||
while(!data->killNow)
|
||||
{
|
||||
amt = read(data->fd, data->mix_data, data->data_size);
|
||||
if(amt < 0)
|
||||
{
|
||||
AL_PRINT("read failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(pDevice);
|
||||
break;
|
||||
}
|
||||
if(amt == 0)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
if(data->doCapture)
|
||||
WriteRingBuffer(data->ring, data->mix_data, amt/frameSize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
char driver[64];
|
||||
oss_data *data;
|
||||
|
||||
strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1);
|
||||
driver[sizeof(driver)-1] = 0;
|
||||
if(!deviceName)
|
||||
deviceName = oss_device;
|
||||
else if(strcmp(deviceName, oss_device) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (oss_data*)calloc(1, sizeof(oss_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(driver, O_WRONLY);
|
||||
if(data->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void oss_close_playback(ALCdevice *device)
|
||||
{
|
||||
oss_data *data = (oss_data*)device->ExtraData;
|
||||
|
||||
close(data->fd);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean oss_reset_playback(ALCdevice *device)
|
||||
{
|
||||
oss_data *data = (oss_data*)device->ExtraData;
|
||||
int numFragmentsLogSize;
|
||||
int log2FragmentSize;
|
||||
unsigned int periods;
|
||||
audio_buf_info info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
int ossFormat;
|
||||
int ossSpeed;
|
||||
char *err;
|
||||
int i;
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
ossFormat = AFMT_U8;
|
||||
break;
|
||||
case 4:
|
||||
switch(aluChannelsFromFormat(device->Format))
|
||||
{
|
||||
case 1: device->Format = AL_FORMAT_MONO16; break;
|
||||
case 2: device->Format = AL_FORMAT_STEREO16; break;
|
||||
case 4: device->Format = AL_FORMAT_QUAD16; break;
|
||||
case 6: device->Format = AL_FORMAT_51CHN16; break;
|
||||
case 7: device->Format = AL_FORMAT_61CHN16; break;
|
||||
case 8: device->Format = AL_FORMAT_71CHN16; break;
|
||||
}
|
||||
/* fall-through */
|
||||
case 2:
|
||||
ossFormat = AFMT_S16_NE;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
periods = device->NumUpdates;
|
||||
numChannels = aluChannelsFromFormat(device->Format);
|
||||
frameSize = numChannels * aluBytesFromFormat(device->Format);
|
||||
|
||||
ossSpeed = device->Frequency;
|
||||
log2FragmentSize = log2i(device->UpdateSize * frameSize);
|
||||
|
||||
/* according to the OSS spec, 16 bytes are the minimum */
|
||||
if (log2FragmentSize < 4)
|
||||
log2FragmentSize = 4;
|
||||
/* Subtract one period since the temp mixing buffer counts as one. Still
|
||||
* need at least two on the card, though. */
|
||||
if(periods > 2) periods--;
|
||||
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
|
||||
|
||||
#define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
|
||||
if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize), "set fragment") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info), "get space")))
|
||||
{
|
||||
AL_PRINT("%s failed: %s\n", err, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
#undef ok
|
||||
|
||||
if((int)aluChannelsFromFormat(device->Format) != numChannels)
|
||||
{
|
||||
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((ossFormat == AFMT_U8 && aluBytesFromFormat(device->Format) == 1) ||
|
||||
(ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2)))
|
||||
{
|
||||
AL_PRINT("Could not set %d-bit output, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = ossSpeed;
|
||||
device->UpdateSize = info.fragsize / frameSize;
|
||||
device->NumUpdates = info.fragments + 1;
|
||||
|
||||
data->data_size = device->UpdateSize * frameSize;
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->thread = StartThread(OSSProc, device);
|
||||
if(data->thread == NULL)
|
||||
{
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void oss_stop_playback(ALCdevice *device)
|
||||
{
|
||||
oss_data *data = (oss_data*)device->ExtraData;
|
||||
|
||||
if(!data->thread)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
StopThread(data->thread);
|
||||
data->thread = NULL;
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
int numFragmentsLogSize;
|
||||
int log2FragmentSize;
|
||||
unsigned int periods;
|
||||
audio_buf_info info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
char driver[64];
|
||||
oss_data *data;
|
||||
int ossFormat;
|
||||
int ossSpeed;
|
||||
char *err;
|
||||
int i;
|
||||
|
||||
strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1);
|
||||
driver[sizeof(driver)-1] = 0;
|
||||
if(!deviceName)
|
||||
deviceName = oss_device_capture;
|
||||
else if(strcmp(deviceName, oss_device_capture) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (oss_data*)calloc(1, sizeof(oss_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(driver, O_RDONLY);
|
||||
if(data->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
ossFormat = AFMT_U8;
|
||||
break;
|
||||
case 2:
|
||||
ossFormat = AFMT_S16_NE;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
periods = 4;
|
||||
numChannels = aluChannelsFromFormat(device->Format);
|
||||
frameSize = numChannels * aluBytesFromFormat(device->Format);
|
||||
ossSpeed = device->Frequency;
|
||||
log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
|
||||
frameSize / periods);
|
||||
|
||||
/* according to the OSS spec, 16 bytes are the minimum */
|
||||
if (log2FragmentSize < 4)
|
||||
log2FragmentSize = 4;
|
||||
numFragmentsLogSize = (periods << 16) | log2FragmentSize;
|
||||
|
||||
#define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
|
||||
if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize), "set fragment") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") &&
|
||||
ok(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info), "get space")))
|
||||
{
|
||||
AL_PRINT("%s failed: %s\n", err, strerror(errno));
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
#undef ok
|
||||
|
||||
if((int)aluChannelsFromFormat(device->Format) != numChannels)
|
||||
{
|
||||
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels);
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((ossFormat == AFMT_U8 && aluBytesFromFormat(device->Format) == 1) ||
|
||||
(ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2)))
|
||||
{
|
||||
AL_PRINT("Could not set %d-bit input, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat);
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
|
||||
if(!data->ring)
|
||||
{
|
||||
AL_PRINT("ring buffer create failed\n");
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->data_size = info.fragsize;
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
device->ExtraData = data;
|
||||
data->thread = StartThread(OSSCaptureProc, device);
|
||||
if(data->thread == NULL)
|
||||
{
|
||||
device->ExtraData = NULL;
|
||||
free(data->mix_data);
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void oss_close_capture(ALCdevice *device)
|
||||
{
|
||||
oss_data *data = (oss_data*)device->ExtraData;
|
||||
data->killNow = 1;
|
||||
StopThread(data->thread);
|
||||
|
||||
close(data->fd);
|
||||
|
||||
DestroyRingBuffer(data->ring);
|
||||
|
||||
free(data->mix_data);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void oss_start_capture(ALCdevice *pDevice)
|
||||
{
|
||||
oss_data *data = (oss_data*)pDevice->ExtraData;
|
||||
data->doCapture = 1;
|
||||
}
|
||||
|
||||
static void oss_stop_capture(ALCdevice *pDevice)
|
||||
{
|
||||
oss_data *data = (oss_data*)pDevice->ExtraData;
|
||||
data->doCapture = 0;
|
||||
}
|
||||
|
||||
static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
oss_data *data = (oss_data*)pDevice->ExtraData;
|
||||
if(lSamples <= (ALCuint)RingBufferSize(data->ring))
|
||||
ReadRingBuffer(data->ring, pBuffer, lSamples);
|
||||
else
|
||||
SetALCError(ALC_INVALID_VALUE);
|
||||
}
|
||||
|
||||
static ALCuint oss_available_samples(ALCdevice *pDevice)
|
||||
{
|
||||
oss_data *data = (oss_data*)pDevice->ExtraData;
|
||||
return RingBufferSize(data->ring);
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs oss_funcs = {
|
||||
oss_open_playback,
|
||||
oss_close_playback,
|
||||
oss_reset_playback,
|
||||
oss_stop_playback,
|
||||
oss_open_capture,
|
||||
oss_close_capture,
|
||||
oss_start_capture,
|
||||
oss_stop_capture,
|
||||
oss_capture_samples,
|
||||
oss_available_samples
|
||||
};
|
||||
|
||||
void alc_oss_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = oss_funcs;
|
||||
}
|
||||
|
||||
void alc_oss_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_oss_probe(int type)
|
||||
{
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(oss_device);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(oss_device);
|
||||
else if(type == CAPTURE_DEVICE_PROBE)
|
||||
AppendCaptureDeviceList(oss_device_capture);
|
||||
}
|
||||
+450
@@ -0,0 +1,450 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2010 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
|
||||
extern inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels]);
|
||||
|
||||
static void SetSpeakerArrangement(const char *name, ALfloat SpeakerAngle[MaxChannels],
|
||||
enum Channel Speaker2Chan[MaxChannels], ALint chans)
|
||||
{
|
||||
char *confkey, *next;
|
||||
char *layout_str;
|
||||
char *sep, *end;
|
||||
enum Channel val;
|
||||
const char *str;
|
||||
int i;
|
||||
|
||||
if(!ConfigValueStr(NULL, name, &str) && !ConfigValueStr(NULL, "layout", &str))
|
||||
return;
|
||||
|
||||
layout_str = strdup(str);
|
||||
next = confkey = layout_str;
|
||||
while(next && *next)
|
||||
{
|
||||
confkey = next;
|
||||
next = strchr(confkey, ',');
|
||||
if(next)
|
||||
{
|
||||
*next = 0;
|
||||
do {
|
||||
next++;
|
||||
} while(isspace(*next) || *next == ',');
|
||||
}
|
||||
|
||||
sep = strchr(confkey, '=');
|
||||
if(!sep || confkey == sep)
|
||||
{
|
||||
ERR("Malformed speaker key: %s\n", confkey);
|
||||
continue;
|
||||
}
|
||||
|
||||
end = sep - 1;
|
||||
while(isspace(*end) && end != confkey)
|
||||
end--;
|
||||
*(++end) = 0;
|
||||
|
||||
if(strcmp(confkey, "fl") == 0 || strcmp(confkey, "front-left") == 0)
|
||||
val = FrontLeft;
|
||||
else if(strcmp(confkey, "fr") == 0 || strcmp(confkey, "front-right") == 0)
|
||||
val = FrontRight;
|
||||
else if(strcmp(confkey, "fc") == 0 || strcmp(confkey, "front-center") == 0)
|
||||
val = FrontCenter;
|
||||
else if(strcmp(confkey, "bl") == 0 || strcmp(confkey, "back-left") == 0)
|
||||
val = BackLeft;
|
||||
else if(strcmp(confkey, "br") == 0 || strcmp(confkey, "back-right") == 0)
|
||||
val = BackRight;
|
||||
else if(strcmp(confkey, "bc") == 0 || strcmp(confkey, "back-center") == 0)
|
||||
val = BackCenter;
|
||||
else if(strcmp(confkey, "sl") == 0 || strcmp(confkey, "side-left") == 0)
|
||||
val = SideLeft;
|
||||
else if(strcmp(confkey, "sr") == 0 || strcmp(confkey, "side-right") == 0)
|
||||
val = SideRight;
|
||||
else
|
||||
{
|
||||
ERR("Unknown speaker for %s: \"%s\"\n", name, confkey);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(sep++) = 0;
|
||||
while(isspace(*sep))
|
||||
sep++;
|
||||
|
||||
for(i = 0;i < chans;i++)
|
||||
{
|
||||
if(Speaker2Chan[i] == val)
|
||||
{
|
||||
long angle = strtol(sep, NULL, 10);
|
||||
if(angle >= -180 && angle <= 180)
|
||||
SpeakerAngle[i] = DEG2RAD(angle);
|
||||
else
|
||||
ERR("Invalid angle for speaker \"%s\": %ld\n", confkey, angle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(layout_str);
|
||||
layout_str = NULL;
|
||||
|
||||
for(i = 0;i < chans;i++)
|
||||
{
|
||||
int min = i;
|
||||
int i2;
|
||||
|
||||
for(i2 = i+1;i2 < chans;i2++)
|
||||
{
|
||||
if(SpeakerAngle[i2] < SpeakerAngle[min])
|
||||
min = i2;
|
||||
}
|
||||
|
||||
if(min != i)
|
||||
{
|
||||
ALfloat tmpf;
|
||||
enum Channel tmpc;
|
||||
|
||||
tmpf = SpeakerAngle[i];
|
||||
SpeakerAngle[i] = SpeakerAngle[min];
|
||||
SpeakerAngle[min] = tmpf;
|
||||
|
||||
tmpc = Speaker2Chan[i];
|
||||
Speaker2Chan[i] = Speaker2Chan[min];
|
||||
Speaker2Chan[min] = tmpc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels])
|
||||
{
|
||||
ALfloat tmpgains[MaxChannels] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
enum Channel Speaker2Chan[MaxChannels];
|
||||
ALfloat SpeakerAngle[MaxChannels];
|
||||
ALfloat langle, rangle;
|
||||
ALfloat a;
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
Speaker2Chan[i] = device->Speaker2Chan[i];
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
SpeakerAngle[i] = device->SpeakerAngle[i];
|
||||
|
||||
/* Some easy special-cases first... */
|
||||
if(device->NumChan <= 1 || hwidth >= F_PI)
|
||||
{
|
||||
/* Full coverage for all speakers. */
|
||||
for(i = 0;i < MaxChannels;i++)
|
||||
gains[i] = 0.0f;
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
{
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
gains[chan] = ingain;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(hwidth <= 0.0f)
|
||||
{
|
||||
/* Infinitely small sound point. */
|
||||
for(i = 0;i < MaxChannels;i++)
|
||||
gains[i] = 0.0f;
|
||||
for(i = 0;i < device->NumChan-1;i++)
|
||||
{
|
||||
if(angle >= SpeakerAngle[i] && angle < SpeakerAngle[i+1])
|
||||
{
|
||||
/* Sound is between speakers i and i+1 */
|
||||
a = (angle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
|
||||
gains[Speaker2Chan[i+1]] = sqrtf( a) * ingain;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Sound is between last and first speakers */
|
||||
if(angle < SpeakerAngle[0])
|
||||
angle += F_2PI;
|
||||
a = (angle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
gains[Speaker2Chan[i]] = sqrtf(1.0f-a) * ingain;
|
||||
gains[Speaker2Chan[0]] = sqrtf( a) * ingain;
|
||||
return;
|
||||
}
|
||||
|
||||
if(fabsf(angle)+hwidth > F_PI)
|
||||
{
|
||||
/* The coverage area would go outside of -pi...+pi. Instead, rotate the
|
||||
* speaker angles so it would be as if angle=0, and keep them wrapped
|
||||
* within -pi...+pi. */
|
||||
if(angle > 0.0f)
|
||||
{
|
||||
ALuint done;
|
||||
ALuint i = 0;
|
||||
while(i < device->NumChan && device->SpeakerAngle[i]-angle < -F_PI)
|
||||
i++;
|
||||
for(done = 0;i < device->NumChan;done++)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
i++;
|
||||
}
|
||||
for(i = 0;done < device->NumChan;i++)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle + F_2PI;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
done++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* NOTE: '< device->NumChan' on the iterators is correct here since
|
||||
* we need to handle index 0. Because the iterators are unsigned,
|
||||
* they'll underflow and wrap to become 0xFFFFFFFF, which will
|
||||
* break as expected. */
|
||||
ALuint done;
|
||||
ALuint i = device->NumChan-1;
|
||||
while(i < device->NumChan && device->SpeakerAngle[i]-angle > F_PI)
|
||||
i--;
|
||||
for(done = device->NumChan-1;i < device->NumChan;done--)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
i--;
|
||||
}
|
||||
for(i = device->NumChan-1;done < device->NumChan;i--)
|
||||
{
|
||||
SpeakerAngle[done] = device->SpeakerAngle[i]-angle - F_2PI;
|
||||
Speaker2Chan[done] = device->Speaker2Chan[i];
|
||||
done--;
|
||||
}
|
||||
}
|
||||
angle = 0.0f;
|
||||
}
|
||||
langle = angle - hwidth;
|
||||
rangle = angle + hwidth;
|
||||
|
||||
/* First speaker */
|
||||
i = 0;
|
||||
do {
|
||||
ALuint last = device->NumChan-1;
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[chan] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle)
|
||||
{
|
||||
a = (F_2PI + rangle-SpeakerAngle[last]) /
|
||||
(F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
else if(SpeakerAngle[last] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[last]) /
|
||||
(F_2PI + SpeakerAngle[i]-SpeakerAngle[last]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
for(i = 1;i < device->NumChan-1;i++)
|
||||
{
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[chan] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(SpeakerAngle[i] < langle && SpeakerAngle[i+1] > langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(SpeakerAngle[i+1]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[i-1]) /
|
||||
(SpeakerAngle[i]-SpeakerAngle[i-1]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
}
|
||||
|
||||
/* Last speaker */
|
||||
i = device->NumChan-1;
|
||||
do {
|
||||
enum Channel chan = Speaker2Chan[i];
|
||||
if(SpeakerAngle[i] >= langle && SpeakerAngle[i] <= rangle)
|
||||
{
|
||||
tmpgains[Speaker2Chan[i]] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
if(SpeakerAngle[i] > rangle && SpeakerAngle[i-1] < rangle)
|
||||
{
|
||||
a = (rangle-SpeakerAngle[i-1]) /
|
||||
(SpeakerAngle[i]-SpeakerAngle[i-1]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, a);
|
||||
}
|
||||
if(SpeakerAngle[i] < langle)
|
||||
{
|
||||
a = (langle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
else if(SpeakerAngle[0] > langle)
|
||||
{
|
||||
a = (F_2PI + langle-SpeakerAngle[i]) /
|
||||
(F_2PI + SpeakerAngle[0]-SpeakerAngle[i]);
|
||||
tmpgains[chan] = lerp(tmpgains[chan], 1.0f, 1.0f-a);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
for(i = 0;i < device->NumChan;i++)
|
||||
{
|
||||
enum Channel chan = device->Speaker2Chan[i];
|
||||
gains[chan] = sqrtf(tmpgains[chan]) * ingain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALvoid aluInitPanning(ALCdevice *Device)
|
||||
{
|
||||
const char *layoutname = NULL;
|
||||
enum Channel *Speaker2Chan;
|
||||
ALfloat *SpeakerAngle;
|
||||
|
||||
Speaker2Chan = Device->Speaker2Chan;
|
||||
SpeakerAngle = Device->SpeakerAngle;
|
||||
switch(Device->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
Device->NumChan = 1;
|
||||
Speaker2Chan[0] = FrontCenter;
|
||||
SpeakerAngle[0] = DEG2RAD(0.0f);
|
||||
layoutname = NULL;
|
||||
break;
|
||||
|
||||
case DevFmtStereo:
|
||||
Device->NumChan = 2;
|
||||
Speaker2Chan[0] = FrontLeft;
|
||||
Speaker2Chan[1] = FrontRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( 90.0f);
|
||||
layoutname = "layout_stereo";
|
||||
break;
|
||||
|
||||
case DevFmtQuad:
|
||||
Device->NumChan = 4;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontRight;
|
||||
Speaker2Chan[3] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-135.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -45.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 45.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 135.0f);
|
||||
layoutname = "layout_quad";
|
||||
break;
|
||||
|
||||
case DevFmtX51:
|
||||
Device->NumChan = 5;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-110.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 110.0f);
|
||||
layoutname = "layout_surround51";
|
||||
break;
|
||||
|
||||
case DevFmtX51Side:
|
||||
Device->NumChan = 5;
|
||||
Speaker2Chan[0] = SideLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = SideRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD(-30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 90.0f);
|
||||
layoutname = "layout_side51";
|
||||
break;
|
||||
|
||||
case DevFmtX61:
|
||||
Device->NumChan = 6;
|
||||
Speaker2Chan[0] = SideLeft;
|
||||
Speaker2Chan[1] = FrontLeft;
|
||||
Speaker2Chan[2] = FrontCenter;
|
||||
Speaker2Chan[3] = FrontRight;
|
||||
Speaker2Chan[4] = SideRight;
|
||||
Speaker2Chan[5] = BackCenter;
|
||||
SpeakerAngle[0] = DEG2RAD(-90.0f);
|
||||
SpeakerAngle[1] = DEG2RAD(-30.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 90.0f);
|
||||
SpeakerAngle[5] = DEG2RAD(180.0f);
|
||||
layoutname = "layout_surround61";
|
||||
break;
|
||||
|
||||
case DevFmtX71:
|
||||
Device->NumChan = 7;
|
||||
Speaker2Chan[0] = BackLeft;
|
||||
Speaker2Chan[1] = SideLeft;
|
||||
Speaker2Chan[2] = FrontLeft;
|
||||
Speaker2Chan[3] = FrontCenter;
|
||||
Speaker2Chan[4] = FrontRight;
|
||||
Speaker2Chan[5] = SideRight;
|
||||
Speaker2Chan[6] = BackRight;
|
||||
SpeakerAngle[0] = DEG2RAD(-150.0f);
|
||||
SpeakerAngle[1] = DEG2RAD( -90.0f);
|
||||
SpeakerAngle[2] = DEG2RAD( -30.0f);
|
||||
SpeakerAngle[3] = DEG2RAD( 0.0f);
|
||||
SpeakerAngle[4] = DEG2RAD( 30.0f);
|
||||
SpeakerAngle[5] = DEG2RAD( 90.0f);
|
||||
SpeakerAngle[6] = DEG2RAD( 150.0f);
|
||||
layoutname = "layout_surround71";
|
||||
break;
|
||||
}
|
||||
if(layoutname && Device->Type != Loopback)
|
||||
SetSpeakerArrangement(layoutname, SpeakerAngle, Speaker2Chan, Device->NumChan);
|
||||
}
|
||||
-271
@@ -1,271 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#ifdef HAVE_DLFCN_H
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static typeof(x) * p##x
|
||||
MAKE_FUNC(Pa_Initialize);
|
||||
MAKE_FUNC(Pa_Terminate);
|
||||
MAKE_FUNC(Pa_GetErrorText);
|
||||
MAKE_FUNC(Pa_StartStream);
|
||||
MAKE_FUNC(Pa_StopStream);
|
||||
MAKE_FUNC(Pa_OpenStream);
|
||||
MAKE_FUNC(Pa_CloseStream);
|
||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||
MAKE_FUNC(Pa_GetStreamInfo);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
|
||||
static const ALCchar pa_device[] = "PortAudio Software";
|
||||
|
||||
typedef struct {
|
||||
PaStream *stream;
|
||||
} pa_data;
|
||||
|
||||
|
||||
static int pa_callback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
|
||||
(void)inputBuffer;
|
||||
(void)timeInfo;
|
||||
(void)statusFlags;
|
||||
|
||||
aluMixData(device, outputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
const PaStreamInfo *streamInfo;
|
||||
PaStreamParameters outParams;
|
||||
pa_data *data;
|
||||
PaError err;
|
||||
|
||||
if(pa_handle == NULL)
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
device->ExtraData = data;
|
||||
|
||||
outParams.device = GetConfigValueInt("port", "device", -1);
|
||||
if(outParams.device < 0)
|
||||
outParams.device = pPa_GetDefaultOutputDevice();
|
||||
outParams.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
|
||||
(float)device->Frequency;
|
||||
outParams.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
outParams.sampleFormat = paUInt8;
|
||||
break;
|
||||
case 2:
|
||||
outParams.sampleFormat = paInt16;
|
||||
break;
|
||||
case 4:
|
||||
outParams.sampleFormat = paFloat32;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
device->ExtraData = NULL;
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
outParams.channelCount = aluChannelsFromFormat(device->Format);
|
||||
|
||||
err = pPa_OpenStream(&data->stream, NULL, &outParams, device->Frequency,
|
||||
device->UpdateSize, paNoFlag, pa_callback, device);
|
||||
if(err != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_OpenStream() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
device->ExtraData = NULL;
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
streamInfo = pPa_GetStreamInfo(data->stream);
|
||||
|
||||
err = pPa_StartStream(data->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_StartStream() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
pPa_CloseStream(data->stream);
|
||||
device->ExtraData = NULL;
|
||||
free(data);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_close_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = pPa_StopStream(data->stream);
|
||||
if(err != paNoError)
|
||||
fprintf(stderr, "Error stopping stream: %s\n", pPa_GetErrorText(err));
|
||||
|
||||
err = pPa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
fprintf(stderr, "Error closing stream: %s\n", pPa_GetErrorText(err));
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean pa_reset_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
const PaStreamInfo *streamInfo;
|
||||
|
||||
streamInfo = pPa_GetStreamInfo(data->stream);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_stop_playback(ALCdevice *device)
|
||||
{
|
||||
(void)device;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
return ALC_FALSE;
|
||||
(void)device;
|
||||
(void)deviceName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const BackendFuncs pa_funcs = {
|
||||
pa_open_playback,
|
||||
pa_close_playback,
|
||||
pa_reset_playback,
|
||||
pa_stop_playback,
|
||||
pa_open_capture,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
void alc_pa_init(BackendFuncs *func_list)
|
||||
{
|
||||
const char *str;
|
||||
PaError err;
|
||||
|
||||
if(func_list) *func_list = pa_funcs;
|
||||
|
||||
#ifdef HAVE_DLFCN_H
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
# define PALIB "libportaudio.2.dylib"
|
||||
#else
|
||||
# define PALIB "libportaudio.so.2"
|
||||
#endif
|
||||
pa_handle = dlopen(PALIB, RTLD_NOW);
|
||||
if(!pa_handle)
|
||||
return;
|
||||
dlerror();
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = (typeof(f)*)dlsym(pa_handle, #f); \
|
||||
if((str=dlerror()) != NULL) \
|
||||
{ \
|
||||
dlclose(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
AL_PRINT("Could not load %s from "PALIB": %s\n", #f, str); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
str = NULL;
|
||||
pa_handle = (void*)0xDEADBEEF;
|
||||
#define LOAD_FUNC(f) p##f = f
|
||||
#endif
|
||||
|
||||
LOAD_FUNC(Pa_Initialize);
|
||||
LOAD_FUNC(Pa_Terminate);
|
||||
LOAD_FUNC(Pa_GetErrorText);
|
||||
LOAD_FUNC(Pa_StartStream);
|
||||
LOAD_FUNC(Pa_StopStream);
|
||||
LOAD_FUNC(Pa_OpenStream);
|
||||
LOAD_FUNC(Pa_CloseStream);
|
||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||
LOAD_FUNC(Pa_GetStreamInfo);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if((err=pPa_Initialize()) != paNoError)
|
||||
{
|
||||
AL_PRINT("Pa_Initialize() returned an error: %s\n", pPa_GetErrorText(err));
|
||||
alc_pa_deinit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void alc_pa_deinit(void)
|
||||
{
|
||||
if(pa_handle)
|
||||
{
|
||||
pPa_Terminate();
|
||||
#ifdef HAVE_DLFCN_H
|
||||
dlclose(pa_handle);
|
||||
pa_handle = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void alc_pa_probe(int type)
|
||||
{
|
||||
if(!pa_handle) alc_pa_init(NULL);
|
||||
if(!pa_handle) return;
|
||||
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(pa_device);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(pa_device);
|
||||
}
|
||||
@@ -1,671 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 by Konstantinos Natsakis <konstantinos.natsakis@gmail.com>
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#ifdef HAVE_DLFCN_H
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#if PA_API_VERSION == 11
|
||||
#define PA_STREAM_ADJUST_LATENCY 0x2000U
|
||||
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
|
||||
{
|
||||
return (x == PA_STREAM_CREATING || x == PA_STREAM_READY);
|
||||
}
|
||||
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
|
||||
{
|
||||
return (x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING ||
|
||||
x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY);
|
||||
}
|
||||
#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD
|
||||
#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD
|
||||
#elif PA_API_VERSION != 12
|
||||
#error Invalid PulseAudio API version
|
||||
#endif
|
||||
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static typeof(x) * p##x
|
||||
MAKE_FUNC(pa_context_unref);
|
||||
MAKE_FUNC(pa_sample_spec_valid);
|
||||
MAKE_FUNC(pa_stream_drop);
|
||||
MAKE_FUNC(pa_strerror);
|
||||
MAKE_FUNC(pa_context_get_state);
|
||||
MAKE_FUNC(pa_stream_get_state);
|
||||
MAKE_FUNC(pa_threaded_mainloop_signal);
|
||||
MAKE_FUNC(pa_stream_peek);
|
||||
MAKE_FUNC(pa_threaded_mainloop_wait);
|
||||
MAKE_FUNC(pa_threaded_mainloop_unlock);
|
||||
MAKE_FUNC(pa_context_new);
|
||||
MAKE_FUNC(pa_threaded_mainloop_stop);
|
||||
MAKE_FUNC(pa_context_disconnect);
|
||||
MAKE_FUNC(pa_threaded_mainloop_start);
|
||||
MAKE_FUNC(pa_threaded_mainloop_get_api);
|
||||
MAKE_FUNC(pa_context_set_state_callback);
|
||||
MAKE_FUNC(pa_stream_write);
|
||||
MAKE_FUNC(pa_xfree);
|
||||
MAKE_FUNC(pa_stream_connect_record);
|
||||
MAKE_FUNC(pa_stream_connect_playback);
|
||||
MAKE_FUNC(pa_path_get_filename);
|
||||
MAKE_FUNC(pa_get_binary_name);
|
||||
MAKE_FUNC(pa_threaded_mainloop_free);
|
||||
MAKE_FUNC(pa_context_errno);
|
||||
MAKE_FUNC(pa_xmalloc0);
|
||||
MAKE_FUNC(pa_stream_unref);
|
||||
MAKE_FUNC(pa_threaded_mainloop_accept);
|
||||
MAKE_FUNC(pa_stream_set_write_callback);
|
||||
MAKE_FUNC(pa_threaded_mainloop_new);
|
||||
MAKE_FUNC(pa_context_connect);
|
||||
MAKE_FUNC(pa_stream_get_buffer_attr);
|
||||
MAKE_FUNC(pa_stream_set_read_callback);
|
||||
MAKE_FUNC(pa_stream_set_state_callback);
|
||||
MAKE_FUNC(pa_stream_new);
|
||||
MAKE_FUNC(pa_stream_disconnect);
|
||||
MAKE_FUNC(pa_threaded_mainloop_lock);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ALCuint samples;
|
||||
ALCuint frame_size;
|
||||
|
||||
RingBuffer *ring;
|
||||
|
||||
pa_buffer_attr attr;
|
||||
pa_sample_spec spec;
|
||||
|
||||
char path_name[PATH_MAX];
|
||||
const char *context_name;
|
||||
const char *stream_name;
|
||||
|
||||
pa_threaded_mainloop *loop;
|
||||
|
||||
pa_stream *stream;
|
||||
pa_context *context;
|
||||
} pulse_data;
|
||||
|
||||
static const ALCchar pulse_device[] = "PulseAudio Software";
|
||||
static const ALCchar pulse_capture_device[] = "PulseAudio Capture";
|
||||
|
||||
|
||||
// PulseAudio I/O Callbacks //{{{
|
||||
static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{
|
||||
{
|
||||
ALCdevice *Device = pdata;
|
||||
pulse_data *data = Device->ExtraData;
|
||||
void *buf = ppa_xmalloc0(data->attr.minreq);
|
||||
(void)len;
|
||||
|
||||
aluMixData(Device, buf, data->attr.minreq/data->frame_size);
|
||||
ppa_stream_write(stream, buf, data->attr.minreq, ppa_xfree, 0,
|
||||
PA_SEEK_RELATIVE);
|
||||
} //}}}
|
||||
|
||||
static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) //{{{
|
||||
{
|
||||
ALCdevice *Device = pdata;
|
||||
pulse_data *data = Device->ExtraData;
|
||||
const void *buf;
|
||||
|
||||
if(ppa_stream_peek(stream, &buf, &length) < 0)
|
||||
{
|
||||
AL_PRINT("pa_stream_peek() failed: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
return;
|
||||
}
|
||||
|
||||
assert(buf);
|
||||
assert(length);
|
||||
|
||||
length /= data->frame_size;
|
||||
|
||||
if(data->samples < length)
|
||||
AL_PRINT("stream_read_callback: buffer overflow!\n");
|
||||
|
||||
WriteRingBuffer(data->ring, buf, (length<data->samples) ? length : data->samples);
|
||||
|
||||
ppa_stream_drop(stream);
|
||||
} //}}}
|
||||
//}}}
|
||||
|
||||
static ALCboolean pulse_open(ALCdevice *device, const ALCchar *device_name) //{{{
|
||||
{
|
||||
pulse_data *data = ppa_xmalloc0(sizeof(pulse_data));
|
||||
pa_context_state_t state;
|
||||
|
||||
if(ppa_get_binary_name(data->path_name, sizeof(data->path_name)))
|
||||
data->context_name = ppa_path_get_filename(data->path_name);
|
||||
else
|
||||
data->context_name = "OpenAL Soft";
|
||||
|
||||
if(!(data->loop = ppa_threaded_mainloop_new()))
|
||||
{
|
||||
AL_PRINT("pa_threaded_mainloop_new() failed!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ppa_threaded_mainloop_start(data->loop) < 0)
|
||||
{
|
||||
AL_PRINT("pa_threaded_mainloop_start() failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
|
||||
data->context = ppa_context_new(ppa_threaded_mainloop_get_api(data->loop), data->context_name);
|
||||
if(!data->context)
|
||||
{
|
||||
AL_PRINT("pa_context_new() failed: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ppa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
|
||||
{
|
||||
AL_PRINT("Context did not connect: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_context_unref(data->context);
|
||||
data->context = NULL;
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while((state=ppa_context_get_state(data->context)) != PA_CONTEXT_READY)
|
||||
{
|
||||
if(!PA_CONTEXT_IS_GOOD(state))
|
||||
{
|
||||
AL_PRINT("Context did not get ready: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_context_unref(data->context);
|
||||
data->context = NULL;
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
Sleep(1);
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(device_name);
|
||||
device->ExtraData = data;
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
return ALC_TRUE;
|
||||
|
||||
out:
|
||||
if(data->loop)
|
||||
{
|
||||
ppa_threaded_mainloop_stop(data->loop);
|
||||
ppa_threaded_mainloop_free(data->loop);
|
||||
}
|
||||
|
||||
ppa_xfree(data);
|
||||
return ALC_FALSE;
|
||||
} //}}}
|
||||
|
||||
static void pulse_close(ALCdevice *device) //{{{
|
||||
{
|
||||
pulse_data *data = device->ExtraData;
|
||||
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
|
||||
if(data->stream)
|
||||
{
|
||||
ppa_stream_disconnect(data->stream);
|
||||
ppa_stream_unref(data->stream);
|
||||
}
|
||||
|
||||
ppa_context_disconnect(data->context);
|
||||
ppa_context_unref(data->context);
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
|
||||
ppa_threaded_mainloop_stop(data->loop);
|
||||
ppa_threaded_mainloop_free(data->loop);
|
||||
|
||||
device->ExtraData = NULL;
|
||||
free(device->szDeviceName);
|
||||
device->szDeviceName = NULL;
|
||||
DestroyRingBuffer(data->ring);
|
||||
|
||||
ppa_xfree(data);
|
||||
} //}}}
|
||||
//}}}
|
||||
|
||||
// OpenAL {{{
|
||||
static ALCboolean pulse_open_playback(ALCdevice *device, const ALCchar *device_name) //{{{
|
||||
{
|
||||
if(!pa_handle)
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!device_name)
|
||||
device_name = pulse_device;
|
||||
else if(strcmp(device_name, pulse_device) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
return pulse_open(device, device_name);
|
||||
} //}}}
|
||||
|
||||
static void pulse_close_playback(ALCdevice *device) //{{{
|
||||
{
|
||||
pulse_close(device);
|
||||
} //}}}
|
||||
|
||||
static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
|
||||
{
|
||||
pulse_data *data = device->ExtraData;
|
||||
pa_stream_state_t state;
|
||||
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
|
||||
data->frame_size = aluBytesFromFormat(device->Format) *
|
||||
aluChannelsFromFormat(device->Format);
|
||||
data->attr.minreq = data->frame_size * device->UpdateSize;
|
||||
data->attr.prebuf = -1;
|
||||
data->attr.maxlength = -1;
|
||||
data->attr.fragsize = -1;
|
||||
data->attr.tlength = data->attr.minreq * device->NumUpdates;
|
||||
data->stream_name = "Playback Stream";
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
data->spec.format = PA_SAMPLE_U8;
|
||||
break;
|
||||
case 2:
|
||||
data->spec.format = PA_SAMPLE_S16NE;
|
||||
break;
|
||||
case 4:
|
||||
data->spec.format = PA_SAMPLE_FLOAT32NE;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
data->spec.rate = device->Frequency;
|
||||
data->spec.channels = aluChannelsFromFormat(device->Format);
|
||||
|
||||
if(ppa_sample_spec_valid(&data->spec) == 0)
|
||||
{
|
||||
AL_PRINT("Invalid sample format\n");
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, NULL);
|
||||
if(!data->stream)
|
||||
{
|
||||
AL_PRINT("pa_stream_new() failed: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(ppa_stream_connect_playback(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
|
||||
{
|
||||
AL_PRINT("Stream did not connect: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_stream_unref(data->stream);
|
||||
data->stream = NULL;
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
|
||||
{
|
||||
if(!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
AL_PRINT("Stream did not get ready: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_stream_unref(data->stream);
|
||||
data->stream = NULL;
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
Sleep(1);
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
}
|
||||
|
||||
data->attr = *(ppa_stream_get_buffer_attr(data->stream));
|
||||
if((data->attr.tlength%data->attr.minreq) != 0)
|
||||
AL_PRINT("tlength (%d) is not a multiple of minreq (%d)!\n",
|
||||
data->attr.tlength, data->attr.minreq);
|
||||
device->UpdateSize = data->attr.minreq;
|
||||
device->NumUpdates = data->attr.tlength/data->attr.minreq;
|
||||
|
||||
ppa_stream_set_write_callback(data->stream, stream_write_callback, device);
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
return ALC_TRUE;
|
||||
} //}}}
|
||||
|
||||
static void pulse_stop_playback(ALCdevice *device) //{{{
|
||||
{
|
||||
pulse_data *data = device->ExtraData;
|
||||
|
||||
if(!data->stream)
|
||||
return;
|
||||
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
|
||||
ppa_stream_disconnect(data->stream);
|
||||
ppa_stream_unref(data->stream);
|
||||
data->stream = NULL;
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
} //}}}
|
||||
|
||||
|
||||
static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name) //{{{
|
||||
{
|
||||
pulse_data *data;
|
||||
pa_stream_state_t state;
|
||||
|
||||
if(!pa_handle)
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!device_name)
|
||||
device_name = pulse_capture_device;
|
||||
else if(strcmp(device_name, pulse_capture_device) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
if(pulse_open(device, device_name) == ALC_FALSE)
|
||||
return ALC_FALSE;
|
||||
|
||||
data = device->ExtraData;
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
|
||||
data->samples = device->UpdateSize * device->NumUpdates;
|
||||
data->frame_size = aluBytesFromFormat(device->Format) *
|
||||
aluChannelsFromFormat(device->Format);
|
||||
|
||||
if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples)))
|
||||
{
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
pulse_close(device);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->attr.minreq = -1;
|
||||
data->attr.prebuf = -1;
|
||||
data->attr.maxlength = -1;
|
||||
data->attr.tlength = -1;
|
||||
data->attr.fragsize = data->frame_size * data->samples / 2;
|
||||
data->stream_name = "Capture Stream";
|
||||
|
||||
data->spec.rate = device->Frequency;
|
||||
data->spec.channels = aluChannelsFromFormat(device->Format);
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
data->spec.format = PA_SAMPLE_U8;
|
||||
break;
|
||||
case 2:
|
||||
data->spec.format = PA_SAMPLE_S16NE;
|
||||
break;
|
||||
case 4:
|
||||
data->spec.format = PA_SAMPLE_FLOAT32NE;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
pulse_close(device);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(ppa_sample_spec_valid(&data->spec) == 0)
|
||||
{
|
||||
AL_PRINT("Invalid sample format\n");
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
pulse_close(device);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, NULL);
|
||||
if(!data->stream)
|
||||
{
|
||||
AL_PRINT("pa_stream_new() failed: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
pulse_close(device);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(ppa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY) < 0)
|
||||
{
|
||||
AL_PRINT("Stream did not connect: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_stream_unref(data->stream);
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
|
||||
data->stream = NULL;
|
||||
pulse_close(device);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
while((state=ppa_stream_get_state(data->stream)) != PA_STREAM_READY)
|
||||
{
|
||||
if(!PA_STREAM_IS_GOOD(state))
|
||||
{
|
||||
AL_PRINT("Stream did not get ready: %s\n",
|
||||
ppa_strerror(ppa_context_errno(data->context)));
|
||||
|
||||
ppa_stream_unref(data->stream);
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
|
||||
data->stream = NULL;
|
||||
pulse_close(device);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
Sleep(1);
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
}
|
||||
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
return ALC_TRUE;
|
||||
} //}}}
|
||||
|
||||
static void pulse_close_capture(ALCdevice *device) //{{{
|
||||
{
|
||||
pulse_close(device);
|
||||
} //}}}
|
||||
|
||||
static void pulse_start_capture(ALCdevice *device) //{{{
|
||||
{
|
||||
pulse_data *data = device->ExtraData;
|
||||
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
ppa_stream_set_read_callback(data->stream, stream_read_callback, device);
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
} //}}}
|
||||
|
||||
static void pulse_stop_capture(ALCdevice *device) //{{{
|
||||
{
|
||||
pulse_data *data = device->ExtraData;
|
||||
|
||||
ppa_threaded_mainloop_lock(data->loop);
|
||||
ppa_stream_set_read_callback(data->stream, NULL, NULL);
|
||||
ppa_threaded_mainloop_unlock(data->loop);
|
||||
} //}}}
|
||||
|
||||
static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{
|
||||
{
|
||||
pulse_data *data = device->ExtraData;
|
||||
ALCuint available = RingBufferSize(data->ring);
|
||||
|
||||
if(available < samples)
|
||||
SetALCError(ALC_INVALID_VALUE);
|
||||
else
|
||||
ReadRingBuffer(data->ring, buffer, samples);
|
||||
} //}}}
|
||||
|
||||
static ALCuint pulse_available_samples(ALCdevice *device) //{{{
|
||||
{
|
||||
pulse_data *data = device->ExtraData;
|
||||
return RingBufferSize(data->ring);
|
||||
} //}}}
|
||||
|
||||
BackendFuncs pulse_funcs = { //{{{
|
||||
pulse_open_playback,
|
||||
pulse_close_playback,
|
||||
pulse_reset_playback,
|
||||
pulse_stop_playback,
|
||||
pulse_open_capture,
|
||||
pulse_close_capture,
|
||||
pulse_start_capture,
|
||||
pulse_stop_capture,
|
||||
pulse_capture_samples,
|
||||
pulse_available_samples
|
||||
}; //}}}
|
||||
|
||||
void alc_pulse_init(BackendFuncs *func_list) //{{{
|
||||
{
|
||||
if(func_list) *func_list = pulse_funcs;
|
||||
|
||||
#ifdef _WIN32
|
||||
pa_handle = LoadLibrary("libpulse-0.dll");
|
||||
#define LOAD_FUNC(x) do { \
|
||||
p##x = GetProcAddress(pa_handle, #x); \
|
||||
if(!(p##x)) { \
|
||||
AL_PRINT("Could not load %s from libpulse-0.dll\n", #x); \
|
||||
FreeLibrary(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#elif defined (HAVE_DLFCN_H)
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
pa_handle = dlopen("libpulse.0.dylib", RTLD_NOW);
|
||||
#else
|
||||
pa_handle = dlopen("libpulse.so.0", RTLD_NOW);
|
||||
#endif
|
||||
#define LOAD_FUNC(x) do { \
|
||||
p##x = dlsym(pa_handle, #x); \
|
||||
if(!(p##x)) { \
|
||||
AL_PRINT("Could not load %s from libpulse\n", #x); \
|
||||
dlclose(pa_handle); \
|
||||
pa_handle = NULL; \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#else
|
||||
|
||||
pa_handle = (void*)0xDEADBEEF;
|
||||
#define LOAD_FUNC(x) p##x = (x)
|
||||
|
||||
#endif
|
||||
if(!pa_handle)
|
||||
return;
|
||||
|
||||
LOAD_FUNC(pa_context_unref);
|
||||
LOAD_FUNC(pa_sample_spec_valid);
|
||||
LOAD_FUNC(pa_stream_drop);
|
||||
LOAD_FUNC(pa_strerror);
|
||||
LOAD_FUNC(pa_context_get_state);
|
||||
LOAD_FUNC(pa_stream_get_state);
|
||||
LOAD_FUNC(pa_threaded_mainloop_signal);
|
||||
LOAD_FUNC(pa_stream_peek);
|
||||
LOAD_FUNC(pa_threaded_mainloop_wait);
|
||||
LOAD_FUNC(pa_threaded_mainloop_unlock);
|
||||
LOAD_FUNC(pa_context_new);
|
||||
LOAD_FUNC(pa_threaded_mainloop_stop);
|
||||
LOAD_FUNC(pa_context_disconnect);
|
||||
LOAD_FUNC(pa_threaded_mainloop_start);
|
||||
LOAD_FUNC(pa_threaded_mainloop_get_api);
|
||||
LOAD_FUNC(pa_context_set_state_callback);
|
||||
LOAD_FUNC(pa_stream_write);
|
||||
LOAD_FUNC(pa_xfree);
|
||||
LOAD_FUNC(pa_stream_connect_record);
|
||||
LOAD_FUNC(pa_stream_connect_playback);
|
||||
LOAD_FUNC(pa_path_get_filename);
|
||||
LOAD_FUNC(pa_get_binary_name);
|
||||
LOAD_FUNC(pa_threaded_mainloop_free);
|
||||
LOAD_FUNC(pa_context_errno);
|
||||
LOAD_FUNC(pa_xmalloc0);
|
||||
LOAD_FUNC(pa_stream_unref);
|
||||
LOAD_FUNC(pa_threaded_mainloop_accept);
|
||||
LOAD_FUNC(pa_stream_set_write_callback);
|
||||
LOAD_FUNC(pa_threaded_mainloop_new);
|
||||
LOAD_FUNC(pa_context_connect);
|
||||
LOAD_FUNC(pa_stream_get_buffer_attr);
|
||||
LOAD_FUNC(pa_stream_set_read_callback);
|
||||
LOAD_FUNC(pa_stream_set_state_callback);
|
||||
LOAD_FUNC(pa_stream_new);
|
||||
LOAD_FUNC(pa_stream_disconnect);
|
||||
LOAD_FUNC(pa_threaded_mainloop_lock);
|
||||
|
||||
#undef LOAD_FUNC
|
||||
} //}}}
|
||||
|
||||
void alc_pulse_deinit(void) //{{{
|
||||
{
|
||||
if(pa_handle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(pa_handle);
|
||||
#elif defined (HAVE_DLFCN_H)
|
||||
dlclose(pa_handle);
|
||||
#endif
|
||||
}
|
||||
pa_handle = NULL;
|
||||
} //}}}
|
||||
|
||||
void alc_pulse_probe(int type) //{{{
|
||||
{
|
||||
if(!pa_handle) alc_pulse_init(NULL);
|
||||
if(!pa_handle) return;
|
||||
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(pulse_device);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(pulse_device);
|
||||
else if(type == CAPTURE_DEVICE_PROBE)
|
||||
AppendCaptureDeviceList(pulse_capture_device);
|
||||
} //}}}
|
||||
//}}}
|
||||
-292
@@ -1,292 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include <sys/audioio.h>
|
||||
|
||||
|
||||
static const ALCchar solaris_device[] = "Solaris Software";
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
} solaris_data;
|
||||
|
||||
|
||||
static ALuint SolarisProc(ALvoid *ptr)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)ptr;
|
||||
solaris_data *data = (solaris_data*)pDevice->ExtraData;
|
||||
int remaining = 0;
|
||||
ALint frameSize;
|
||||
int wrote;
|
||||
|
||||
frameSize = aluChannelsFromFormat(pDevice->Format) *
|
||||
aluBytesFromFormat(pDevice->Format);
|
||||
|
||||
while(!data->killNow && !pDevice->Connected)
|
||||
{
|
||||
ALint len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
|
||||
aluMixData(pDevice, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
{
|
||||
wrote = write(data->fd, WritePtr, len);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
AL_PRINT("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(pDevice);
|
||||
len = 0;
|
||||
}
|
||||
else
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
char driver[64];
|
||||
solaris_data *data;
|
||||
|
||||
strncpy(driver, GetConfigValue("solaris", "device", "/dev/audio"), sizeof(driver)-1);
|
||||
driver[sizeof(driver)-1] = 0;
|
||||
if(!deviceName)
|
||||
deviceName = solaris_device;
|
||||
else if(strcmp(deviceName, solaris_device) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (solaris_data*)calloc(1, sizeof(solaris_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(driver, O_WRONLY);
|
||||
if(data->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
AL_PRINT("Could not open %s: %s\n", driver, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void solaris_close_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
close(data->fd);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
audio_info_t info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
|
||||
AUDIO_INITINFO(&info);
|
||||
|
||||
switch(aluBytesFromFormat(device->Format))
|
||||
{
|
||||
case 1:
|
||||
info.play.precision = 8;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR8;
|
||||
break;
|
||||
case 4:
|
||||
switch(numChannels)
|
||||
{
|
||||
case 1: device->Format = AL_FORMAT_MONO16; break;
|
||||
case 2: device->Format = AL_FORMAT_STEREO16; break;
|
||||
case 4: device->Format = AL_FORMAT_QUAD16; break;
|
||||
case 6: device->Format = AL_FORMAT_51CHN16; break;
|
||||
case 7: device->Format = AL_FORMAT_61CHN16; break;
|
||||
case 8: device->Format = AL_FORMAT_71CHN16; break;
|
||||
}
|
||||
/* fall-through */
|
||||
case 2:
|
||||
info.play.precision = 16;
|
||||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
default:
|
||||
AL_PRINT("Unknown format: 0x%x\n", device->Format);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
numChannels = aluChannelsFromFormat(device->Format);
|
||||
info.play.sample_rate = device->Frequency;
|
||||
info.play.channels = numChannels;
|
||||
|
||||
frameSize = numChannels * aluBytesFromFormat(device->Format);
|
||||
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
|
||||
|
||||
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
|
||||
{
|
||||
AL_PRINT("ioctl failed: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(aluChannelsFromFormat(device->Format) != info.play.channels)
|
||||
{
|
||||
AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), info.play.channels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!((info.play.precision == 8 && aluBytesFromFormat(device->Format) == 1) ||
|
||||
(info.play.precision == 16 && aluBytesFromFormat(device->Format) == 2)))
|
||||
{
|
||||
AL_PRINT("Could not set %d-bit output, got %d\n", aluBytesFromFormat(device->Format)*8, info.play.precision);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->Frequency = info.play.sample_rate;
|
||||
device->UpdateSize = (info.play.buffer_size/device->NumUpdates) + 1;
|
||||
|
||||
data->data_size = device->UpdateSize * frameSize;
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->thread = StartThread(SolarisProc, device);
|
||||
if(data->thread == NULL)
|
||||
{
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void solaris_stop_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
if(!data->thread)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
StopThread(data->thread);
|
||||
data->thread = NULL;
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean solaris_open_capture(ALCdevice *device, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
|
||||
{
|
||||
(void)device;
|
||||
(void)deviceName;
|
||||
(void)frequency;
|
||||
(void)format;
|
||||
(void)SampleSize;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void solaris_close_capture(ALCdevice *device)
|
||||
{
|
||||
(void)device;
|
||||
}
|
||||
|
||||
static void solaris_start_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void solaris_stop_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void solaris_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)pBuffer;
|
||||
(void)lSamples;
|
||||
}
|
||||
|
||||
static ALCuint solaris_available_samples(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs solaris_funcs = {
|
||||
solaris_open_playback,
|
||||
solaris_close_playback,
|
||||
solaris_reset_playback,
|
||||
solaris_stop_playback,
|
||||
solaris_open_capture,
|
||||
solaris_close_capture,
|
||||
solaris_start_capture,
|
||||
solaris_stop_capture,
|
||||
solaris_capture_samples,
|
||||
solaris_available_samples
|
||||
};
|
||||
|
||||
void alc_solaris_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = solaris_funcs;
|
||||
}
|
||||
|
||||
void alc_solaris_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_solaris_probe(ALCboolean capture)
|
||||
{
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(solaris_device);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(solaris_device);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef AL_VECTOR_H
|
||||
#define AL_VECTOR_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <AL/al.h>
|
||||
|
||||
/* "Base" vector type, designed to alias with the actual vector types. */
|
||||
typedef struct vector__s {
|
||||
ALsizei Capacity;
|
||||
ALsizei Size;
|
||||
} *vector_;
|
||||
|
||||
#define TYPEDEF_VECTOR(T, N) typedef struct { \
|
||||
ALsizei Capacity; \
|
||||
ALsizei Size; \
|
||||
T Data[]; \
|
||||
} _##N; \
|
||||
typedef _##N* N; \
|
||||
typedef const _##N* const_##N;
|
||||
|
||||
#define VECTOR(T) struct { \
|
||||
ALsizei Capacity; \
|
||||
ALsizei Size; \
|
||||
T Data[]; \
|
||||
}*
|
||||
|
||||
#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0)
|
||||
#define VECTOR_INIT_STATIC() NULL
|
||||
#define VECTOR_DEINIT(_x) do { free((_x)); (_x) = NULL; } while(0)
|
||||
|
||||
/* Helper to increase a vector's reserve. Do not call directly. */
|
||||
ALboolean vector_reserve(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count, ALboolean exact);
|
||||
#define VECTOR_RESERVE(_x, _c) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c), AL_TRUE))
|
||||
|
||||
ALboolean vector_resize(char *ptr, size_t base_size, size_t obj_size, ALsizei obj_count);
|
||||
#define VECTOR_RESIZE(_x, _c) (vector_resize((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_c)))
|
||||
|
||||
#define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0)
|
||||
#define VECTOR_SIZE(_x) ((_x) ? (_x)->Size : 0)
|
||||
|
||||
#define VECTOR_ITER_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
|
||||
#define VECTOR_ITER_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL)
|
||||
|
||||
ALboolean vector_insert(char *ptr, size_t base_size, size_t obj_size, void *ins_pos, const void *datstart, const void *datend);
|
||||
#ifdef __GNUC__
|
||||
#define TYPE_CHECK(T1, T2) __builtin_types_compatible_p(T1, T2)
|
||||
#define VECTOR_INSERT(_x, _i, _s, _e) __extension__({ \
|
||||
ALboolean _r; \
|
||||
static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_i))), "Incompatible insertion iterator"); \
|
||||
static_assert(TYPE_CHECK(__typeof((_x)->Data[0]), __typeof(*(_s))), "Incompatible insertion source type"); \
|
||||
static_assert(TYPE_CHECK(__typeof(*(_s)), __typeof(*(_e))), "Incompatible iterator sources"); \
|
||||
_r = vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)); \
|
||||
_r; \
|
||||
})
|
||||
#else
|
||||
#define VECTOR_INSERT(_x, _i, _s, _e) (vector_insert((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), (_i), (_s), (_e)))
|
||||
#endif
|
||||
|
||||
#define VECTOR_PUSH_BACK(_x, _obj) (vector_reserve((char*)&(_x), sizeof(*(_x)), sizeof((_x)->Data[0]), VECTOR_SIZE(_x)+1, AL_FALSE) && \
|
||||
(((_x)->Data[(_x)->Size++] = (_obj)),AL_TRUE))
|
||||
#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
|
||||
|
||||
#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1])
|
||||
#define VECTOR_FRONT(_x) ((_x)->Data[0])
|
||||
|
||||
#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)])
|
||||
|
||||
#define VECTOR_FOR_EACH(_t, _x, _f) do { \
|
||||
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_ITER_END((_x)); \
|
||||
for(;_iter != _end;++_iter) \
|
||||
_f(_iter); \
|
||||
} while(0)
|
||||
|
||||
#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \
|
||||
_t *_iter = VECTOR_ITER_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_ITER_END((_x)); \
|
||||
for(;_iter != _end;++_iter) \
|
||||
{ \
|
||||
if(_f(_iter)) \
|
||||
break; \
|
||||
} \
|
||||
(_i) = _iter; \
|
||||
} while(0)
|
||||
|
||||
#endif /* AL_VECTOR_H */
|
||||
-347
@@ -1,347 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
long DataStart;
|
||||
|
||||
ALvoid *buffer;
|
||||
ALuint size;
|
||||
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
} wave_data;
|
||||
|
||||
|
||||
static const ALCchar waveDevice[] = "Wave File Writer";
|
||||
|
||||
|
||||
static ALuint WaveProc(ALvoid *ptr)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)ptr;
|
||||
wave_data *data = (wave_data*)pDevice->ExtraData;
|
||||
ALuint frameSize;
|
||||
ALuint now, last;
|
||||
size_t fs;
|
||||
ALuint avail;
|
||||
union {
|
||||
short s;
|
||||
char b[sizeof(short)];
|
||||
} uSB;
|
||||
|
||||
uSB.s = 1;
|
||||
frameSize = aluBytesFromFormat(pDevice->Format) *
|
||||
aluChannelsFromFormat(pDevice->Format);
|
||||
|
||||
last = timeGetTime();
|
||||
while(!data->killNow && pDevice->Connected)
|
||||
{
|
||||
now = timeGetTime();
|
||||
|
||||
avail = (now-last) * pDevice->Frequency / 1000;
|
||||
if(avail < pDevice->UpdateSize)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
while(avail >= pDevice->UpdateSize)
|
||||
{
|
||||
aluMixData(pDevice, data->buffer, pDevice->UpdateSize);
|
||||
|
||||
if(uSB.b[0] != 1 && aluBytesFromFormat(pDevice->Format) > 1)
|
||||
{
|
||||
ALubyte *bytes = data->buffer;
|
||||
ALuint i;
|
||||
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^1], data->f);
|
||||
}
|
||||
else
|
||||
fs = fwrite(data->buffer, frameSize, pDevice->UpdateSize,
|
||||
data->f);
|
||||
if(ferror(data->f))
|
||||
{
|
||||
AL_PRINT("Error writing to file\n");
|
||||
aluHandleDisconnect(pDevice);
|
||||
break;
|
||||
}
|
||||
|
||||
avail -= pDevice->UpdateSize;
|
||||
}
|
||||
last = now;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
wave_data *data;
|
||||
const char *fname;
|
||||
|
||||
fname = GetConfigValue("wave", "file", "");
|
||||
if(!fname[0])
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = waveDevice;
|
||||
else if(strcmp(deviceName, waveDevice) != 0)
|
||||
return ALC_FALSE;
|
||||
|
||||
data = (wave_data*)calloc(1, sizeof(wave_data));
|
||||
|
||||
data->f = fopen(fname, "wb");
|
||||
if(!data->f)
|
||||
{
|
||||
free(data);
|
||||
AL_PRINT("Could not open file '%s': %s\n", fname, strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void wave_close_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
|
||||
fclose(data->f);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean wave_reset_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint channels, bits, i;
|
||||
|
||||
fseek(data->f, 0, SEEK_SET);
|
||||
clearerr(data->f);
|
||||
|
||||
bits = aluBytesFromFormat(device->Format) * 8;
|
||||
channels = aluChannelsFromFormat(device->Format);
|
||||
switch(bits)
|
||||
{
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
if(channels == 0)
|
||||
{
|
||||
AL_PRINT("Unknown format?! %x\n", device->Format);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
AL_PRINT("Unknown format?! %x\n", device->Format);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
fprintf(data->f, "RIFF");
|
||||
fputc(0, data->f); // 'RIFF' header len; filled in at close
|
||||
fputc(0, data->f);
|
||||
fputc(0, data->f);
|
||||
fputc(0, data->f);
|
||||
|
||||
fprintf(data->f, "WAVE");
|
||||
|
||||
fprintf(data->f, "fmt ");
|
||||
fputc(16, data->f); // 'fmt ' header len; 16 bytes for PCM
|
||||
fputc(0, data->f);
|
||||
fputc(0, data->f);
|
||||
fputc(0, data->f);
|
||||
// 16-bit val, format type id (PCM: 1)
|
||||
fputc(1, data->f);
|
||||
fputc(0, data->f);
|
||||
// 16-bit val, channel count
|
||||
fputc(channels&0xff, data->f);
|
||||
fputc((channels>>8)&0xff, data->f);
|
||||
// 32-bit val, frequency
|
||||
fputc(device->Frequency&0xff, data->f);
|
||||
fputc((device->Frequency>>8)&0xff, data->f);
|
||||
fputc((device->Frequency>>16)&0xff, data->f);
|
||||
fputc((device->Frequency>>24)&0xff, data->f);
|
||||
// 32-bit val, bytes per second
|
||||
i = device->Frequency * channels * bits / 8;
|
||||
fputc(i&0xff, data->f);
|
||||
fputc((i>>8)&0xff, data->f);
|
||||
fputc((i>>16)&0xff, data->f);
|
||||
fputc((i>>24)&0xff, data->f);
|
||||
// 16-bit val, frame size
|
||||
i = channels * bits / 8;
|
||||
fputc(i&0xff, data->f);
|
||||
fputc((i>>8)&0xff, data->f);
|
||||
// 16-bit val, bits per sample
|
||||
fputc(bits&0xff, data->f);
|
||||
fputc((bits>>8)&0xff, data->f);
|
||||
|
||||
fprintf(data->f, "data");
|
||||
fputc(0, data->f); // 'data' header len; filled in at close
|
||||
fputc(0, data->f);
|
||||
fputc(0, data->f);
|
||||
fputc(0, data->f);
|
||||
|
||||
if(ferror(data->f))
|
||||
{
|
||||
AL_PRINT("Error writing header: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->DataStart = ftell(data->f);
|
||||
|
||||
data->size = device->UpdateSize * channels * bits / 8;
|
||||
data->buffer = malloc(data->size);
|
||||
if(!data->buffer)
|
||||
{
|
||||
AL_PRINT("buffer malloc failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->thread = StartThread(WaveProc, device);
|
||||
if(data->thread == NULL)
|
||||
{
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void wave_stop_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint dataLen;
|
||||
long size;
|
||||
|
||||
if(!data->thread)
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
StopThread(data->thread);
|
||||
data->thread = NULL;
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
|
||||
size = ftell(data->f);
|
||||
if(size > 0)
|
||||
{
|
||||
dataLen = size - data->DataStart;
|
||||
if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
|
||||
{
|
||||
fputc(dataLen&0xff, data->f); // 'data' header len
|
||||
fputc((dataLen>>8)&0xff, data->f);
|
||||
fputc((dataLen>>16)&0xff, data->f);
|
||||
fputc((dataLen>>24)&0xff, data->f);
|
||||
}
|
||||
if(fseek(data->f, 4, SEEK_SET) == 0)
|
||||
{
|
||||
size -= 8;
|
||||
fputc(size&0xff, data->f); // 'WAVE' header len
|
||||
fputc((size>>8)&0xff, data->f);
|
||||
fputc((size>>16)&0xff, data->f);
|
||||
fputc((size>>24)&0xff, data->f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean wave_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)deviceName;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void wave_close_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void wave_start_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void wave_stop_capture(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
}
|
||||
|
||||
static void wave_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
(void)pDevice;
|
||||
(void)pBuffer;
|
||||
(void)lSamples;
|
||||
}
|
||||
|
||||
static ALCuint wave_available_samples(ALCdevice *pDevice)
|
||||
{
|
||||
(void)pDevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs wave_funcs = {
|
||||
wave_open_playback,
|
||||
wave_close_playback,
|
||||
wave_reset_playback,
|
||||
wave_stop_playback,
|
||||
wave_open_capture,
|
||||
wave_close_capture,
|
||||
wave_start_capture,
|
||||
wave_stop_capture,
|
||||
wave_capture_samples,
|
||||
wave_available_samples
|
||||
};
|
||||
|
||||
void alc_wave_init(BackendFuncs *func_list)
|
||||
{
|
||||
*func_list = wave_funcs;
|
||||
}
|
||||
|
||||
void alc_wave_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
void alc_wave_probe(int type)
|
||||
{
|
||||
if(*(GetConfigValue("wave", "file", "")) == 0)
|
||||
return;
|
||||
|
||||
if(type == DEVICE_PROBE)
|
||||
AppendDeviceList(waveDevice);
|
||||
else if(type == ALL_DEVICE_PROBE)
|
||||
AppendAllDeviceList(waveDevice);
|
||||
}
|
||||
-470
@@ -1,470 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define _WIN32_WINNT 0x0500
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
// MMSYSTEM Capture Device
|
||||
ALboolean bWaveInShutdown;
|
||||
HANDLE hWaveInHdrEvent;
|
||||
HANDLE hWaveInThreadEvent;
|
||||
HANDLE hWaveInThread;
|
||||
DWORD ulWaveInThreadID;
|
||||
ALint lWaveInBuffersCommitted;
|
||||
HWAVEIN hWaveInHandle;
|
||||
WAVEHDR WaveInBuffer[4];
|
||||
ALCchar *pCapturedSampleData;
|
||||
ALuint ulCapturedDataSize;
|
||||
ALuint ulReadCapturedDataPos;
|
||||
ALuint ulWriteCapturedDataPos;
|
||||
} WinMMData;
|
||||
|
||||
|
||||
static ALCchar **CaptureDeviceList;
|
||||
static ALuint NumCaptureDevices;
|
||||
|
||||
/*
|
||||
WaveInProc
|
||||
|
||||
Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
|
||||
returns to the application (with more data)
|
||||
*/
|
||||
static void CALLBACK WaveInProc(HWAVEIN hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice *)dwInstance;
|
||||
WinMMData *pData = pDevice->ExtraData;
|
||||
|
||||
(void)hDevice;
|
||||
(void)dwParam2;
|
||||
|
||||
if ((uMsg==WIM_DATA))
|
||||
{
|
||||
// Decrement number of buffers in use
|
||||
pData->lWaveInBuffersCommitted--;
|
||||
|
||||
if (pData->bWaveInShutdown == AL_FALSE)
|
||||
{
|
||||
// Notify Wave Processor Thread that a Wave Header has returned
|
||||
PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pData->lWaveInBuffersCommitted == 0)
|
||||
{
|
||||
// Signal Wave Buffers Returned event
|
||||
if (pData->hWaveInHdrEvent)
|
||||
SetEvent(pData->hWaveInHdrEvent);
|
||||
|
||||
// Post 'Quit' Message to WaveIn Processor Thread
|
||||
PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CaptureThreadProc
|
||||
|
||||
Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
|
||||
audio data.
|
||||
*/
|
||||
DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)lpParameter;
|
||||
WinMMData *pData = pDevice->ExtraData;
|
||||
ALuint ulOffset, ulMaxSize, ulSection;
|
||||
LPWAVEHDR pWaveHdr;
|
||||
MSG msg;
|
||||
|
||||
while (GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
if ((msg.message==WIM_DATA)&&(!pData->bWaveInShutdown))
|
||||
{
|
||||
SuspendContext(NULL);
|
||||
|
||||
pWaveHdr = ((LPWAVEHDR)msg.lParam);
|
||||
|
||||
// Calculate offset in local buffer to write data to
|
||||
ulOffset = pData->ulWriteCapturedDataPos % pData->ulCapturedDataSize;
|
||||
|
||||
if ((ulOffset + pWaveHdr->dwBytesRecorded) > pData->ulCapturedDataSize)
|
||||
{
|
||||
ulSection = pData->ulCapturedDataSize - ulOffset;
|
||||
memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, ulSection);
|
||||
memcpy(pData->pCapturedSampleData, pWaveHdr->lpData + ulSection, pWaveHdr->dwBytesRecorded - ulSection);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(pData->pCapturedSampleData + ulOffset, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded);
|
||||
}
|
||||
|
||||
pData->ulWriteCapturedDataPos += pWaveHdr->dwBytesRecorded;
|
||||
|
||||
if (pData->ulWriteCapturedDataPos > (pData->ulReadCapturedDataPos + pData->ulCapturedDataSize))
|
||||
{
|
||||
// Application has not read enough audio data from the capture buffer so data has been
|
||||
// overwritten. Reset ReadPosition.
|
||||
pData->ulReadCapturedDataPos = pData->ulWriteCapturedDataPos - pData->ulCapturedDataSize;
|
||||
}
|
||||
|
||||
// To prevent an over-flow prevent the offset values from getting too large
|
||||
ulMaxSize = pData->ulCapturedDataSize << 4;
|
||||
if ((pData->ulReadCapturedDataPos > ulMaxSize) && (pData->ulWriteCapturedDataPos > ulMaxSize))
|
||||
{
|
||||
pData->ulReadCapturedDataPos -= ulMaxSize;
|
||||
pData->ulWriteCapturedDataPos -= ulMaxSize;
|
||||
}
|
||||
|
||||
// Send buffer back to capture more data
|
||||
waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
|
||||
pData->lWaveInBuffersCommitted++;
|
||||
|
||||
ProcessContext(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Signal Wave Thread completed event
|
||||
if (pData->hWaveInThreadEvent)
|
||||
SetEvent(pData->hWaveInThreadEvent);
|
||||
|
||||
ExitThread(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
(void)device;
|
||||
(void)deviceName;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void WinMMClosePlayback(ALCdevice *device)
|
||||
{
|
||||
(void)device;
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
|
||||
{
|
||||
WAVEFORMATEX wfexCaptureFormat;
|
||||
WinMMData *pData = NULL;
|
||||
ALint lDeviceID = 0;
|
||||
ALint lBufferSize;
|
||||
ALuint i;
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
if (deviceName)
|
||||
{
|
||||
for(i = 0;i < NumCaptureDevices;i++)
|
||||
{
|
||||
if (!strcmp(deviceName, CaptureDeviceList[i]))
|
||||
{
|
||||
lDeviceID = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == NumCaptureDevices)
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
pData = calloc(1, sizeof(*pData));
|
||||
if(!pData)
|
||||
{
|
||||
SetALCError(ALC_OUT_OF_MEMORY);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
|
||||
wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfexCaptureFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
|
||||
wfexCaptureFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
|
||||
wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
|
||||
wfexCaptureFormat.nChannels / 8;
|
||||
wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
|
||||
wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
|
||||
wfexCaptureFormat.nBlockAlign;
|
||||
wfexCaptureFormat.cbSize = 0;
|
||||
|
||||
if (waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
|
||||
goto failure;
|
||||
|
||||
pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
|
||||
if (pData->hWaveInHdrEvent == NULL)
|
||||
goto failure;
|
||||
|
||||
pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
|
||||
if (pData->hWaveInThreadEvent == NULL)
|
||||
goto failure;
|
||||
|
||||
// Allocate circular memory buffer for the captured audio
|
||||
pData->ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates *
|
||||
wfexCaptureFormat.nBlockAlign;
|
||||
|
||||
// Make sure circular buffer is at least 100ms in size (and an exact multiple of
|
||||
// the block alignment
|
||||
if (pData->ulCapturedDataSize < (wfexCaptureFormat.nAvgBytesPerSec / 10))
|
||||
{
|
||||
pData->ulCapturedDataSize = wfexCaptureFormat.nAvgBytesPerSec / 10;
|
||||
pData->ulCapturedDataSize -= (pData->ulCapturedDataSize % wfexCaptureFormat.nBlockAlign);
|
||||
}
|
||||
|
||||
pData->pCapturedSampleData = (ALCchar*)malloc(pData->ulCapturedDataSize);
|
||||
pData->lWaveInBuffersCommitted=0;
|
||||
|
||||
// Create 4 Buffers of 50ms each
|
||||
lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
|
||||
lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
|
||||
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR));
|
||||
pData->WaveInBuffer[i].dwBufferLength = lBufferSize;
|
||||
pData->WaveInBuffer[i].lpData = calloc(1,pData->WaveInBuffer[i].dwBufferLength);
|
||||
pData->WaveInBuffer[i].dwFlags = 0;
|
||||
pData->WaveInBuffer[i].dwLoops = 0;
|
||||
waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
|
||||
waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
|
||||
pData->lWaveInBuffersCommitted++;
|
||||
}
|
||||
|
||||
pData->ulReadCapturedDataPos = 0;
|
||||
pData->ulWriteCapturedDataPos = 0;
|
||||
|
||||
pDevice->ExtraData = pData;
|
||||
|
||||
pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
|
||||
if (pData->hWaveInThread == NULL)
|
||||
goto failure;
|
||||
|
||||
pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
|
||||
return ALC_TRUE;
|
||||
|
||||
failure:
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
if(pData->WaveInBuffer[i].lpData)
|
||||
{
|
||||
waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
|
||||
free(pData->WaveInBuffer[i].lpData);
|
||||
}
|
||||
}
|
||||
|
||||
free(pData->pCapturedSampleData);
|
||||
if(pData->hWaveInHandle)
|
||||
waveInClose(pData->hWaveInHandle);
|
||||
if(pData->hWaveInThread)
|
||||
CloseHandle(pData->hWaveInThread);
|
||||
if (pData->hWaveInHdrEvent)
|
||||
CloseHandle(pData->hWaveInHdrEvent);
|
||||
if (pData->hWaveInThreadEvent)
|
||||
CloseHandle(pData->hWaveInThreadEvent);
|
||||
|
||||
free(pData);
|
||||
pDevice->ExtraData = NULL;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void WinMMCloseCapture(ALCdevice *pDevice)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
int i;
|
||||
|
||||
// Call waveOutReset to shutdown wave device
|
||||
pData->bWaveInShutdown = AL_TRUE;
|
||||
waveInReset(pData->hWaveInHandle);
|
||||
|
||||
// Wait for signal that all Wave Buffers have returned
|
||||
WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
|
||||
|
||||
// Wait for signal that Wave Thread has been destroyed
|
||||
WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
|
||||
|
||||
// Release the wave buffers
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
|
||||
free(pData->WaveInBuffer[i].lpData);
|
||||
}
|
||||
|
||||
// Free Audio Buffer data
|
||||
free(pData->pCapturedSampleData);
|
||||
pData->pCapturedSampleData = NULL;
|
||||
|
||||
// Close the Wave device
|
||||
waveInClose(pData->hWaveInHandle);
|
||||
pData->hWaveInHandle = 0;
|
||||
|
||||
CloseHandle(pData->hWaveInThread);
|
||||
pData->hWaveInThread = 0;
|
||||
|
||||
if (pData->hWaveInHdrEvent)
|
||||
{
|
||||
CloseHandle(pData->hWaveInHdrEvent);
|
||||
pData->hWaveInHdrEvent = 0;
|
||||
}
|
||||
|
||||
if (pData->hWaveInThreadEvent)
|
||||
{
|
||||
CloseHandle(pData->hWaveInThreadEvent);
|
||||
pData->hWaveInThreadEvent = 0;
|
||||
}
|
||||
|
||||
free(pData);
|
||||
pDevice->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void WinMMStartCapture(ALCdevice *pDevice)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
waveInStart(pData->hWaveInHandle);
|
||||
}
|
||||
|
||||
static void WinMMStopCapture(ALCdevice *pDevice)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
waveInStop(pData->hWaveInHandle);
|
||||
}
|
||||
|
||||
static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
ALuint ulSamples = (unsigned long)lSamples;
|
||||
ALuint ulBytes, ulBytesToCopy;
|
||||
ALuint ulCapturedSamples;
|
||||
ALuint ulReadOffset;
|
||||
ALuint frameSize = aluBytesFromFormat(pDevice->Format) *
|
||||
aluChannelsFromFormat(pDevice->Format);
|
||||
|
||||
// Check that we have the requested numbers of Samples
|
||||
ulCapturedSamples = (pData->ulWriteCapturedDataPos -
|
||||
pData->ulReadCapturedDataPos) /
|
||||
frameSize;
|
||||
if(ulSamples > ulCapturedSamples)
|
||||
{
|
||||
SetALCError(ALC_INVALID_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
ulBytes = ulSamples * frameSize;
|
||||
|
||||
// Get Read Offset
|
||||
ulReadOffset = (pData->ulReadCapturedDataPos % pData->ulCapturedDataSize);
|
||||
|
||||
// Check for wrap-around condition
|
||||
if ((ulReadOffset + ulBytes) > pData->ulCapturedDataSize)
|
||||
{
|
||||
// Copy data from last Read position to end of data
|
||||
ulBytesToCopy = pData->ulCapturedDataSize - ulReadOffset;
|
||||
memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytesToCopy);
|
||||
|
||||
// Copy rest of the data from the start of the captured data
|
||||
memcpy(((char *)pBuffer) + ulBytesToCopy, pData->pCapturedSampleData, ulBytes - ulBytesToCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy data from the read position in the captured data
|
||||
memcpy(pBuffer, pData->pCapturedSampleData + ulReadOffset, ulBytes);
|
||||
}
|
||||
|
||||
// Update Read Position
|
||||
pData->ulReadCapturedDataPos += ulBytes;
|
||||
}
|
||||
|
||||
static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
|
||||
{
|
||||
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
|
||||
ALCuint lCapturedBytes = (pData->ulWriteCapturedDataPos - pData->ulReadCapturedDataPos);
|
||||
return lCapturedBytes / (aluBytesFromFormat(pDevice->Format) *
|
||||
aluChannelsFromFormat(pDevice->Format));
|
||||
}
|
||||
|
||||
|
||||
BackendFuncs WinMMFuncs = {
|
||||
WinMMOpenPlayback,
|
||||
WinMMClosePlayback,
|
||||
NULL,
|
||||
NULL,
|
||||
WinMMOpenCapture,
|
||||
WinMMCloseCapture,
|
||||
WinMMStartCapture,
|
||||
WinMMStopCapture,
|
||||
WinMMCaptureSamples,
|
||||
WinMMAvailableSamples
|
||||
};
|
||||
|
||||
void alcWinMMInit(BackendFuncs *FuncList)
|
||||
{
|
||||
*FuncList = WinMMFuncs;
|
||||
}
|
||||
|
||||
void alcWinMMDeinit()
|
||||
{
|
||||
ALuint lLoop;
|
||||
|
||||
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
|
||||
free(CaptureDeviceList[lLoop]);
|
||||
free(CaptureDeviceList);
|
||||
CaptureDeviceList = NULL;
|
||||
|
||||
NumCaptureDevices = 0;
|
||||
}
|
||||
|
||||
void alcWinMMProbe(int type)
|
||||
{
|
||||
ALuint lLoop;
|
||||
|
||||
if(type != CAPTURE_DEVICE_PROBE)
|
||||
return;
|
||||
|
||||
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
|
||||
free(CaptureDeviceList[lLoop]);
|
||||
|
||||
NumCaptureDevices = waveInGetNumDevs();
|
||||
CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
|
||||
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
|
||||
{
|
||||
WAVEINCAPS WaveInCaps;
|
||||
|
||||
if(waveInGetDevCaps(lLoop, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "WaveIn on %s", WaveInCaps.szPname);
|
||||
AppendCaptureDeviceList(name);
|
||||
CaptureDeviceList[lLoop] = strdup(name);
|
||||
}
|
||||
else
|
||||
CaptureDeviceList[lLoop] = strdup("");
|
||||
}
|
||||
}
|
||||
+969
-187
File diff suppressed because it is too large
Load Diff
@@ -1,72 +1,114 @@
|
||||
#ifndef _AL_AUXEFFECTSLOT_H_
|
||||
#define _AL_AUXEFFECTSLOT_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
#include "alEffect.h"
|
||||
#include "alFilter.h"
|
||||
|
||||
#include "align.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define AL_EFFECTSLOT_EFFECT 0x0001
|
||||
#define AL_EFFECTSLOT_GAIN 0x0002
|
||||
#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003
|
||||
struct ALeffectStateVtable;
|
||||
struct ALeffectslot;
|
||||
|
||||
#define AL_EFFECTSLOT_NULL 0x0000
|
||||
typedef struct ALeffectState {
|
||||
const struct ALeffectStateVtable *vtbl;
|
||||
} ALeffectState;
|
||||
|
||||
typedef struct ALeffectState ALeffectState;
|
||||
struct ALeffectStateVtable {
|
||||
void (*const Destruct)(ALeffectState *state);
|
||||
|
||||
typedef struct ALeffectslot
|
||||
{
|
||||
ALeffect effect;
|
||||
ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device);
|
||||
void (*const update)(ALeffectState *state, ALCdevice *device, const struct ALeffectslot *slot);
|
||||
void (*const process)(ALeffectState *state, ALuint samplesToDo, const ALfloat *restrict samplesIn, ALfloat (*restrict samplesOut)[BUFFERSIZE]);
|
||||
|
||||
ALfloat Gain;
|
||||
ALboolean AuxSendAuto;
|
||||
void (*const Delete)(void *ptr);
|
||||
};
|
||||
|
||||
#define DEFINE_ALEFFECTSTATE_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALeffectState, void, Destruct) \
|
||||
DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*) \
|
||||
DECLARE_THUNK2(T, ALeffectState, void, update, ALCdevice*, const ALeffectslot*) \
|
||||
DECLARE_THUNK3(T, ALeffectState, void, process, ALuint, const ALfloat*restrict, ALfloatBUFFERSIZE*restrict) \
|
||||
static void T##_ALeffectState_Delete(void *ptr) \
|
||||
{ return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); } \
|
||||
\
|
||||
static const struct ALeffectStateVtable T##_ALeffectState_vtable = { \
|
||||
T##_ALeffectState_Destruct, \
|
||||
\
|
||||
T##_ALeffectState_deviceUpdate, \
|
||||
T##_ALeffectState_update, \
|
||||
T##_ALeffectState_process, \
|
||||
\
|
||||
T##_ALeffectState_Delete, \
|
||||
}
|
||||
|
||||
|
||||
struct ALeffectStateFactoryVtable;
|
||||
|
||||
typedef struct ALeffectStateFactory {
|
||||
const struct ALeffectStateFactoryVtable *vtbl;
|
||||
} ALeffectStateFactory;
|
||||
|
||||
struct ALeffectStateFactoryVtable {
|
||||
ALeffectState *(*const create)(ALeffectStateFactory *factory);
|
||||
};
|
||||
|
||||
#define DEFINE_ALEFFECTSTATEFACTORY_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALeffectStateFactory, ALeffectState*, create) \
|
||||
\
|
||||
static const struct ALeffectStateFactoryVtable T##_ALeffectStateFactory_vtable = { \
|
||||
T##_ALeffectStateFactory_create, \
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALeffectslot {
|
||||
ALenum EffectType;
|
||||
ALeffectProps EffectProps;
|
||||
|
||||
volatile ALfloat Gain;
|
||||
volatile ALboolean AuxSendAuto;
|
||||
|
||||
ATOMIC(ALenum) NeedsUpdate;
|
||||
ALeffectState *EffectState;
|
||||
|
||||
ALfloat WetBuffer[BUFFERSIZE];
|
||||
alignas(16) ALfloat WetBuffer[1][BUFFERSIZE];
|
||||
|
||||
ALuint refcount;
|
||||
RefCount ref;
|
||||
|
||||
// Index to itself
|
||||
ALuint effectslot;
|
||||
|
||||
struct ALeffectslot *next;
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALeffectslot;
|
||||
|
||||
ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
|
||||
ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
|
||||
ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot);
|
||||
|
||||
ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue);
|
||||
ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
|
||||
ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue);
|
||||
ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
|
||||
|
||||
ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue);
|
||||
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
|
||||
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue);
|
||||
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
|
||||
inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALeffectslot*)LookupUIntMapKey(&context->EffectSlotMap, id); }
|
||||
inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALeffectslot*)RemoveUIntMapKey(&context->EffectSlotMap, id); }
|
||||
|
||||
ALenum InitEffectSlot(ALeffectslot *slot);
|
||||
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
|
||||
|
||||
|
||||
struct ALeffectState {
|
||||
ALvoid (*Destroy)(ALeffectState *State);
|
||||
ALvoid (*Update)(ALeffectState *State, ALCcontext *Context, ALeffect *Effect);
|
||||
ALvoid (*Process)(ALeffectState *State, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[OUTPUTCHANNELS]);
|
||||
};
|
||||
ALeffectStateFactory *ALnullStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALreverbStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALautowahStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALchorusStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALcompressorStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALdistortionStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALechoStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALequalizerStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALflangerStateFactory_getFactory(void);
|
||||
ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void);
|
||||
|
||||
ALeffectState *EAXVerbCreate(ALCcontext *Context);
|
||||
ALeffectState *VerbCreate(ALCcontext *Context);
|
||||
ALeffectState *EchoCreate(ALCcontext *Context);
|
||||
ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void);
|
||||
|
||||
#define ALEffect_Destroy(a) ((a)->Destroy((a)))
|
||||
#define ALEffect_Update(a,b,c) ((a)->Update((a),(b),(c)))
|
||||
#define ALEffect_Process(a,b,c,d,e) ((a)->Process((a),(b),(c),(d),(e)))
|
||||
|
||||
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect);
|
||||
|
||||
void InitEffectFactoryMap(void);
|
||||
void DeinitEffectFactoryMap(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+96
-15
@@ -1,30 +1,111 @@
|
||||
#ifndef _AL_BUFFER_H_
|
||||
#define _AL_BUFFER_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define UNUSED 0
|
||||
#define PENDING 1
|
||||
#define PROCESSED 2
|
||||
/* User formats */
|
||||
enum UserFmtType {
|
||||
UserFmtByte = AL_BYTE_SOFT,
|
||||
UserFmtUByte = AL_UNSIGNED_BYTE_SOFT,
|
||||
UserFmtShort = AL_SHORT_SOFT,
|
||||
UserFmtUShort = AL_UNSIGNED_SHORT_SOFT,
|
||||
UserFmtInt = AL_INT_SOFT,
|
||||
UserFmtUInt = AL_UNSIGNED_INT_SOFT,
|
||||
UserFmtFloat = AL_FLOAT_SOFT,
|
||||
UserFmtDouble = AL_DOUBLE_SOFT,
|
||||
UserFmtByte3 = AL_BYTE3_SOFT,
|
||||
UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT,
|
||||
UserFmtMulaw,
|
||||
UserFmtAlaw,
|
||||
UserFmtIMA4,
|
||||
UserFmtMSADPCM,
|
||||
};
|
||||
enum UserFmtChannels {
|
||||
UserFmtMono = AL_MONO_SOFT,
|
||||
UserFmtStereo = AL_STEREO_SOFT,
|
||||
UserFmtRear = AL_REAR_SOFT,
|
||||
UserFmtQuad = AL_QUAD_SOFT,
|
||||
UserFmtX51 = AL_5POINT1_SOFT, /* (WFX order) */
|
||||
UserFmtX61 = AL_6POINT1_SOFT, /* (WFX order) */
|
||||
UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */
|
||||
};
|
||||
|
||||
typedef struct ALbuffer
|
||||
ALuint BytesFromUserFmt(enum UserFmtType type) DECL_CONST;
|
||||
ALuint ChannelsFromUserFmt(enum UserFmtChannels chans) DECL_CONST;
|
||||
inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
|
||||
{
|
||||
ALenum format;
|
||||
ALenum eOriginalFormat;
|
||||
ALshort *data;
|
||||
ALsizei size;
|
||||
ALsizei frequency;
|
||||
ALsizei padding;
|
||||
ALenum state;
|
||||
ALuint refcount; // Number of sources using this buffer (deletion can only occur when this is 0)
|
||||
struct ALbuffer *next;
|
||||
return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type);
|
||||
}
|
||||
|
||||
|
||||
/* Storable formats */
|
||||
enum FmtType {
|
||||
FmtByte = UserFmtByte,
|
||||
FmtShort = UserFmtShort,
|
||||
FmtFloat = UserFmtFloat,
|
||||
};
|
||||
enum FmtChannels {
|
||||
FmtMono = UserFmtMono,
|
||||
FmtStereo = UserFmtStereo,
|
||||
FmtRear = UserFmtRear,
|
||||
FmtQuad = UserFmtQuad,
|
||||
FmtX51 = UserFmtX51,
|
||||
FmtX61 = UserFmtX61,
|
||||
FmtX71 = UserFmtX71,
|
||||
};
|
||||
#define MAX_INPUT_CHANNELS (8)
|
||||
|
||||
ALuint BytesFromFmt(enum FmtType type) DECL_CONST;
|
||||
ALuint ChannelsFromFmt(enum FmtChannels chans) DECL_CONST;
|
||||
inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
|
||||
{
|
||||
return ChannelsFromFmt(chans) * BytesFromFmt(type);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALbuffer {
|
||||
ALvoid *data;
|
||||
|
||||
ALsizei Frequency;
|
||||
ALenum Format;
|
||||
ALsizei SampleLen;
|
||||
|
||||
enum FmtChannels FmtChannels;
|
||||
enum FmtType FmtType;
|
||||
|
||||
enum UserFmtChannels OriginalChannels;
|
||||
enum UserFmtType OriginalType;
|
||||
ALsizei OriginalSize;
|
||||
ALsizei OriginalAlign;
|
||||
|
||||
ALsizei LoopStart;
|
||||
ALsizei LoopEnd;
|
||||
|
||||
ALsizei UnpackAlign;
|
||||
ALsizei PackAlign;
|
||||
|
||||
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
|
||||
RefCount ref;
|
||||
|
||||
RWLock lock;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALbuffer;
|
||||
|
||||
ALvoid ALAPIENTRY alBufferSubDataEXT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length);
|
||||
ALbuffer *NewBuffer(ALCcontext *context);
|
||||
void DeleteBuffer(ALCdevice *device, ALbuffer *buffer);
|
||||
|
||||
ALenum LoadData(ALbuffer *buffer, ALuint freq, ALenum NewFormat, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALboolean storesrc);
|
||||
|
||||
inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALbuffer*)LookupUIntMapKey(&device->BufferMap, id); }
|
||||
inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALbuffer*)RemoveUIntMapKey(&device->BufferMap, id); }
|
||||
|
||||
ALvoid ReleaseALBuffers(ALCdevice *device);
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#ifndef _AL_DATABUFFER_H_
|
||||
#define _AL_DATABUFFER_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define UNMAPPED 0
|
||||
#define MAPPED 1
|
||||
|
||||
typedef struct ALdatabuffer
|
||||
{
|
||||
ALubyte *data;
|
||||
ALuint size;
|
||||
ALenum state;
|
||||
|
||||
ALenum usage;
|
||||
|
||||
/* Index to self */
|
||||
ALuint databuffer;
|
||||
|
||||
struct ALdatabuffer *next;
|
||||
} ALdatabuffer;
|
||||
|
||||
ALvoid ALAPIENTRY alGenDatabuffersEXT(ALsizei n,ALuint *puiBuffers);
|
||||
ALvoid ALAPIENTRY alDeleteDatabuffersEXT(ALsizei n, const ALuint *puiBuffers);
|
||||
ALboolean ALAPIENTRY alIsDatabufferEXT(ALuint uiBuffer);
|
||||
|
||||
ALvoid ALAPIENTRY alDatabufferDataEXT(ALuint buffer,const ALvoid *data,ALsizei size,ALenum usage);
|
||||
ALvoid ALAPIENTRY alDatabufferSubDataEXT(ALuint buffer, ALuint start, ALsizei length, const ALvoid *data);
|
||||
ALvoid ALAPIENTRY alGetDatabufferSubDataEXT(ALuint buffer, ALuint start, ALsizei length, ALvoid *data);
|
||||
|
||||
ALvoid ALAPIENTRY alDatabufferfEXT(ALuint buffer, ALenum eParam, ALfloat flValue);
|
||||
ALvoid ALAPIENTRY alDatabufferfvEXT(ALuint buffer, ALenum eParam, const ALfloat* flValues);
|
||||
ALvoid ALAPIENTRY alDatabufferiEXT(ALuint buffer, ALenum eParam, ALint lValue);
|
||||
ALvoid ALAPIENTRY alDatabufferivEXT(ALuint buffer, ALenum eParam, const ALint* plValues);
|
||||
ALvoid ALAPIENTRY alGetDatabufferfEXT(ALuint buffer, ALenum eParam, ALfloat *pflValue);
|
||||
ALvoid ALAPIENTRY alGetDatabufferfvEXT(ALuint buffer, ALenum eParam, ALfloat* pflValues);
|
||||
ALvoid ALAPIENTRY alGetDatabufferiEXT(ALuint buffer, ALenum eParam, ALint *plValue);
|
||||
ALvoid ALAPIENTRY alGetDatabufferivEXT(ALuint buffer, ALenum eParam, ALint* plValues);
|
||||
|
||||
ALvoid ALAPIENTRY alSelectDatabufferEXT(ALenum target, ALuint uiBuffer);
|
||||
|
||||
ALvoid* ALAPIENTRY alMapDatabufferEXT(ALuint uiBuffer, ALuint start, ALsizei length, ALenum access);
|
||||
ALvoid ALAPIENTRY alUnmapDatabufferEXT(ALuint uiBuffer);
|
||||
|
||||
ALvoid ReleaseALDatabuffers(ALCdevice *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+127
-204
@@ -1,211 +1,69 @@
|
||||
// NOTE: The effect structure is getting too large, it may be a good idea to
|
||||
// start using a union or another form of unified storage.
|
||||
#ifndef _AL_EFFECT_H_
|
||||
#define _AL_EFFECT_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define AL_EFFECT_TYPE 0x8001
|
||||
|
||||
#define AL_EFFECT_NULL 0x0000
|
||||
#define AL_EFFECT_REVERB 0x0001
|
||||
#define AL_EFFECT_CHORUS 0x0002
|
||||
#define AL_EFFECT_DISTORTION 0x0003
|
||||
#define AL_EFFECT_ECHO 0x0004
|
||||
#define AL_EFFECT_FLANGER 0x0005
|
||||
#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006
|
||||
#define AL_EFFECT_VOCAL_MORPHER 0x0007
|
||||
#define AL_EFFECT_PITCH_SHIFTER 0x0008
|
||||
#define AL_EFFECT_RING_MODULATOR 0x0009
|
||||
#define AL_EFFECT_AUTOWAH 0x000A
|
||||
#define AL_EFFECT_COMPRESSOR 0x000B
|
||||
#define AL_EFFECT_EQUALIZER 0x000C
|
||||
#define AL_EFFECT_EAXREVERB 0x8000
|
||||
|
||||
#define AL_REVERB_DENSITY 0x0001
|
||||
#define AL_REVERB_DIFFUSION 0x0002
|
||||
#define AL_REVERB_GAIN 0x0003
|
||||
#define AL_REVERB_GAINHF 0x0004
|
||||
#define AL_REVERB_DECAY_TIME 0x0005
|
||||
#define AL_REVERB_DECAY_HFRATIO 0x0006
|
||||
#define AL_REVERB_REFLECTIONS_GAIN 0x0007
|
||||
#define AL_REVERB_REFLECTIONS_DELAY 0x0008
|
||||
#define AL_REVERB_LATE_REVERB_GAIN 0x0009
|
||||
#define AL_REVERB_LATE_REVERB_DELAY 0x000A
|
||||
#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B
|
||||
#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C
|
||||
#define AL_REVERB_DECAY_HFLIMIT 0x000D
|
||||
|
||||
#define AL_REVERB_MIN_DENSITY (0.0f)
|
||||
#define AL_REVERB_MAX_DENSITY (1.0f)
|
||||
#define AL_REVERB_DEFAULT_DENSITY (1.0f)
|
||||
#define AL_REVERB_MIN_DIFFUSION (0.0f)
|
||||
#define AL_REVERB_MAX_DIFFUSION (1.0f)
|
||||
#define AL_REVERB_DEFAULT_DIFFUSION (1.0f)
|
||||
#define AL_REVERB_MIN_GAIN (0.0f)
|
||||
#define AL_REVERB_MAX_GAIN (1.0f)
|
||||
#define AL_REVERB_DEFAULT_GAIN (0.32f)
|
||||
#define AL_REVERB_MIN_GAINHF (0.0f)
|
||||
#define AL_REVERB_MAX_GAINHF (1.0f)
|
||||
#define AL_REVERB_DEFAULT_GAINHF (0.89f)
|
||||
#define AL_REVERB_MIN_DECAY_TIME (0.1f)
|
||||
#define AL_REVERB_MAX_DECAY_TIME (20.0f)
|
||||
#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f)
|
||||
#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f)
|
||||
#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f)
|
||||
#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f)
|
||||
#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f)
|
||||
#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f)
|
||||
#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
|
||||
#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f)
|
||||
#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f)
|
||||
#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
|
||||
#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f)
|
||||
#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f)
|
||||
#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
|
||||
#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f)
|
||||
#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f)
|
||||
#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
|
||||
#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
|
||||
#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
|
||||
#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
|
||||
#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
|
||||
#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
|
||||
#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
|
||||
#define AL_REVERB_MIN_DECAY_HFLIMIT (AL_FALSE)
|
||||
#define AL_REVERB_MAX_DECAY_HFLIMIT (AL_TRUE)
|
||||
#define AL_REVERB_DEFAULT_DECAY_HFLIMIT (AL_TRUE)
|
||||
|
||||
#define AL_ECHO_DELAY 0x0001
|
||||
#define AL_ECHO_LRDELAY 0x0002
|
||||
#define AL_ECHO_DAMPING 0x0003
|
||||
#define AL_ECHO_FEEDBACK 0x0004
|
||||
#define AL_ECHO_SPREAD 0x0005
|
||||
|
||||
#define AL_ECHO_MIN_DELAY (0.0f)
|
||||
#define AL_ECHO_MAX_DELAY (0.207f)
|
||||
#define AL_ECHO_DEFAULT_DELAY (0.1f)
|
||||
#define AL_ECHO_MIN_LRDELAY (0.0f)
|
||||
#define AL_ECHO_MAX_LRDELAY (0.404f)
|
||||
#define AL_ECHO_DEFAULT_LRDELAY (0.1f)
|
||||
#define AL_ECHO_MIN_DAMPING (0.0f)
|
||||
#define AL_ECHO_MAX_DAMPING (0.99f)
|
||||
#define AL_ECHO_DEFAULT_DAMPING (0.5f)
|
||||
#define AL_ECHO_MIN_FEEDBACK (0.0f)
|
||||
#define AL_ECHO_MAX_FEEDBACK (1.0f)
|
||||
#define AL_ECHO_DEFAULT_FEEDBACK (0.5f)
|
||||
#define AL_ECHO_MIN_SPREAD (-1.0f)
|
||||
#define AL_ECHO_MAX_SPREAD (1.0f)
|
||||
#define AL_ECHO_DEFAULT_SPREAD (-1.0f)
|
||||
|
||||
#define AL_EAXREVERB_DENSITY 0x0001
|
||||
#define AL_EAXREVERB_DIFFUSION 0x0002
|
||||
#define AL_EAXREVERB_GAIN 0x0003
|
||||
#define AL_EAXREVERB_GAINHF 0x0004
|
||||
#define AL_EAXREVERB_GAINLF 0x0005
|
||||
#define AL_EAXREVERB_DECAY_TIME 0x0006
|
||||
#define AL_EAXREVERB_DECAY_HFRATIO 0x0007
|
||||
#define AL_EAXREVERB_DECAY_LFRATIO 0x0008
|
||||
#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009
|
||||
#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A
|
||||
#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B
|
||||
#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C
|
||||
#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D
|
||||
#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E
|
||||
#define AL_EAXREVERB_ECHO_TIME 0x000F
|
||||
#define AL_EAXREVERB_ECHO_DEPTH 0x0010
|
||||
#define AL_EAXREVERB_MODULATION_TIME 0x0011
|
||||
#define AL_EAXREVERB_MODULATION_DEPTH 0x0012
|
||||
#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013
|
||||
#define AL_EAXREVERB_HFREFERENCE 0x0014
|
||||
#define AL_EAXREVERB_LFREFERENCE 0x0015
|
||||
#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016
|
||||
#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017
|
||||
|
||||
#define AL_EAXREVERB_MIN_DENSITY (0.0f)
|
||||
#define AL_EAXREVERB_MAX_DENSITY (1.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f)
|
||||
#define AL_EAXREVERB_MIN_DIFFUSION (0.0f)
|
||||
#define AL_EAXREVERB_MAX_DIFFUSION (1.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f)
|
||||
#define AL_EAXREVERB_MIN_GAIN (0.0f)
|
||||
#define AL_EAXREVERB_MAX_GAIN (1.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_GAIN (0.32f)
|
||||
#define AL_EAXREVERB_MIN_GAINHF (0.0f)
|
||||
#define AL_EAXREVERB_MAX_GAINHF (1.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f)
|
||||
#define AL_EAXREVERB_MIN_GAINLF (0.0f)
|
||||
#define AL_EAXREVERB_MAX_GAINLF (1.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f)
|
||||
#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f)
|
||||
#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f)
|
||||
#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f)
|
||||
#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f)
|
||||
#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f)
|
||||
#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f)
|
||||
#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f)
|
||||
#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f)
|
||||
#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
|
||||
#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f)
|
||||
#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f)
|
||||
#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
|
||||
#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)
|
||||
#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f)
|
||||
#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
|
||||
#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f)
|
||||
#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f)
|
||||
#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
|
||||
#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)
|
||||
#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f)
|
||||
#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f)
|
||||
#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f)
|
||||
#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f)
|
||||
#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f)
|
||||
#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f)
|
||||
#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f)
|
||||
#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f)
|
||||
#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f)
|
||||
#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
|
||||
#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
|
||||
#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f)
|
||||
#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f)
|
||||
#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f)
|
||||
#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f)
|
||||
#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
|
||||
#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
|
||||
#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
|
||||
#define AL_EAXREVERB_MIN_DECAY_HFLIMIT (AL_FALSE)
|
||||
#define AL_EAXREVERB_MAX_DECAY_HFLIMIT (AL_TRUE)
|
||||
#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT (AL_TRUE)
|
||||
struct ALeffect;
|
||||
|
||||
enum {
|
||||
EAXREVERB = 0,
|
||||
REVERB,
|
||||
AUTOWAH,
|
||||
CHORUS,
|
||||
COMPRESSOR,
|
||||
DISTORTION,
|
||||
ECHO,
|
||||
EQUALIZER,
|
||||
FLANGER,
|
||||
MODULATOR,
|
||||
DEDICATED,
|
||||
|
||||
MAX_EFFECTS
|
||||
};
|
||||
extern ALboolean DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
typedef struct ALeffect
|
||||
{
|
||||
// Effect type (AL_EFFECT_NULL, ...)
|
||||
ALenum type;
|
||||
extern ALfloat ReverbBoost;
|
||||
extern ALboolean EmulateEAXReverb;
|
||||
|
||||
struct ALeffectVtable {
|
||||
void (*const setParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*const setParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*const setParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*const setParamfv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
|
||||
void (*const getParami)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*const getParamiv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*const getParamf)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*const getParamfv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
};
|
||||
|
||||
#define DEFINE_ALEFFECT_VTABLE(T) \
|
||||
const struct ALeffectVtable T##_vtable = { \
|
||||
T##_setParami, T##_setParamiv, \
|
||||
T##_setParamf, T##_setParamfv, \
|
||||
T##_getParami, T##_getParamiv, \
|
||||
T##_getParamf, T##_getParamfv, \
|
||||
}
|
||||
|
||||
extern const struct ALeffectVtable ALeaxreverb_vtable;
|
||||
extern const struct ALeffectVtable ALreverb_vtable;
|
||||
extern const struct ALeffectVtable ALautowah_vtable;
|
||||
extern const struct ALeffectVtable ALchorus_vtable;
|
||||
extern const struct ALeffectVtable ALcompressor_vtable;
|
||||
extern const struct ALeffectVtable ALdistortion_vtable;
|
||||
extern const struct ALeffectVtable ALecho_vtable;
|
||||
extern const struct ALeffectVtable ALequalizer_vtable;
|
||||
extern const struct ALeffectVtable ALflanger_vtable;
|
||||
extern const struct ALeffectVtable ALmodulator_vtable;
|
||||
extern const struct ALeffectVtable ALnull_vtable;
|
||||
extern const struct ALeffectVtable ALdedicated_vtable;
|
||||
|
||||
|
||||
typedef union ALeffectProps {
|
||||
struct {
|
||||
// Shared Reverb Properties
|
||||
ALfloat Density;
|
||||
@@ -235,6 +93,34 @@ typedef struct ALeffect
|
||||
ALfloat LFReference;
|
||||
} Reverb;
|
||||
|
||||
struct {
|
||||
ALfloat AttackTime;
|
||||
ALfloat ReleaseTime;
|
||||
ALfloat PeakGain;
|
||||
ALfloat Resonance;
|
||||
} Autowah;
|
||||
|
||||
struct {
|
||||
ALint Waveform;
|
||||
ALint Phase;
|
||||
ALfloat Rate;
|
||||
ALfloat Depth;
|
||||
ALfloat Feedback;
|
||||
ALfloat Delay;
|
||||
} Chorus;
|
||||
|
||||
struct {
|
||||
ALboolean OnOff;
|
||||
} Compressor;
|
||||
|
||||
struct {
|
||||
ALfloat Edge;
|
||||
ALfloat Gain;
|
||||
ALfloat LowpassCutoff;
|
||||
ALfloat EQCenter;
|
||||
ALfloat EQBandwidth;
|
||||
} Distortion;
|
||||
|
||||
struct {
|
||||
ALfloat Delay;
|
||||
ALfloat LRDelay;
|
||||
@@ -245,28 +131,65 @@ typedef struct ALeffect
|
||||
ALfloat Spread;
|
||||
} Echo;
|
||||
|
||||
// Index to itself
|
||||
ALuint effect;
|
||||
struct {
|
||||
ALfloat Delay;
|
||||
ALfloat LowCutoff;
|
||||
ALfloat LowGain;
|
||||
ALfloat Mid1Center;
|
||||
ALfloat Mid1Gain;
|
||||
ALfloat Mid1Width;
|
||||
ALfloat Mid2Center;
|
||||
ALfloat Mid2Gain;
|
||||
ALfloat Mid2Width;
|
||||
ALfloat HighCutoff;
|
||||
ALfloat HighGain;
|
||||
} Equalizer;
|
||||
|
||||
struct ALeffect *next;
|
||||
struct {
|
||||
ALint Waveform;
|
||||
ALint Phase;
|
||||
ALfloat Rate;
|
||||
ALfloat Depth;
|
||||
ALfloat Feedback;
|
||||
ALfloat Delay;
|
||||
} Flanger;
|
||||
|
||||
struct {
|
||||
ALfloat Frequency;
|
||||
ALfloat HighPassCutoff;
|
||||
ALint Waveform;
|
||||
} Modulator;
|
||||
|
||||
struct {
|
||||
ALfloat Gain;
|
||||
} Dedicated;
|
||||
} ALeffectProps;
|
||||
|
||||
typedef struct ALeffect {
|
||||
// Effect type (AL_EFFECT_NULL, ...)
|
||||
ALenum type;
|
||||
|
||||
ALeffectProps Props;
|
||||
|
||||
const struct ALeffectVtable *vtbl;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALeffect;
|
||||
|
||||
ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects);
|
||||
ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, ALuint *effects);
|
||||
ALboolean AL_APIENTRY alIsEffect(ALuint effect);
|
||||
inline struct ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALeffect*)LookupUIntMapKey(&device->EffectMap, id); }
|
||||
inline struct ALeffect *RemoveEffect(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALeffect*)RemoveUIntMapKey(&device->EffectMap, id); }
|
||||
|
||||
ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue);
|
||||
ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, ALint *piValues);
|
||||
ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue);
|
||||
ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
|
||||
|
||||
ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue);
|
||||
ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues);
|
||||
ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue);
|
||||
ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
|
||||
inline ALboolean IsReverbEffect(ALenum type)
|
||||
{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; }
|
||||
|
||||
ALenum InitEffect(ALeffect *effect);
|
||||
ALvoid ReleaseALEffects(ALCdevice *device);
|
||||
|
||||
ALvoid LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
#ifndef _AL_ERROR_H_
|
||||
#define _AL_ERROR_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
ALvoid alSetError(ALenum errorCode);
|
||||
extern ALboolean TrapALError;
|
||||
|
||||
ALvoid alSetError(ALCcontext *Context, ALenum errorCode);
|
||||
|
||||
#define SET_ERROR_AND_RETURN(ctx, err) do { \
|
||||
alSetError((ctx), (err)); \
|
||||
return; \
|
||||
} while(0)
|
||||
|
||||
#define SET_ERROR_AND_RETURN_VALUE(ctx, err, val) do { \
|
||||
alSetError((ctx), (err)); \
|
||||
return (val); \
|
||||
} while(0)
|
||||
|
||||
#define SET_ERROR_AND_GOTO(ctx, err, lbl) do { \
|
||||
alSetError((ctx), (err)); \
|
||||
goto lbl; \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#ifndef _AL_EXTENSION_H_
|
||||
#define _AL_EXTENSION_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ALextension_struct
|
||||
{
|
||||
ALchar *extName;
|
||||
ALvoid *address;
|
||||
} ALextension;
|
||||
|
||||
typedef struct ALfunction_struct
|
||||
{
|
||||
ALchar *funcName;
|
||||
ALvoid *address;
|
||||
} ALfunction;
|
||||
|
||||
typedef struct ALenum_struct
|
||||
{
|
||||
ALchar *enumName;
|
||||
ALenum value;
|
||||
} ALenums;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+77
-63
@@ -1,93 +1,107 @@
|
||||
#ifndef _AL_FILTER_H_
|
||||
#define _AL_FILTER_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alu.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ALfloat coeff;
|
||||
#ifndef _MSC_VER
|
||||
ALfloat history[0];
|
||||
#else
|
||||
ALfloat history[1];
|
||||
#endif
|
||||
} FILTER;
|
||||
#define LOWPASSFREQREF (5000.0f)
|
||||
#define HIGHPASSFREQREF (250.0f)
|
||||
|
||||
static __inline ALfloat lpFilter4P(FILTER *iir, ALuint offset, ALfloat input)
|
||||
|
||||
/* Filters implementation is based on the "Cookbook formulae for audio *
|
||||
* EQ biquad filter coefficients" by Robert Bristow-Johnson *
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
|
||||
|
||||
typedef enum ALfilterType {
|
||||
/** EFX-style low-pass filter, specifying a gain and reference frequency. */
|
||||
ALfilterType_HighShelf,
|
||||
/** EFX-style high-pass filter, specifying a gain and reference frequency. */
|
||||
ALfilterType_LowShelf,
|
||||
/** Peaking filter, specifying a gain, reference frequency, and bandwidth. */
|
||||
ALfilterType_Peaking,
|
||||
|
||||
/** Low-pass cut-off filter, specifying a cut-off frequency and bandwidth. */
|
||||
ALfilterType_LowPass,
|
||||
/** High-pass cut-off filter, specifying a cut-off frequency and bandwidth. */
|
||||
ALfilterType_HighPass,
|
||||
/** Band-pass filter, specifying a center frequency and bandwidth. */
|
||||
ALfilterType_BandPass,
|
||||
} ALfilterType;
|
||||
|
||||
typedef struct ALfilterState {
|
||||
ALfloat x[2]; /* History of two last input samples */
|
||||
ALfloat y[2]; /* History of two last output samples */
|
||||
ALfloat a[3]; /* Transfer function coefficients "a" */
|
||||
ALfloat b[3]; /* Transfer function coefficients "b" */
|
||||
|
||||
void (*process)(struct ALfilterState *self, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
|
||||
} ALfilterState;
|
||||
#define ALfilterState_process(a, ...) ((a)->process((a), __VA_ARGS__))
|
||||
|
||||
void ALfilterState_clear(ALfilterState *filter);
|
||||
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth);
|
||||
|
||||
inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample)
|
||||
{
|
||||
ALfloat *history = &iir->history[offset];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
ALfloat outsmp;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
history[0] = output;
|
||||
output = output + (history[1]-output)*a;
|
||||
history[1] = output;
|
||||
output = output + (history[2]-output)*a;
|
||||
history[2] = output;
|
||||
output = output + (history[3]-output)*a;
|
||||
history[3] = output;
|
||||
outsmp = filter->b[0] * sample +
|
||||
filter->b[1] * filter->x[0] +
|
||||
filter->b[2] * filter->x[1] -
|
||||
filter->a[1] * filter->y[0] -
|
||||
filter->a[2] * filter->y[1];
|
||||
filter->x[1] = filter->x[0];
|
||||
filter->x[0] = sample;
|
||||
filter->y[1] = filter->y[0];
|
||||
filter->y[0] = outsmp;
|
||||
|
||||
return output;
|
||||
return outsmp;
|
||||
}
|
||||
|
||||
static __inline ALfloat lpFilter2P(FILTER *iir, ALuint offset, ALfloat input)
|
||||
{
|
||||
ALfloat *history = &iir->history[offset];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
history[0] = output;
|
||||
output = output + (history[1]-output)*a;
|
||||
history[1] = output;
|
||||
|
||||
return output;
|
||||
}
|
||||
void ALfilterState_processC(ALfilterState *filter, ALfloat *restrict dst, const ALfloat *src, ALuint numsamples);
|
||||
|
||||
|
||||
#define AL_FILTER_TYPE 0x8001
|
||||
|
||||
#define AL_FILTER_NULL 0x0000
|
||||
#define AL_FILTER_LOWPASS 0x0001
|
||||
#define AL_FILTER_HIGHPASS 0x0002
|
||||
#define AL_FILTER_BANDPASS 0x0003
|
||||
|
||||
#define AL_LOWPASS_GAIN 0x0001
|
||||
#define AL_LOWPASS_GAINHF 0x0002
|
||||
|
||||
|
||||
typedef struct ALfilter
|
||||
{
|
||||
typedef struct ALfilter {
|
||||
// Filter type (AL_FILTER_NULL, ...)
|
||||
ALenum type;
|
||||
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
|
||||
// Index to itself
|
||||
ALuint filter;
|
||||
void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
|
||||
struct ALfilter *next;
|
||||
void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALfilter;
|
||||
|
||||
ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters);
|
||||
ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, ALuint *filters);
|
||||
ALboolean AL_APIENTRY alIsFilter(ALuint filter);
|
||||
#define ALfilter_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v)))
|
||||
|
||||
ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue);
|
||||
ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, ALint *piValues);
|
||||
ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue);
|
||||
ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
|
||||
#define ALfilter_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
|
||||
|
||||
ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue);
|
||||
ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues);
|
||||
ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue);
|
||||
ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
|
||||
inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfilter*)LookupUIntMapKey(&device->FilterMap, id); }
|
||||
inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfilter*)RemoveUIntMapKey(&device->FilterMap, id); }
|
||||
|
||||
ALvoid ReleaseALFilters(ALCdevice *device);
|
||||
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
#ifndef _AL_LISTENER_H_
|
||||
#define _AL_LISTENER_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define AL_METERS_PER_UNIT 0x20004
|
||||
typedef struct ALlistener {
|
||||
volatile ALfloat Position[3];
|
||||
volatile ALfloat Velocity[3];
|
||||
volatile ALfloat Forward[3];
|
||||
volatile ALfloat Up[3];
|
||||
volatile ALfloat Gain;
|
||||
volatile ALfloat MetersPerUnit;
|
||||
|
||||
typedef struct ALlistener_struct
|
||||
{
|
||||
ALfloat Position[3];
|
||||
ALfloat Velocity[3];
|
||||
ALfloat Forward[3];
|
||||
ALfloat Up[3];
|
||||
ALfloat Gain;
|
||||
ALfloat MetersPerUnit;
|
||||
struct {
|
||||
ALfloat Matrix[4][4];
|
||||
ALfloat Velocity[3];
|
||||
} Params;
|
||||
} ALlistener;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
+787
-231
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,172 @@
|
||||
#ifndef ALMIDI_H
|
||||
#define ALMIDI_H
|
||||
|
||||
#include "alMain.h"
|
||||
#include "atomic.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ALsfmodulator {
|
||||
struct {
|
||||
ALenum Input;
|
||||
ALenum Type;
|
||||
ALenum Form;
|
||||
} Source[2];
|
||||
ALint Amount;
|
||||
ALenum TransformOp;
|
||||
ALenum Dest;
|
||||
} ALsfmodulator;
|
||||
|
||||
typedef struct ALenvelope {
|
||||
ALint DelayTime;
|
||||
ALint AttackTime;
|
||||
ALint HoldTime;
|
||||
ALint DecayTime;
|
||||
ALint SustainAttn;
|
||||
ALint ReleaseTime;
|
||||
ALint KeyToHoldTime;
|
||||
ALint KeyToDecayTime;
|
||||
} ALenvelope;
|
||||
|
||||
|
||||
typedef struct ALfontsound {
|
||||
RefCount ref;
|
||||
|
||||
struct ALbuffer *Buffer;
|
||||
|
||||
ALint MinKey, MaxKey;
|
||||
ALint MinVelocity, MaxVelocity;
|
||||
|
||||
ALint ModLfoToPitch;
|
||||
ALint VibratoLfoToPitch;
|
||||
ALint ModEnvToPitch;
|
||||
|
||||
ALint FilterCutoff;
|
||||
ALint FilterQ;
|
||||
ALint ModLfoToFilterCutoff;
|
||||
ALint ModEnvToFilterCutoff;
|
||||
ALint ModLfoToVolume;
|
||||
|
||||
ALint ChorusSend;
|
||||
ALint ReverbSend;
|
||||
|
||||
ALint Pan;
|
||||
|
||||
struct {
|
||||
ALint Delay;
|
||||
ALint Frequency;
|
||||
} ModLfo;
|
||||
struct {
|
||||
ALint Delay;
|
||||
ALint Frequency;
|
||||
} VibratoLfo;
|
||||
|
||||
ALenvelope ModEnv;
|
||||
ALenvelope VolEnv;
|
||||
|
||||
ALint Attenuation;
|
||||
|
||||
ALint CoarseTuning;
|
||||
ALint FineTuning;
|
||||
|
||||
ALenum LoopMode;
|
||||
|
||||
ALint TuningScale;
|
||||
|
||||
ALint ExclusiveClass;
|
||||
|
||||
ALuint Start;
|
||||
ALuint End;
|
||||
ALuint LoopStart;
|
||||
ALuint LoopEnd;
|
||||
ALuint SampleRate;
|
||||
ALubyte PitchKey;
|
||||
ALbyte PitchCorrection;
|
||||
ALenum SampleType;
|
||||
struct ALfontsound *Link;
|
||||
|
||||
/* NOTE: Each map entry contains *four* (4) ALsfmodulator objects. */
|
||||
UIntMap ModulatorMap;
|
||||
|
||||
ALuint id;
|
||||
} ALfontsound;
|
||||
|
||||
void ALfontsound_setPropi(ALfontsound *self, ALCcontext *context, ALenum param, ALint value);
|
||||
void ALfontsound_setModStagei(ALfontsound *self, ALCcontext *context, ALsizei stage, ALenum param, ALint value);
|
||||
|
||||
ALfontsound *NewFontsound(ALCcontext *context);
|
||||
void DeleteFontsound(ALCdevice *device, ALfontsound *sound);
|
||||
|
||||
inline struct ALfontsound *LookupFontsound(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfontsound*)LookupUIntMapKey(&device->FontsoundMap, id); }
|
||||
inline struct ALfontsound *RemoveFontsound(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALfontsound*)RemoveUIntMapKey(&device->FontsoundMap, id); }
|
||||
|
||||
void ReleaseALFontsounds(ALCdevice *device);
|
||||
|
||||
|
||||
typedef struct ALsfpreset {
|
||||
RefCount ref;
|
||||
|
||||
ALint Preset; /* a.k.a. MIDI program number */
|
||||
ALint Bank; /* MIDI bank 0...127, or percussion (bank 128) */
|
||||
|
||||
ALfontsound **Sounds;
|
||||
ALsizei NumSounds;
|
||||
|
||||
ALuint id;
|
||||
} ALsfpreset;
|
||||
|
||||
ALsfpreset *NewPreset(ALCcontext *context);
|
||||
void DeletePreset(ALCdevice *device, ALsfpreset *preset);
|
||||
|
||||
inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALsfpreset*)LookupUIntMapKey(&device->PresetMap, id); }
|
||||
inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALsfpreset*)RemoveUIntMapKey(&device->PresetMap, id); }
|
||||
|
||||
void ReleaseALPresets(ALCdevice *device);
|
||||
|
||||
|
||||
typedef struct ALsoundfont {
|
||||
RefCount ref;
|
||||
|
||||
ALsfpreset **Presets;
|
||||
ALsizei NumPresets;
|
||||
|
||||
RWLock Lock;
|
||||
|
||||
ALuint id;
|
||||
} ALsoundfont;
|
||||
|
||||
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
|
||||
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
|
||||
|
||||
inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALsoundfont*)LookupUIntMapKey(&device->SfontMap, id); }
|
||||
inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id)
|
||||
{ return (struct ALsoundfont*)RemoveUIntMapKey(&device->SfontMap, id); }
|
||||
|
||||
void ReleaseALSoundfonts(ALCdevice *device);
|
||||
|
||||
|
||||
inline ALboolean IsValidCtrlInput(int cc)
|
||||
{
|
||||
/* These correspond to MIDI functions, not real controller values. */
|
||||
if(cc == 0 || cc == 6 || cc == 32 || cc == 38 || (cc >= 98 && cc <= 101) || cc >= 120)
|
||||
return AL_FALSE;
|
||||
/* These are the LSB components of CC0...CC31, which are automatically used when
|
||||
* reading the MSB controller value. */
|
||||
if(cc >= 32 && cc <= 63)
|
||||
return AL_FALSE;
|
||||
/* All the rest are okay! */
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ALMIDI_H */
|
||||
+116
-79
@@ -1,106 +1,143 @@
|
||||
#ifndef _AL_SOURCE_H_
|
||||
#define _AL_SOURCE_H_
|
||||
|
||||
#define AL_NUM_SOURCE_PARAMS 128
|
||||
#define MAX_SENDS 4
|
||||
|
||||
#define MAX_SENDS 2
|
||||
|
||||
#include "alFilter.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "AL/al.h"
|
||||
|
||||
#define AL_DIRECT_FILTER 0x20005
|
||||
#define AL_AUXILIARY_SEND_FILTER 0x20006
|
||||
#define AL_AIR_ABSORPTION_FACTOR 0x20007
|
||||
#define AL_ROOM_ROLLOFF_FACTOR 0x20008
|
||||
#define AL_CONE_OUTER_GAINHF 0x20009
|
||||
#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A
|
||||
#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B
|
||||
#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C
|
||||
#include "hrtf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ALbufferlistitem
|
||||
{
|
||||
ALuint buffer;
|
||||
ALuint bufferstate;
|
||||
ALuint flag;
|
||||
struct ALbufferlistitem *next;
|
||||
extern enum Resampler DefaultResampler;
|
||||
|
||||
extern const ALsizei ResamplerPadding[ResamplerMax];
|
||||
extern const ALsizei ResamplerPrePadding[ResamplerMax];
|
||||
|
||||
|
||||
typedef struct ALbufferlistitem {
|
||||
struct ALbuffer *buffer;
|
||||
struct ALbufferlistitem *volatile next;
|
||||
struct ALbufferlistitem *volatile prev;
|
||||
} ALbufferlistitem;
|
||||
|
||||
typedef struct ALsource
|
||||
{
|
||||
ALfloat flPitch;
|
||||
ALfloat flGain;
|
||||
ALfloat flOuterGain;
|
||||
ALfloat flMinGain;
|
||||
ALfloat flMaxGain;
|
||||
ALfloat flInnerAngle;
|
||||
ALfloat flOuterAngle;
|
||||
ALfloat flRefDistance;
|
||||
ALfloat flMaxDistance;
|
||||
ALfloat flRollOffFactor;
|
||||
ALfloat vPosition[3];
|
||||
ALfloat vVelocity[3];
|
||||
ALfloat vOrientation[3];
|
||||
ALboolean bHeadRelative;
|
||||
ALboolean bLooping;
|
||||
ALenum DistanceModel;
|
||||
|
||||
ALuint ulBufferID;
|
||||
typedef struct ALactivesource {
|
||||
struct ALsource *Source;
|
||||
|
||||
ALboolean inuse;
|
||||
ALboolean play;
|
||||
ALenum state;
|
||||
ALuint position;
|
||||
ALuint position_fraction;
|
||||
/** Method to update mixing parameters. */
|
||||
ALvoid (*Update)(struct ALactivesource *self, const ALCcontext *context);
|
||||
|
||||
struct ALbufferlistitem *queue; // Linked list of buffers in queue
|
||||
ALuint BuffersInQueue; // Number of buffers in queue
|
||||
ALuint BuffersPlayed; // Number of buffers played on this loop
|
||||
/** Current target parameters used for mixing. */
|
||||
ALint Step;
|
||||
|
||||
ALfilter DirectFilter;
|
||||
ALboolean IsHrtf;
|
||||
|
||||
ALuint Offset; /* Number of output samples mixed since starting. */
|
||||
|
||||
DirectParams Direct;
|
||||
SendParams Send[MAX_SENDS];
|
||||
} ALactivesource;
|
||||
|
||||
|
||||
typedef struct ALsource {
|
||||
/** Source properties. */
|
||||
volatile ALfloat Pitch;
|
||||
volatile ALfloat Gain;
|
||||
volatile ALfloat OuterGain;
|
||||
volatile ALfloat MinGain;
|
||||
volatile ALfloat MaxGain;
|
||||
volatile ALfloat InnerAngle;
|
||||
volatile ALfloat OuterAngle;
|
||||
volatile ALfloat RefDistance;
|
||||
volatile ALfloat MaxDistance;
|
||||
volatile ALfloat RollOffFactor;
|
||||
volatile ALfloat Position[3];
|
||||
volatile ALfloat Velocity[3];
|
||||
volatile ALfloat Orientation[3];
|
||||
volatile ALboolean HeadRelative;
|
||||
volatile ALboolean Looping;
|
||||
volatile enum DistanceModel DistanceModel;
|
||||
volatile ALboolean DirectChannels;
|
||||
|
||||
volatile ALboolean DryGainHFAuto;
|
||||
volatile ALboolean WetGainAuto;
|
||||
volatile ALboolean WetGainHFAuto;
|
||||
volatile ALfloat OuterGainHF;
|
||||
|
||||
volatile ALfloat AirAbsorptionFactor;
|
||||
volatile ALfloat RoomRolloffFactor;
|
||||
volatile ALfloat DopplerFactor;
|
||||
|
||||
volatile ALfloat Radius;
|
||||
|
||||
enum Resampler Resampler;
|
||||
|
||||
/**
|
||||
* Last user-specified offset, and the offset type (bytes, samples, or
|
||||
* seconds).
|
||||
*/
|
||||
ALdouble Offset;
|
||||
ALenum OffsetType;
|
||||
|
||||
/** Source type (static, streaming, or undetermined) */
|
||||
volatile ALint SourceType;
|
||||
|
||||
/** Source state (initial, playing, paused, or stopped) */
|
||||
volatile ALenum state;
|
||||
ALenum new_state;
|
||||
|
||||
/**
|
||||
* Source offset in samples, relative to the currently playing buffer, NOT
|
||||
* the whole queue, and the fractional (fixed-point) offset to the next
|
||||
* sample.
|
||||
*/
|
||||
ALuint position;
|
||||
ALuint position_fraction;
|
||||
|
||||
/** Source Buffer Queue info. */
|
||||
ATOMIC(ALbufferlistitem*) queue;
|
||||
ATOMIC(ALbufferlistitem*) current_buffer;
|
||||
RWLock queue_lock;
|
||||
|
||||
/** Current buffer sample info. */
|
||||
ALuint NumChannels;
|
||||
ALuint SampleSize;
|
||||
|
||||
/** Direct filter and auxiliary send info. */
|
||||
struct {
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
} Direct;
|
||||
struct {
|
||||
struct ALeffectslot *Slot;
|
||||
ALfilter WetFilter;
|
||||
FILTER iirFilter;
|
||||
ALfloat history[2];
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
} Send[MAX_SENDS];
|
||||
|
||||
ALboolean DryGainHFAuto;
|
||||
ALboolean WetGainAuto;
|
||||
ALboolean WetGainHFAuto;
|
||||
ALfloat OuterGainHF;
|
||||
/** Source needs to update its mixing parameters. */
|
||||
ATOMIC(ALenum) NeedsUpdate;
|
||||
|
||||
FILTER iirFilter;
|
||||
ALfloat history[OUTPUTCHANNELS*2];
|
||||
|
||||
ALfloat AirAbsorptionFactor;
|
||||
|
||||
ALfloat RoomRolloffFactor;
|
||||
|
||||
ALfloat DopplerFactor;
|
||||
|
||||
// Index to itself
|
||||
ALuint source;
|
||||
|
||||
ALint lOffset;
|
||||
ALint lOffsetType;
|
||||
|
||||
// Source Type (Static, Streaming, or Undetermined)
|
||||
ALint lSourceType;
|
||||
|
||||
// Current gains, which are ramped while mixed
|
||||
ALfloat DryGains[OUTPUTCHANNELS];
|
||||
ALfloat WetGains[MAX_SENDS];
|
||||
ALboolean FirstStart;
|
||||
|
||||
struct ALsource *next;
|
||||
/** Self ID */
|
||||
ALuint id;
|
||||
} ALsource;
|
||||
|
||||
inline struct ALsource *LookupSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)LookupUIntMapKey(&context->SourceMap, id); }
|
||||
inline struct ALsource *RemoveSource(ALCcontext *context, ALuint id)
|
||||
{ return (struct ALsource*)RemoveUIntMapKey(&context->SourceMap, id); }
|
||||
|
||||
ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state);
|
||||
ALboolean ApplyOffset(ALsource *Source);
|
||||
|
||||
ALvoid ReleaseALSources(ALCcontext *Context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef _AL_STATE_H_
|
||||
#define _AL_STATE_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,42 +1,20 @@
|
||||
#ifndef _AL_THUNK_H_
|
||||
#define _AL_THUNK_H_
|
||||
#ifndef ALTHUNK_H
|
||||
#define ALTHUNK_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void alThunkInit(void);
|
||||
void alThunkExit(void);
|
||||
ALuint alThunkAddEntry(ALvoid * ptr);
|
||||
void alThunkRemoveEntry(ALuint index);
|
||||
ALvoid *alThunkLookupEntry(ALuint index);
|
||||
|
||||
#if (SIZEOF_VOIDP > SIZEOF_UINT)
|
||||
|
||||
#define ALTHUNK_INIT() alThunkInit()
|
||||
#define ALTHUNK_EXIT() alThunkExit()
|
||||
#define ALTHUNK_ADDENTRY(p) alThunkAddEntry(p)
|
||||
#define ALTHUNK_REMOVEENTRY(i) alThunkRemoveEntry(i)
|
||||
#define ALTHUNK_LOOKUPENTRY(i) alThunkLookupEntry(i)
|
||||
|
||||
#else
|
||||
|
||||
#define ALTHUNK_INIT()
|
||||
#define ALTHUNK_EXIT()
|
||||
#define ALTHUNK_ADDENTRY(p) ((ALuint)p)
|
||||
#define ALTHUNK_REMOVEENTRY(i)
|
||||
#define ALTHUNK_LOOKUPENTRY(i) ((ALvoid*)(i))
|
||||
|
||||
#endif // (SIZEOF_VOIDP > SIZEOF_INT)
|
||||
void ThunkInit(void);
|
||||
void ThunkExit(void);
|
||||
ALenum NewThunkEntry(ALuint *index);
|
||||
void FreeThunkEntry(ALuint index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //_AL_THUNK_H_
|
||||
#endif //ALTHUNK_H
|
||||
|
||||
|
||||
+199
-120
@@ -1,154 +1,233 @@
|
||||
#ifndef _ALU_H_
|
||||
#define _ALU_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#ifdef HAVE_FLOAT_H
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846 /* pi */
|
||||
#define M_PI_2 1.57079632679489661923 /* pi/2 */
|
||||
#ifdef HAVE_IEEEFP_H
|
||||
#include <ieeefp.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SQRTF
|
||||
#define aluSqrt(x) ((ALfloat)sqrtf((float)(x)))
|
||||
#else
|
||||
#define aluSqrt(x) ((ALfloat)sqrt((double)(x)))
|
||||
#include "alMain.h"
|
||||
#include "alBuffer.h"
|
||||
#include "alFilter.h"
|
||||
|
||||
#include "hrtf.h"
|
||||
#include "align.h"
|
||||
|
||||
|
||||
#define F_PI (3.14159265358979323846f)
|
||||
#define F_PI_2 (1.57079632679489661923f)
|
||||
#define F_2PI (6.28318530717958647692f)
|
||||
|
||||
#ifndef FLT_EPSILON
|
||||
#define FLT_EPSILON (1.19209290e-07f)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ACOSF
|
||||
#define aluAcos(x) ((ALfloat)acosf((float)(x)))
|
||||
#else
|
||||
#define aluAcos(x) ((ALfloat)acos((double)(x)))
|
||||
#endif
|
||||
#define DEG2RAD(x) ((ALfloat)(x) * (F_PI/180.0f))
|
||||
#define RAD2DEG(x) ((ALfloat)(x) * (180.0f/F_PI))
|
||||
|
||||
#ifdef HAVE_ATANF
|
||||
#define aluAtan(x) ((ALfloat)atanf((float)(x)))
|
||||
#else
|
||||
#define aluAtan(x) ((ALfloat)atan((double)(x)))
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FABSF
|
||||
#define aluFabs(x) ((ALfloat)fabsf((float)(x)))
|
||||
#else
|
||||
#define aluFabs(x) ((ALfloat)fabs((double)(x)))
|
||||
#endif
|
||||
#define SRC_HISTORY_BITS (6)
|
||||
#define SRC_HISTORY_LENGTH (1<<SRC_HISTORY_BITS)
|
||||
#define SRC_HISTORY_MASK (SRC_HISTORY_LENGTH-1)
|
||||
|
||||
#define MAX_PITCH (10)
|
||||
|
||||
// fixes for mingw32.
|
||||
#if defined(max) && !defined(__max)
|
||||
#define __max max
|
||||
#endif
|
||||
#if defined(min) && !defined(__min)
|
||||
#define __min min
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
FRONT_LEFT = 0,
|
||||
FRONT_RIGHT,
|
||||
FRONT_CENTER,
|
||||
SIDE_LEFT,
|
||||
SIDE_RIGHT,
|
||||
BACK_LEFT,
|
||||
BACK_RIGHT,
|
||||
BACK_CENTER,
|
||||
LFE,
|
||||
|
||||
OUTPUTCHANNELS
|
||||
enum ActiveFilters {
|
||||
AF_None = 0,
|
||||
AF_LowPass = 1,
|
||||
AF_HighPass = 2,
|
||||
AF_BandPass = AF_LowPass | AF_HighPass
|
||||
};
|
||||
|
||||
#define BUFFERSIZE 24000
|
||||
|
||||
extern ALboolean DuplicateStereo;
|
||||
typedef struct HrtfState {
|
||||
alignas(16) ALfloat History[SRC_HISTORY_LENGTH];
|
||||
alignas(16) ALfloat Values[HRIR_LENGTH][2];
|
||||
} HrtfState;
|
||||
|
||||
/* NOTE: The AL_FORMAT_REAR* enums aren't handled here be cause they're
|
||||
* converted to AL_FORMAT_QUAD* when loaded */
|
||||
static __inline ALuint aluBytesFromFormat(ALenum format)
|
||||
typedef struct HrtfParams {
|
||||
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
|
||||
alignas(16) ALfloat CoeffStep[HRIR_LENGTH][2];
|
||||
ALuint Delay[2];
|
||||
ALint DelayStep[2];
|
||||
} HrtfParams;
|
||||
|
||||
|
||||
typedef struct MixGains {
|
||||
ALfloat Current;
|
||||
ALfloat Step;
|
||||
ALfloat Target;
|
||||
} MixGains;
|
||||
|
||||
|
||||
typedef struct DirectParams {
|
||||
ALfloat (*OutBuffer)[BUFFERSIZE];
|
||||
|
||||
/* If not 'moving', gain/coefficients are set directly without fading. */
|
||||
ALboolean Moving;
|
||||
/* Stepping counter for gain/coefficient fading. */
|
||||
ALuint Counter;
|
||||
|
||||
struct {
|
||||
enum ActiveFilters ActiveType;
|
||||
ALfilterState LowPass;
|
||||
ALfilterState HighPass;
|
||||
} Filters[MAX_INPUT_CHANNELS];
|
||||
|
||||
union {
|
||||
struct {
|
||||
HrtfParams Params[MAX_INPUT_CHANNELS];
|
||||
HrtfState State[MAX_INPUT_CHANNELS];
|
||||
ALuint IrSize;
|
||||
ALfloat Gain;
|
||||
ALfloat Dir[3];
|
||||
} Hrtf;
|
||||
|
||||
MixGains Gains[MAX_INPUT_CHANNELS][MaxChannels];
|
||||
} Mix;
|
||||
} DirectParams;
|
||||
|
||||
typedef struct SendParams {
|
||||
ALfloat (*OutBuffer)[BUFFERSIZE];
|
||||
|
||||
ALboolean Moving;
|
||||
ALuint Counter;
|
||||
|
||||
struct {
|
||||
enum ActiveFilters ActiveType;
|
||||
ALfilterState LowPass;
|
||||
ALfilterState HighPass;
|
||||
} Filters[MAX_INPUT_CHANNELS];
|
||||
|
||||
/* Gain control, which applies to all input channels to a single (mono)
|
||||
* output buffer. */
|
||||
MixGains Gain;
|
||||
} SendParams;
|
||||
|
||||
|
||||
typedef const ALfloat* (*ResamplerFunc)(const ALfloat *src, ALuint frac, ALuint increment,
|
||||
ALfloat *restrict dst, ALuint dstlen);
|
||||
|
||||
typedef void (*MixerFunc)(const ALfloat *data, ALuint OutChans,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE], struct MixGains *Gains,
|
||||
ALuint Counter, ALuint OutPos, ALuint BufferSize);
|
||||
typedef void (*HrtfMixerFunc)(ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat *data,
|
||||
ALuint Counter, ALuint Offset, ALuint OutPos,
|
||||
const ALuint IrSize, const HrtfParams *hrtfparams,
|
||||
HrtfState *hrtfstate, ALuint BufferSize);
|
||||
|
||||
|
||||
#define GAIN_SILENCE_THRESHOLD (0.00001f) /* -100dB */
|
||||
|
||||
#define SPEEDOFSOUNDMETRESPERSEC (343.3f)
|
||||
#define AIRABSORBGAINHF (0.99426f) /* -0.05dB */
|
||||
|
||||
#define FRACTIONBITS (14)
|
||||
#define FRACTIONONE (1<<FRACTIONBITS)
|
||||
#define FRACTIONMASK (FRACTIONONE-1)
|
||||
|
||||
|
||||
inline ALfloat minf(ALfloat a, ALfloat b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALfloat maxf(ALfloat a, ALfloat b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALfloat clampf(ALfloat val, ALfloat min, ALfloat max)
|
||||
{ return minf(max, maxf(min, val)); }
|
||||
|
||||
inline ALdouble mind(ALdouble a, ALdouble b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALdouble maxd(ALdouble a, ALdouble b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALdouble clampd(ALdouble val, ALdouble min, ALdouble max)
|
||||
{ return mind(max, maxd(min, val)); }
|
||||
|
||||
inline ALuint minu(ALuint a, ALuint b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALuint maxu(ALuint a, ALuint b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALuint clampu(ALuint val, ALuint min, ALuint max)
|
||||
{ return minu(max, maxu(min, val)); }
|
||||
|
||||
inline ALint mini(ALint a, ALint b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALint maxi(ALint a, ALint b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALint clampi(ALint val, ALint min, ALint max)
|
||||
{ return mini(max, maxi(min, val)); }
|
||||
|
||||
inline ALint64 mini64(ALint64 a, ALint64 b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALint64 maxi64(ALint64 a, ALint64 b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALint64 clampi64(ALint64 val, ALint64 min, ALint64 max)
|
||||
{ return mini64(max, maxi64(min, val)); }
|
||||
|
||||
inline ALuint64 minu64(ALuint64 a, ALuint64 b)
|
||||
{ return ((a > b) ? b : a); }
|
||||
inline ALuint64 maxu64(ALuint64 a, ALuint64 b)
|
||||
{ return ((a > b) ? a : b); }
|
||||
inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
|
||||
{ return minu64(max, maxu64(min, val)); }
|
||||
|
||||
|
||||
inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
|
||||
{
|
||||
switch(format)
|
||||
{
|
||||
case AL_FORMAT_MONO8:
|
||||
case AL_FORMAT_STEREO8:
|
||||
case AL_FORMAT_QUAD8_LOKI:
|
||||
case AL_FORMAT_QUAD8:
|
||||
case AL_FORMAT_51CHN8:
|
||||
case AL_FORMAT_61CHN8:
|
||||
case AL_FORMAT_71CHN8:
|
||||
return 1;
|
||||
|
||||
case AL_FORMAT_MONO16:
|
||||
case AL_FORMAT_STEREO16:
|
||||
case AL_FORMAT_QUAD16_LOKI:
|
||||
case AL_FORMAT_QUAD16:
|
||||
case AL_FORMAT_51CHN16:
|
||||
case AL_FORMAT_61CHN16:
|
||||
case AL_FORMAT_71CHN16:
|
||||
return 2;
|
||||
|
||||
case AL_FORMAT_MONO_FLOAT32:
|
||||
case AL_FORMAT_STEREO_FLOAT32:
|
||||
case AL_FORMAT_QUAD32:
|
||||
case AL_FORMAT_51CHN32:
|
||||
case AL_FORMAT_61CHN32:
|
||||
case AL_FORMAT_71CHN32:
|
||||
return 4;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return val1 + (val2-val1)*mu;
|
||||
}
|
||||
static __inline ALuint aluChannelsFromFormat(ALenum format)
|
||||
inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALfloat mu)
|
||||
{
|
||||
switch(format)
|
||||
{
|
||||
case AL_FORMAT_MONO8:
|
||||
case AL_FORMAT_MONO16:
|
||||
case AL_FORMAT_MONO_FLOAT32:
|
||||
return 1;
|
||||
ALfloat mu2 = mu*mu;
|
||||
ALfloat a0 = -0.5f*val0 + 1.5f*val1 + -1.5f*val2 + 0.5f*val3;
|
||||
ALfloat a1 = val0 + -2.5f*val1 + 2.0f*val2 + -0.5f*val3;
|
||||
ALfloat a2 = -0.5f*val0 + 0.5f*val2;
|
||||
ALfloat a3 = val1;
|
||||
|
||||
case AL_FORMAT_STEREO8:
|
||||
case AL_FORMAT_STEREO16:
|
||||
case AL_FORMAT_STEREO_FLOAT32:
|
||||
return 2;
|
||||
|
||||
case AL_FORMAT_QUAD8_LOKI:
|
||||
case AL_FORMAT_QUAD16_LOKI:
|
||||
case AL_FORMAT_QUAD8:
|
||||
case AL_FORMAT_QUAD16:
|
||||
case AL_FORMAT_QUAD32:
|
||||
return 4;
|
||||
|
||||
case AL_FORMAT_51CHN8:
|
||||
case AL_FORMAT_51CHN16:
|
||||
case AL_FORMAT_51CHN32:
|
||||
return 6;
|
||||
|
||||
case AL_FORMAT_61CHN8:
|
||||
case AL_FORMAT_61CHN16:
|
||||
case AL_FORMAT_61CHN32:
|
||||
return 7;
|
||||
|
||||
case AL_FORMAT_71CHN8:
|
||||
case AL_FORMAT_71CHN16:
|
||||
case AL_FORMAT_71CHN32:
|
||||
return 8;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return a0*mu*mu2 + a1*mu2 + a2*mu + a3;
|
||||
}
|
||||
|
||||
ALvoid aluInitPanning(ALCcontext *Context);
|
||||
|
||||
ALvoid aluInitPanning(ALCdevice *Device);
|
||||
|
||||
/**
|
||||
* ComputeAngleGains
|
||||
*
|
||||
* Sets channel gains based on a given source's angle and its half-width. The
|
||||
* angle and hwidth parameters are in radians.
|
||||
*/
|
||||
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat hwidth, ALfloat ingain, ALfloat gains[MaxChannels]);
|
||||
|
||||
/**
|
||||
* SetGains
|
||||
*
|
||||
* Helper to set the appropriate channels to the specified gain.
|
||||
*/
|
||||
inline void SetGains(const ALCdevice *device, ALfloat ingain, ALfloat gains[MaxChannels])
|
||||
{
|
||||
ComputeAngleGains(device, 0.0f, F_PI, ingain, gains);
|
||||
}
|
||||
|
||||
|
||||
ALvoid CalcSourceParams(struct ALactivesource *src, const ALCcontext *ALContext);
|
||||
ALvoid CalcNonAttnSourceParams(struct ALactivesource *src, const ALCcontext *ALContext);
|
||||
|
||||
ALvoid MixSource(struct ALactivesource *src, ALCdevice *Device, ALuint SamplesToDo);
|
||||
|
||||
ALvoid aluMixData(ALCdevice *device, ALvoid *buffer, ALsizei size);
|
||||
/* Caller must lock the device. */
|
||||
ALvoid aluHandleDisconnect(ALCdevice *device);
|
||||
|
||||
extern ALfloat ConeScale;
|
||||
extern ALfloat ZScale;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+128
-109
@@ -1,109 +1,128 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Boris Mikhaylov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BS2B_H
|
||||
#define BS2B_H
|
||||
|
||||
/* Number of crossfeed levels */
|
||||
#define BS2B_CLEVELS 3
|
||||
|
||||
/* Normal crossfeed levels */
|
||||
#define BS2B_HIGH_CLEVEL 3
|
||||
#define BS2B_MIDDLE_CLEVEL 2
|
||||
#define BS2B_LOW_CLEVEL 1
|
||||
|
||||
/* Easy crossfeed levels */
|
||||
#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS
|
||||
#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS
|
||||
#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS
|
||||
|
||||
/* Default crossfeed levels */
|
||||
#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL
|
||||
/* Default sample rate (Hz) */
|
||||
#define BS2B_DEFAULT_SRATE 44100
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
struct bs2b {
|
||||
int level; /* Crossfeed level */
|
||||
int srate; /* Sample rate (Hz) */
|
||||
|
||||
/* Lowpass IIR filter coefficients */
|
||||
double a0_lo;
|
||||
double b1_lo;
|
||||
|
||||
/* Highboost IIR filter coefficients */
|
||||
double a0_hi;
|
||||
double a1_hi;
|
||||
double b1_hi;
|
||||
|
||||
/* Global gain against overloading */
|
||||
double gain;
|
||||
|
||||
/* Buffer of last filtered sample.
|
||||
* [0] - first channel, [1] - second channel
|
||||
*/
|
||||
struct t_last_sample {
|
||||
double asis[2];
|
||||
double lo[2];
|
||||
double hi[2];
|
||||
} last_sample;
|
||||
};
|
||||
|
||||
/* Clear buffers and set new coefficients with new crossfeed level value.
|
||||
* level - crossfeed level of *LEVEL values.
|
||||
*/
|
||||
void bs2b_set_level(struct bs2b *bs2b, int level);
|
||||
|
||||
/* Return current crossfeed level value */
|
||||
int bs2b_get_level(struct bs2b *bs2b);
|
||||
|
||||
/* Clear buffers and set new coefficients with new sample rate value.
|
||||
* srate - sample rate by Hz.
|
||||
*/
|
||||
void bs2b_set_srate(struct bs2b *bs2b, int srate);
|
||||
|
||||
/* Return current sample rate value */
|
||||
int bs2b_get_srate(struct bs2b *bs2b);
|
||||
|
||||
/* Clear buffer */
|
||||
void bs2b_clear(struct bs2b *bs2b);
|
||||
|
||||
/* Return 1 if buffer is clear */
|
||||
int bs2b_is_clear(struct bs2b *bs2b);
|
||||
|
||||
/* Crossfeeds one stereo sample that are pointed by sample.
|
||||
* [0] - first channel, [1] - second channel.
|
||||
* Returns crossfided samle by sample pointer.
|
||||
*/
|
||||
|
||||
/* sample poits to floats */
|
||||
void bs2b_cross_feed(struct bs2b *bs2b, float *sample);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* BS2B_H */
|
||||
/*-
|
||||
* Copyright (c) 2005 Boris Mikhaylov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BS2B_H
|
||||
#define BS2B_H
|
||||
|
||||
/* Number of crossfeed levels */
|
||||
#define BS2B_CLEVELS 3
|
||||
|
||||
/* Normal crossfeed levels */
|
||||
#define BS2B_HIGH_CLEVEL 3
|
||||
#define BS2B_MIDDLE_CLEVEL 2
|
||||
#define BS2B_LOW_CLEVEL 1
|
||||
|
||||
/* Easy crossfeed levels */
|
||||
#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS
|
||||
#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS
|
||||
#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS
|
||||
|
||||
/* Default crossfeed levels */
|
||||
#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL
|
||||
/* Default sample rate (Hz) */
|
||||
#define BS2B_DEFAULT_SRATE 44100
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
struct bs2b {
|
||||
int level; /* Crossfeed level */
|
||||
int srate; /* Sample rate (Hz) */
|
||||
|
||||
/* Lowpass IIR filter coefficients */
|
||||
float a0_lo;
|
||||
float b1_lo;
|
||||
|
||||
/* Highboost IIR filter coefficients */
|
||||
float a0_hi;
|
||||
float a1_hi;
|
||||
float b1_hi;
|
||||
|
||||
/* Buffer of last filtered sample.
|
||||
* [0] - first channel, [1] - second channel
|
||||
*/
|
||||
struct t_last_sample {
|
||||
float asis[2];
|
||||
float lo[2];
|
||||
float hi[2];
|
||||
} last_sample;
|
||||
};
|
||||
|
||||
/* Clear buffers and set new coefficients with new crossfeed level value.
|
||||
* level - crossfeed level of *LEVEL values.
|
||||
*/
|
||||
void bs2b_set_level(struct bs2b *bs2b, int level);
|
||||
|
||||
/* Return current crossfeed level value */
|
||||
int bs2b_get_level(struct bs2b *bs2b);
|
||||
|
||||
/* Clear buffers and set new coefficients with new sample rate value.
|
||||
* srate - sample rate by Hz.
|
||||
*/
|
||||
void bs2b_set_srate(struct bs2b *bs2b, int srate);
|
||||
|
||||
/* Return current sample rate value */
|
||||
int bs2b_get_srate(struct bs2b *bs2b);
|
||||
|
||||
/* Clear buffer */
|
||||
void bs2b_clear(struct bs2b *bs2b);
|
||||
|
||||
/* Crossfeeds one stereo sample that are pointed by sample.
|
||||
* [0] - first channel, [1] - second channel.
|
||||
* Returns crossfided sample by sample pointer.
|
||||
*/
|
||||
inline void bs2b_cross_feed(struct bs2b *bs2b, float *restrict sample)
|
||||
{
|
||||
/* Single pole IIR filter.
|
||||
* O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1]
|
||||
*/
|
||||
|
||||
/* Lowpass filter */
|
||||
#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1))
|
||||
|
||||
/* Highboost filter */
|
||||
#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1))
|
||||
|
||||
/* Lowpass filter */
|
||||
bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]);
|
||||
bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]);
|
||||
|
||||
/* Highboost filter */
|
||||
bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]);
|
||||
bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]);
|
||||
bs2b->last_sample.asis[0] = sample[0];
|
||||
bs2b->last_sample.asis[1] = sample[1];
|
||||
|
||||
/* Crossfeed */
|
||||
sample[0] = bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1];
|
||||
sample[1] = bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0];
|
||||
#undef hi_filter
|
||||
#undef lo_filter
|
||||
} /* bs2b_cross_feed */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* BS2B_H */
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef SAMPLE_CVT_H
|
||||
#define SAMPLE_CVT_H
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alBuffer.h"
|
||||
|
||||
void ConvertData(ALvoid *dst, enum UserFmtType dstType, const ALvoid *src, enum UserFmtType srcType, ALsizei numchans, ALsizei len, ALsizei align);
|
||||
|
||||
#endif /* SAMPLE_CVT_H */
|
||||
+469
-383
@@ -29,436 +29,522 @@
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alThunk.h"
|
||||
#include "alError.h"
|
||||
#include "alSource.h"
|
||||
|
||||
|
||||
static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *ALEffectSlot, ALeffect *effect);
|
||||
extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id);
|
||||
extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id);
|
||||
|
||||
static ALenum AddEffectSlotArray(ALCcontext *Context, ALeffectslot **start, ALsizei count);
|
||||
static void RemoveEffectSlotArray(ALCcontext *Context, const ALeffectslot *slot);
|
||||
|
||||
|
||||
ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
|
||||
static UIntMap EffectStateFactoryMap;
|
||||
static inline ALeffectStateFactory *getFactoryByType(ALenum type)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALsizei i, j;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
ALCdevice *Device = Context->Device;
|
||||
|
||||
if(Context->AuxiliaryEffectSlotCount+n <= Device->AuxiliaryEffectSlotMax)
|
||||
{
|
||||
// Check that enough memory has been allocted in the 'effectslots' array for n Effect Slots
|
||||
if (!IsBadWritePtr((void*)effectslots, n * sizeof(ALuint)))
|
||||
{
|
||||
ALeffectslot **list = &Context->AuxiliaryEffectSlot;
|
||||
while(*list)
|
||||
list = &(*list)->next;
|
||||
|
||||
i = 0;
|
||||
while(i < n)
|
||||
{
|
||||
*list = calloc(1, sizeof(ALeffectslot));
|
||||
if(!(*list))
|
||||
{
|
||||
// We must have run out or memory
|
||||
alDeleteAuxiliaryEffectSlots(i, effectslots);
|
||||
alSetError(AL_OUT_OF_MEMORY);
|
||||
break;
|
||||
}
|
||||
|
||||
(*list)->Gain = 1.0;
|
||||
(*list)->AuxSendAuto = AL_TRUE;
|
||||
for(j = 0;j < BUFFERSIZE;j++)
|
||||
(*list)->WetBuffer[j] = 0.0f;
|
||||
(*list)->refcount = 0;
|
||||
|
||||
effectslots[i] = (ALuint)ALTHUNK_ADDENTRY(*list);
|
||||
(*list)->effectslot = effectslots[i];
|
||||
|
||||
Context->AuxiliaryEffectSlotCount++;
|
||||
i++;
|
||||
|
||||
list = &(*list)->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_OPERATION);
|
||||
}
|
||||
|
||||
ProcessContext(Context);
|
||||
ALeffectStateFactory* (*getFactory)(void) = LookupUIntMapKey(&EffectStateFactoryMap, type);
|
||||
if(getFactory != NULL)
|
||||
return getFactory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALeffectslot *ALAuxiliaryEffectSlot;
|
||||
ALCcontext *context;
|
||||
VECTOR(ALeffectslot*) slotvec;
|
||||
ALsizei cur;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
VECTOR_INIT(slotvec);
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
if(!VECTOR_RESERVE(slotvec, n))
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot));
|
||||
err = AL_OUT_OF_MEMORY;
|
||||
if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
|
||||
{
|
||||
al_free(slot);
|
||||
alDeleteAuxiliaryEffectSlots(cur, effectslots);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
}
|
||||
|
||||
err = NewThunkEntry(&slot->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntry(&context->EffectSlotMap, slot->id, slot);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
FreeThunkEntry(slot->id);
|
||||
DELETE_OBJ(slot->EffectState);
|
||||
al_free(slot);
|
||||
|
||||
alDeleteAuxiliaryEffectSlots(cur, effectslots);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
}
|
||||
|
||||
VECTOR_PUSH_BACK(slotvec, slot);
|
||||
|
||||
effectslots[cur] = slot->id;
|
||||
}
|
||||
err = AddEffectSlotArray(context, VECTOR_ITER_BEGIN(slotvec), n);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
alDeleteAuxiliaryEffectSlots(cur, effectslots);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
}
|
||||
|
||||
done:
|
||||
VECTOR_DEINIT(slotvec);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALeffectslot *slot;
|
||||
ALsizei i;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if (n >= 0)
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
// Check that all effectslots are valid
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (!alIsAuxiliaryEffectSlot(effectslots[i]))
|
||||
{
|
||||
alSetError(AL_INVALID_NAME);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALAuxiliaryEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslots[i]);
|
||||
if(ALAuxiliaryEffectSlot->refcount > 0)
|
||||
{
|
||||
alSetError(AL_INVALID_NAME);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == n)
|
||||
{
|
||||
// All effectslots are valid
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
// Recheck that the effectslot is valid, because there could be duplicated names
|
||||
if (alIsAuxiliaryEffectSlot(effectslots[i]))
|
||||
{
|
||||
ALeffectslot **list;
|
||||
|
||||
ALAuxiliaryEffectSlot = ((ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslots[i]));
|
||||
|
||||
// Remove Source from list of Sources
|
||||
list = &Context->AuxiliaryEffectSlot;
|
||||
while(*list && *list != ALAuxiliaryEffectSlot)
|
||||
list = &(*list)->next;
|
||||
|
||||
if(*list)
|
||||
*list = (*list)->next;
|
||||
ALTHUNK_REMOVEENTRY(ALAuxiliaryEffectSlot->effectslot);
|
||||
|
||||
if(ALAuxiliaryEffectSlot->EffectState)
|
||||
ALEffect_Destroy(ALAuxiliaryEffectSlot->EffectState);
|
||||
|
||||
memset(ALAuxiliaryEffectSlot, 0, sizeof(ALeffectslot));
|
||||
free(ALAuxiliaryEffectSlot);
|
||||
|
||||
Context->AuxiliaryEffectSlotCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if((slot=LookupEffectSlot(context, effectslots[i])) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&slot->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(Context);
|
||||
// All effectslots are valid
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL)
|
||||
continue;
|
||||
FreeThunkEntry(slot->id);
|
||||
|
||||
RemoveEffectSlotArray(context, slot);
|
||||
DELETE_OBJ(slot->EffectState);
|
||||
|
||||
memset(slot, 0, sizeof(*slot));
|
||||
al_free(slot);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
|
||||
AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALeffectslot **list;
|
||||
ALCcontext *context;
|
||||
ALboolean ret;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return AL_FALSE;
|
||||
context = GetContextRef();
|
||||
if(!context) return AL_FALSE;
|
||||
|
||||
list = &Context->AuxiliaryEffectSlot;
|
||||
while(*list && (*list)->effectslot != effectslot)
|
||||
list = &(*list)->next;
|
||||
ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE);
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(context);
|
||||
|
||||
return (*list ? AL_TRUE : AL_FALSE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue)
|
||||
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALeffectslot *slot;
|
||||
ALeffect *effect = NULL;
|
||||
ALenum err;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if (alIsAuxiliaryEffectSlot(effectslot))
|
||||
device = context->Device;
|
||||
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
ALeffectslot *ALEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslot);
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
effect = (value ? LookupEffect(device, value) : NULL);
|
||||
if(!(value == 0 || effect != NULL))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
if(alIsEffect(iValue))
|
||||
{
|
||||
ALeffect *effect = (ALeffect*)ALTHUNK_LOOKUPENTRY(iValue);
|
||||
InitializeEffect(Context, ALEffectSlot, effect);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
err = InitializeEffect(device, slot, effect);
|
||||
if(err != AL_NO_ERROR)
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
|
||||
break;
|
||||
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
if(iValue == AL_TRUE || iValue == AL_FALSE)
|
||||
ALEffectSlot->AuxSendAuto = iValue;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
if(!(value == AL_TRUE || value == AL_FALSE))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
slot->AuxSendAuto = value;
|
||||
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues)
|
||||
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCcontext *context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (alIsAuxiliaryEffectSlot(effectslot))
|
||||
switch(param)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
alAuxiliaryEffectSloti(effectslot, param, piValues[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (alIsAuxiliaryEffectSlot(effectslot))
|
||||
{
|
||||
ALeffectslot *ALEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslot);
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
if(flValue >= 0.0f && flValue <= 1.0f)
|
||||
ALEffectSlot->Gain = flValue;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (alIsAuxiliaryEffectSlot(effectslot))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
alAuxiliaryEffectSlotf(effectslot, param, pflValues[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (alIsAuxiliaryEffectSlot(effectslot))
|
||||
{
|
||||
ALeffectslot *ALEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslot);
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
*piValue = ALEffectSlot->effect.effect;
|
||||
break;
|
||||
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
*piValue = ALEffectSlot->AuxSendAuto;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (alIsAuxiliaryEffectSlot(effectslot))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
alGetAuxiliaryEffectSloti(effectslot, param, piValues);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (alIsAuxiliaryEffectSlot(effectslot))
|
||||
{
|
||||
ALeffectslot *ALEffectSlot = (ALeffectslot*)ALTHUNK_LOOKUPENTRY(effectslot);
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
*pflValue = ALEffectSlot->Gain;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (alIsAuxiliaryEffectSlot(effectslot))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
alGetAuxiliaryEffectSlotf(effectslot, param, pflValues);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
|
||||
static ALvoid InitializeEffect(ALCcontext *Context, ALeffectslot *ALEffectSlot, ALeffect *effect)
|
||||
{
|
||||
if((!effect) || (effect->type != ALEffectSlot->effect.type))
|
||||
{
|
||||
ALeffectState *NewState = NULL;
|
||||
if(effect)
|
||||
{
|
||||
if(effect->type == AL_EFFECT_EAXREVERB)
|
||||
NewState = EAXVerbCreate(Context);
|
||||
else if(effect->type == AL_EFFECT_REVERB)
|
||||
NewState = VerbCreate(Context);
|
||||
else if(effect->type == AL_EFFECT_ECHO)
|
||||
NewState = EchoCreate(Context);
|
||||
/* No new state? An error occured.. */
|
||||
if(!NewState)
|
||||
return;
|
||||
}
|
||||
if(ALEffectSlot->EffectState)
|
||||
ALEffect_Destroy(ALEffectSlot->EffectState);
|
||||
ALEffectSlot->EffectState = NewState;
|
||||
}
|
||||
if(!effect)
|
||||
{
|
||||
memset(&ALEffectSlot->effect, 0, sizeof(ALEffectSlot->effect));
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
alAuxiliaryEffectSloti(effectslot, param, values[0]);
|
||||
return;
|
||||
}
|
||||
memcpy(&ALEffectSlot->effect, effect, sizeof(*effect));
|
||||
ALEffect_Update(ALEffectSlot->EffectState, Context, effect);
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(LookupEffectSlot(context, effectslot) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALeffectslot *slot;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
if(!(value >= 0.0f && value <= 1.0f))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
slot->Gain = value;
|
||||
ATOMIC_STORE(&slot->NeedsUpdate, AL_TRUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
alAuxiliaryEffectSlotf(effectslot, param, values[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(LookupEffectSlot(context, effectslot) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALeffectslot *slot;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
*value = slot->AuxSendAuto;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
|
||||
alGetAuxiliaryEffectSloti(effectslot, param, values);
|
||||
return;
|
||||
}
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(LookupEffectSlot(context, effectslot) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALeffectslot *slot;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if((slot=LookupEffectSlot(context, effectslot)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
*value = slot->Gain;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_GAIN:
|
||||
alGetAuxiliaryEffectSlotf(effectslot, param, values);
|
||||
return;
|
||||
}
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(LookupEffectSlot(context, effectslot) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
static ALenum AddEffectSlotArray(ALCcontext *context, ALeffectslot **start, ALsizei count)
|
||||
{
|
||||
ALenum err = AL_NO_ERROR;
|
||||
|
||||
LockContext(context);
|
||||
if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_ITER_END(context->ActiveAuxSlots), start, start+count))
|
||||
err = AL_OUT_OF_MEMORY;
|
||||
UnlockContext(context);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot)
|
||||
{
|
||||
ALeffectslot **iter;
|
||||
|
||||
LockContext(context);
|
||||
#define MATCH_SLOT(_i) (slot == *(_i))
|
||||
VECTOR_FIND_IF(iter, ALeffectslot*, context->ActiveAuxSlots, MATCH_SLOT);
|
||||
if(iter != VECTOR_ITER_END(context->ActiveAuxSlots))
|
||||
{
|
||||
*iter = VECTOR_BACK(context->ActiveAuxSlots);
|
||||
VECTOR_POP_BACK(context->ActiveAuxSlots);
|
||||
}
|
||||
#undef MATCH_SLOT
|
||||
UnlockContext(context);
|
||||
}
|
||||
|
||||
|
||||
void InitEffectFactoryMap(void)
|
||||
{
|
||||
InitUIntMap(&EffectStateFactoryMap, ~0);
|
||||
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_AUTOWAH, ALautowahStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory);
|
||||
InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory);
|
||||
}
|
||||
|
||||
void DeinitEffectFactoryMap(void)
|
||||
{
|
||||
ResetUIntMap(&EffectStateFactoryMap);
|
||||
}
|
||||
|
||||
|
||||
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
|
||||
{
|
||||
ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
|
||||
ALeffectStateFactory *factory;
|
||||
|
||||
if(newtype != EffectSlot->EffectType)
|
||||
{
|
||||
ALeffectState *State;
|
||||
FPUCtl oldMode;
|
||||
|
||||
factory = getFactoryByType(newtype);
|
||||
if(!factory)
|
||||
{
|
||||
ERR("Failed to find factory for effect type 0x%04x\n", newtype);
|
||||
return AL_INVALID_ENUM;
|
||||
}
|
||||
State = V0(factory,create)();
|
||||
if(!State)
|
||||
return AL_OUT_OF_MEMORY;
|
||||
|
||||
SetMixerFPUMode(&oldMode);
|
||||
|
||||
ALCdevice_Lock(Device);
|
||||
if(V(State,deviceUpdate)(Device) == AL_FALSE)
|
||||
{
|
||||
ALCdevice_Unlock(Device);
|
||||
RestoreFPUMode(&oldMode);
|
||||
DELETE_OBJ(State);
|
||||
return AL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
State = ExchangePtr((XchgPtr*)&EffectSlot->EffectState, State);
|
||||
if(!effect)
|
||||
{
|
||||
memset(&EffectSlot->EffectProps, 0, sizeof(EffectSlot->EffectProps));
|
||||
EffectSlot->EffectType = AL_EFFECT_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props));
|
||||
EffectSlot->EffectType = effect->type;
|
||||
}
|
||||
|
||||
/* FIXME: This should be done asynchronously, but since the EffectState
|
||||
* object was changed, it needs an update before its Process method can
|
||||
* be called. */
|
||||
ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_FALSE);
|
||||
V(EffectSlot->EffectState,update)(Device, EffectSlot);
|
||||
ALCdevice_Unlock(Device);
|
||||
|
||||
RestoreFPUMode(&oldMode);
|
||||
|
||||
DELETE_OBJ(State);
|
||||
State = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(effect)
|
||||
{
|
||||
ALCdevice_Lock(Device);
|
||||
memcpy(&EffectSlot->EffectProps, &effect->Props, sizeof(effect->Props));
|
||||
ALCdevice_Unlock(Device);
|
||||
ATOMIC_STORE(&EffectSlot->NeedsUpdate, AL_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
ALenum InitEffectSlot(ALeffectslot *slot)
|
||||
{
|
||||
ALeffectStateFactory *factory;
|
||||
ALuint i, c;
|
||||
|
||||
slot->EffectType = AL_EFFECT_NULL;
|
||||
|
||||
factory = getFactoryByType(AL_EFFECT_NULL);
|
||||
if(!(slot->EffectState=V0(factory,create)()))
|
||||
return AL_OUT_OF_MEMORY;
|
||||
|
||||
slot->Gain = 1.0;
|
||||
slot->AuxSendAuto = AL_TRUE;
|
||||
ATOMIC_INIT(&slot->NeedsUpdate, AL_FALSE);
|
||||
for(c = 0;c < 1;c++)
|
||||
{
|
||||
for(i = 0;i < BUFFERSIZE;i++)
|
||||
slot->WetBuffer[c][i] = 0.0f;
|
||||
}
|
||||
InitRef(&slot->ref, 0);
|
||||
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
|
||||
{
|
||||
while(Context->AuxiliaryEffectSlot)
|
||||
ALsizei pos;
|
||||
for(pos = 0;pos < Context->EffectSlotMap.size;pos++)
|
||||
{
|
||||
ALeffectslot *temp = Context->AuxiliaryEffectSlot;
|
||||
Context->AuxiliaryEffectSlot = Context->AuxiliaryEffectSlot->next;
|
||||
ALeffectslot *temp = Context->EffectSlotMap.array[pos].value;
|
||||
Context->EffectSlotMap.array[pos].value = NULL;
|
||||
|
||||
// Release effectslot structure
|
||||
if(temp->EffectState)
|
||||
ALEffect_Destroy(temp->EffectState);
|
||||
ALTHUNK_REMOVEENTRY(temp->effectslot);
|
||||
DELETE_OBJ(temp->EffectState);
|
||||
|
||||
FreeThunkEntry(temp->id);
|
||||
memset(temp, 0, sizeof(ALeffectslot));
|
||||
free(temp);
|
||||
al_free(temp);
|
||||
}
|
||||
Context->AuxiliaryEffectSlotCount = 0;
|
||||
}
|
||||
|
||||
+1155
-953
File diff suppressed because it is too large
Load Diff
@@ -1,639 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
#include "alError.h"
|
||||
#include "alDatabuffer.h"
|
||||
#include "alThunk.h"
|
||||
|
||||
|
||||
/*
|
||||
* alGenDatabuffersEXT(ALsizei n, ALuint *puiBuffers)
|
||||
*
|
||||
* Generates n AL Databuffers, and stores the Databuffers Names in the array pointed to by puiBuffers
|
||||
*/
|
||||
ALvoid ALAPIENTRY alGenDatabuffersEXT(ALsizei n,ALuint *puiBuffers)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALsizei i=0;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
/* Check that we are actually generation some Databuffers */
|
||||
if(n > 0)
|
||||
{
|
||||
ALCdevice *device = Context->Device;
|
||||
|
||||
/* Check the pointer is valid (and points to enough memory to store
|
||||
* Databuffer Names) */
|
||||
if(!IsBadWritePtr((void*)puiBuffers, n * sizeof(ALuint)))
|
||||
{
|
||||
ALdatabuffer **list = &device->Databuffers;
|
||||
while(*list)
|
||||
list = &(*list)->next;
|
||||
|
||||
/* Create all the new Databuffers */
|
||||
while(i < n)
|
||||
{
|
||||
*list = calloc(1, sizeof(ALdatabuffer));
|
||||
if(!(*list))
|
||||
{
|
||||
alDeleteDatabuffersEXT(i, puiBuffers);
|
||||
alSetError(AL_OUT_OF_MEMORY);
|
||||
break;
|
||||
}
|
||||
|
||||
puiBuffers[i] = (ALuint)ALTHUNK_ADDENTRY(*list);
|
||||
(*list)->databuffer = puiBuffers[i];
|
||||
(*list)->state = UNMAPPED;
|
||||
device->DatabufferCount++;
|
||||
i++;
|
||||
|
||||
list = &(*list)->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
}
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
/*
|
||||
* alDatabeleteBuffersEXT(ALsizei n, ALuint *puiBuffers)
|
||||
*
|
||||
* Deletes the n AL Databuffers pointed to by puiBuffers
|
||||
*/
|
||||
ALvoid ALAPIENTRY alDeleteDatabuffersEXT(ALsizei n, const ALuint *puiBuffers)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALdatabuffer *ALBuf;
|
||||
ALsizei i;
|
||||
ALboolean bFailed = AL_FALSE;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
/* Check we are actually Deleting some Databuffers */
|
||||
if(n >= 0)
|
||||
{
|
||||
ALCdevice *device = Context->Device;
|
||||
|
||||
/* Check that all the databuffers are valid and can actually be
|
||||
* deleted */
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
/* Check for valid Buffer ID (can be NULL buffer) */
|
||||
if(alIsDatabufferEXT(puiBuffers[i]))
|
||||
{
|
||||
/* If not the NULL buffer, check that it's unmapped */
|
||||
ALBuf = ((ALdatabuffer *)ALTHUNK_LOOKUPENTRY(puiBuffers[i]));
|
||||
if(ALBuf)
|
||||
{
|
||||
if(ALBuf->state != UNMAPPED)
|
||||
{
|
||||
/* Databuffer still in use, cannot be deleted */
|
||||
alSetError(AL_INVALID_OPERATION);
|
||||
bFailed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Invalid Databuffer */
|
||||
alSetError(AL_INVALID_NAME);
|
||||
bFailed = AL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If all the Databuffers were valid (and unmapped), then we can
|
||||
* delete them */
|
||||
if(!bFailed)
|
||||
{
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if(puiBuffers[i] && alIsDatabufferEXT(puiBuffers[i]))
|
||||
{
|
||||
ALdatabuffer **list = &device->Databuffers;
|
||||
|
||||
ALBuf = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(puiBuffers[i]);
|
||||
while(*list && *list != ALBuf)
|
||||
list = &(*list)->next;
|
||||
|
||||
if(*list)
|
||||
*list = (*list)->next;
|
||||
|
||||
if(ALBuf == Context->SampleSource)
|
||||
Context->SampleSource = NULL;
|
||||
if(ALBuf == Context->SampleSink)
|
||||
Context->SampleSink = NULL;
|
||||
|
||||
// Release the memory used to store audio data
|
||||
free(ALBuf->data);
|
||||
|
||||
// Release buffer structure
|
||||
ALTHUNK_REMOVEENTRY(puiBuffers[i]);
|
||||
memset(ALBuf, 0, sizeof(ALdatabuffer));
|
||||
device->DatabufferCount--;
|
||||
free(ALBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(Context);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* alIsDatabufferEXT(ALuint uiBuffer)
|
||||
*
|
||||
* Checks if ulBuffer is a valid Databuffer Name
|
||||
*/
|
||||
ALboolean ALAPIENTRY alIsDatabufferEXT(ALuint uiBuffer)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALdatabuffer *ALBuf;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return AL_FALSE;
|
||||
|
||||
/* Check through list of generated databuffers for uiBuffer */
|
||||
ALBuf = Context->Device->Databuffers;
|
||||
while(ALBuf && ALBuf->databuffer != uiBuffer)
|
||||
ALBuf = ALBuf->next;
|
||||
|
||||
ProcessContext(Context);
|
||||
|
||||
return ((ALBuf || !uiBuffer) ? AL_TRUE : AL_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* alDatabufferDataEXT(ALuint buffer,ALvoid *data,ALsizei size,ALenum usage)
|
||||
*
|
||||
* Fill databuffer with data
|
||||
*/
|
||||
ALvoid ALAPIENTRY alDatabufferDataEXT(ALuint buffer,const ALvoid *data,ALsizei size,ALenum usage)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALdatabuffer *ALBuf;
|
||||
ALvoid *temp;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
ALBuf = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(buffer);
|
||||
if(ALBuf->state == UNMAPPED)
|
||||
{
|
||||
if(usage == AL_STREAM_WRITE_EXT || usage == AL_STREAM_READ_EXT ||
|
||||
usage == AL_STREAM_COPY_EXT || usage == AL_STATIC_WRITE_EXT ||
|
||||
usage == AL_STATIC_READ_EXT || usage == AL_STATIC_COPY_EXT ||
|
||||
usage == AL_DYNAMIC_WRITE_EXT || usage == AL_DYNAMIC_READ_EXT ||
|
||||
usage == AL_DYNAMIC_COPY_EXT)
|
||||
{
|
||||
/* (Re)allocate data */
|
||||
temp = realloc(ALBuf->data, size);
|
||||
if(temp)
|
||||
{
|
||||
ALBuf->data = temp;
|
||||
ALBuf->size = size;
|
||||
ALBuf->usage = usage;
|
||||
if(data)
|
||||
memcpy(ALBuf->data, data, size);
|
||||
}
|
||||
else
|
||||
alSetError(AL_OUT_OF_MEMORY);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_OPERATION);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid ALAPIENTRY alDatabufferSubDataEXT(ALuint uiBuffer, ALuint start, ALsizei length, const ALvoid *data)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALdatabuffer *pBuffer;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(alIsDatabufferEXT(uiBuffer) && uiBuffer != 0)
|
||||
{
|
||||
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(uiBuffer);
|
||||
|
||||
if(length >= 0 && start+length <= pBuffer->size)
|
||||
{
|
||||
if(pBuffer->state == UNMAPPED)
|
||||
memcpy(pBuffer->data+start, data, length);
|
||||
else
|
||||
alSetError(AL_INVALID_OPERATION);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
ALvoid ALAPIENTRY alGetDatabufferSubDataEXT(ALuint uiBuffer, ALuint start, ALsizei length, ALvoid *data)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALdatabuffer *pBuffer;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(alIsDatabufferEXT(uiBuffer) && uiBuffer != 0)
|
||||
{
|
||||
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(uiBuffer);
|
||||
|
||||
if(length >= 0 && start+length <= pBuffer->size)
|
||||
{
|
||||
if(pBuffer->state == UNMAPPED)
|
||||
memcpy(data, pBuffer->data+start, length);
|
||||
else
|
||||
alSetError(AL_INVALID_OPERATION);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALvoid ALAPIENTRY alDatabufferfEXT(ALuint buffer, ALenum eParam, ALfloat flValue)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
(void)flValue;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
ALvoid ALAPIENTRY alDatabufferfvEXT(ALuint buffer, ALenum eParam, const ALfloat* flValues)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
(void)flValues;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALvoid ALAPIENTRY alDatabufferiEXT(ALuint buffer, ALenum eParam, ALint lValue)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
(void)lValue;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
ALvoid ALAPIENTRY alDatabufferivEXT(ALuint buffer, ALenum eParam, const ALint* plValues)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
(void)plValues;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALvoid ALAPIENTRY alGetDatabufferfEXT(ALuint buffer, ALenum eParam, ALfloat *pflValue)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(pflValue)
|
||||
{
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
ALvoid ALAPIENTRY alGetDatabufferfvEXT(ALuint buffer, ALenum eParam, ALfloat* pflValues)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(pflValues)
|
||||
{
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
ALvoid ALAPIENTRY alGetDatabufferiEXT(ALuint buffer, ALenum eParam, ALint *plValue)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALdatabuffer *pBuffer;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(plValue)
|
||||
{
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(buffer);
|
||||
|
||||
switch(eParam)
|
||||
{
|
||||
case AL_SIZE:
|
||||
*plValue = pBuffer->size;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
ALvoid ALAPIENTRY alGetDatabufferivEXT(ALuint buffer, ALenum eParam, ALint* plValues)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(plValues)
|
||||
{
|
||||
if(alIsDatabufferEXT(buffer) && buffer != 0)
|
||||
{
|
||||
switch (eParam)
|
||||
{
|
||||
case AL_SIZE:
|
||||
alGetBufferi(buffer, eParam, plValues);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALvoid ALAPIENTRY alSelectDatabufferEXT(ALenum target, ALuint uiBuffer)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALdatabuffer *pBuffer;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(alIsDatabufferEXT(uiBuffer))
|
||||
{
|
||||
pBuffer = (ALdatabuffer*)(uiBuffer ? ALTHUNK_LOOKUPENTRY(uiBuffer) : NULL);
|
||||
if(target == AL_SAMPLE_SOURCE_EXT)
|
||||
pContext->SampleSource = pBuffer;
|
||||
else if(target == AL_SAMPLE_SINK_EXT)
|
||||
pContext->SampleSink = pBuffer;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALvoid* ALAPIENTRY alMapDatabufferEXT(ALuint uiBuffer, ALuint start, ALsizei length, ALenum access)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALdatabuffer *pBuffer;
|
||||
ALvoid *ret = NULL;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return NULL;
|
||||
|
||||
if(alIsDatabufferEXT(uiBuffer) && uiBuffer != 0)
|
||||
{
|
||||
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(uiBuffer);
|
||||
|
||||
if(length >= 0 && start+length <= pBuffer->size)
|
||||
{
|
||||
if(access == AL_READ_ONLY_EXT || access == AL_WRITE_ONLY_EXT ||
|
||||
access == AL_READ_WRITE_EXT)
|
||||
{
|
||||
if(pBuffer->state == UNMAPPED)
|
||||
{
|
||||
ret = pBuffer->data + start;
|
||||
pBuffer->state = MAPPED;
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_OPERATION);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ALvoid ALAPIENTRY alUnmapDatabufferEXT(ALuint uiBuffer)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALdatabuffer *pBuffer;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(alIsDatabufferEXT(uiBuffer) && uiBuffer != 0)
|
||||
{
|
||||
pBuffer = (ALdatabuffer*)ALTHUNK_LOOKUPENTRY(uiBuffer);
|
||||
|
||||
if(pBuffer->state == MAPPED)
|
||||
pBuffer->state = UNMAPPED;
|
||||
else
|
||||
alSetError(AL_INVALID_OPERATION);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ReleaseALDatabuffers()
|
||||
*
|
||||
* INTERNAL FN : Called by DLLMain on exit to destroy any buffers that still exist
|
||||
*/
|
||||
ALvoid ReleaseALDatabuffers(ALCdevice *device)
|
||||
{
|
||||
ALdatabuffer *ALBuffer;
|
||||
ALdatabuffer *ALBufferTemp;
|
||||
|
||||
ALBuffer = device->Databuffers;
|
||||
while(ALBuffer)
|
||||
{
|
||||
// Release sample data
|
||||
free(ALBuffer->data);
|
||||
|
||||
// Release Buffer structure
|
||||
ALBufferTemp = ALBuffer;
|
||||
ALBuffer = ALBuffer->next;
|
||||
memset(ALBufferTemp, 0, sizeof(ALdatabuffer));
|
||||
free(ALBufferTemp);
|
||||
}
|
||||
device->Databuffers = NULL;
|
||||
device->DatabufferCount = 0;
|
||||
}
|
||||
+537
-1079
File diff suppressed because it is too large
Load Diff
+42
-19
@@ -20,35 +20,58 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alError.h"
|
||||
|
||||
ALAPI ALenum ALAPIENTRY alGetError(ALvoid)
|
||||
ALboolean TrapALError = AL_FALSE;
|
||||
|
||||
ALvoid alSetError(ALCcontext *Context, ALenum errorCode)
|
||||
{
|
||||
ALenum curerr = AL_NO_ERROR;
|
||||
if(TrapALError)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/* DebugBreak will cause an exception if there is no debugger */
|
||||
if(IsDebuggerPresent())
|
||||
DebugBreak();
|
||||
#elif defined(SIGTRAP)
|
||||
raise(SIGTRAP);
|
||||
#endif
|
||||
}
|
||||
ATOMIC_COMPARE_EXCHANGE_STRONG(ALenum, &Context->LastError, &curerr, errorCode);
|
||||
}
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetError(void)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALenum errorCode;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return AL_INVALID_OPERATION;
|
||||
Context = GetContextRef();
|
||||
if(!Context)
|
||||
{
|
||||
if(TrapALError)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(IsDebuggerPresent())
|
||||
DebugBreak();
|
||||
#elif defined(SIGTRAP)
|
||||
raise(SIGTRAP);
|
||||
#endif
|
||||
}
|
||||
return AL_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
errorCode = Context->LastError;
|
||||
Context->LastError = AL_NO_ERROR;
|
||||
errorCode = ATOMIC_EXCHANGE(ALenum, &Context->LastError, AL_NO_ERROR);
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(Context);
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
ALvoid alSetError(ALenum errorCode)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(Context)
|
||||
{
|
||||
if(Context->LastError == AL_NO_ERROR)
|
||||
Context->LastError = errorCode;
|
||||
ProcessContext(Context);
|
||||
}
|
||||
}
|
||||
|
||||
+37
-334
@@ -23,346 +23,58 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "alExtension.h"
|
||||
|
||||
#include "alError.h"
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alEffect.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alDatabuffer.h"
|
||||
#include "alSource.h"
|
||||
#include "alBuffer.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
static ALfunction function[]= {
|
||||
{ "alEnable", (ALvoid *) alEnable },
|
||||
{ "alDisable", (ALvoid *) alDisable },
|
||||
{ "alIsEnabled", (ALvoid *) alIsEnabled },
|
||||
{ "alGetString", (ALvoid *) alGetString },
|
||||
{ "alGetBooleanv", (ALvoid *) alGetBooleanv },
|
||||
{ "alGetIntegerv", (ALvoid *) alGetIntegerv },
|
||||
{ "alGetFloatv", (ALvoid *) alGetFloatv },
|
||||
{ "alGetDoublev", (ALvoid *) alGetDoublev },
|
||||
{ "alGetBoolean", (ALvoid *) alGetBoolean },
|
||||
{ "alGetInteger", (ALvoid *) alGetInteger },
|
||||
{ "alGetFloat", (ALvoid *) alGetFloat },
|
||||
{ "alGetDouble", (ALvoid *) alGetDouble },
|
||||
{ "alGetError", (ALvoid *) alGetError },
|
||||
{ "alIsExtensionPresent", (ALvoid *) alIsExtensionPresent },
|
||||
{ "alGetProcAddress", (ALvoid *) alGetProcAddress },
|
||||
{ "alGetEnumValue", (ALvoid *) alGetEnumValue },
|
||||
{ "alListenerf", (ALvoid *) alListenerf },
|
||||
{ "alListener3f", (ALvoid *) alListener3f },
|
||||
{ "alListenerfv", (ALvoid *) alListenerfv },
|
||||
{ "alListeneri", (ALvoid *) alListeneri },
|
||||
{ "alListener3i", (ALvoid *) alListener3i },
|
||||
{ "alListeneriv", (ALvoid *) alListeneriv },
|
||||
{ "alGetListenerf", (ALvoid *) alGetListenerf },
|
||||
{ "alGetListener3f", (ALvoid *) alGetListener3f },
|
||||
{ "alGetListenerfv", (ALvoid *) alGetListenerfv },
|
||||
{ "alGetListeneri", (ALvoid *) alGetListeneri },
|
||||
{ "alGetListener3i", (ALvoid *) alGetListener3i },
|
||||
{ "alGetListeneriv", (ALvoid *) alGetListeneriv },
|
||||
{ "alGenSources", (ALvoid *) alGenSources },
|
||||
{ "alDeleteSources", (ALvoid *) alDeleteSources },
|
||||
{ "alIsSource", (ALvoid *) alIsSource },
|
||||
{ "alSourcef", (ALvoid *) alSourcef },
|
||||
{ "alSource3f", (ALvoid *) alSource3f },
|
||||
{ "alSourcefv", (ALvoid *) alSourcefv },
|
||||
{ "alSourcei", (ALvoid *) alSourcei },
|
||||
{ "alSource3i", (ALvoid *) alSource3i },
|
||||
{ "alSourceiv", (ALvoid *) alSourceiv },
|
||||
{ "alGetSourcef", (ALvoid *) alGetSourcef },
|
||||
{ "alGetSource3f", (ALvoid *) alGetSource3f },
|
||||
{ "alGetSourcefv", (ALvoid *) alGetSourcefv },
|
||||
{ "alGetSourcei", (ALvoid *) alGetSourcei },
|
||||
{ "alGetSource3i", (ALvoid *) alGetSource3i },
|
||||
{ "alGetSourceiv", (ALvoid *) alGetSourceiv },
|
||||
{ "alSourcePlayv", (ALvoid *) alSourcePlayv },
|
||||
{ "alSourceStopv", (ALvoid *) alSourceStopv },
|
||||
{ "alSourceRewindv", (ALvoid *) alSourceRewindv },
|
||||
{ "alSourcePausev", (ALvoid *) alSourcePausev },
|
||||
{ "alSourcePlay", (ALvoid *) alSourcePlay },
|
||||
{ "alSourceStop", (ALvoid *) alSourceStop },
|
||||
{ "alSourceRewind", (ALvoid *) alSourceRewind },
|
||||
{ "alSourcePause", (ALvoid *) alSourcePause },
|
||||
{ "alSourceQueueBuffers", (ALvoid *) alSourceQueueBuffers },
|
||||
{ "alSourceUnqueueBuffers", (ALvoid *) alSourceUnqueueBuffers },
|
||||
{ "alGenBuffers", (ALvoid *) alGenBuffers },
|
||||
{ "alDeleteBuffers", (ALvoid *) alDeleteBuffers },
|
||||
{ "alIsBuffer", (ALvoid *) alIsBuffer },
|
||||
{ "alBufferData", (ALvoid *) alBufferData },
|
||||
{ "alBufferf", (ALvoid *) alBufferf },
|
||||
{ "alBuffer3f", (ALvoid *) alBuffer3f },
|
||||
{ "alBufferfv", (ALvoid *) alBufferfv },
|
||||
{ "alBufferi", (ALvoid *) alBufferi },
|
||||
{ "alBuffer3i", (ALvoid *) alBuffer3i },
|
||||
{ "alBufferiv", (ALvoid *) alBufferiv },
|
||||
{ "alGetBufferf", (ALvoid *) alGetBufferf },
|
||||
{ "alGetBuffer3f", (ALvoid *) alGetBuffer3f },
|
||||
{ "alGetBufferfv", (ALvoid *) alGetBufferfv },
|
||||
{ "alGetBufferi", (ALvoid *) alGetBufferi },
|
||||
{ "alGetBuffer3i", (ALvoid *) alGetBuffer3i },
|
||||
{ "alGetBufferiv", (ALvoid *) alGetBufferiv },
|
||||
{ "alDopplerFactor", (ALvoid *) alDopplerFactor },
|
||||
{ "alDopplerVelocity", (ALvoid *) alDopplerVelocity },
|
||||
{ "alSpeedOfSound", (ALvoid *) alSpeedOfSound },
|
||||
{ "alDistanceModel", (ALvoid *) alDistanceModel },
|
||||
|
||||
{ "alGenFilters", (ALvoid *) alGenFilters },
|
||||
{ "alDeleteFilters", (ALvoid *) alDeleteFilters },
|
||||
{ "alIsFilter", (ALvoid *) alIsFilter },
|
||||
{ "alFilteri", (ALvoid *) alFilteri },
|
||||
{ "alFilteriv", (ALvoid *) alFilteriv },
|
||||
{ "alFilterf", (ALvoid *) alFilterf },
|
||||
{ "alFilterfv", (ALvoid *) alFilterfv },
|
||||
{ "alGetFilteri", (ALvoid *) alGetFilteri },
|
||||
{ "alGetFilteriv", (ALvoid *) alGetFilteriv },
|
||||
{ "alGetFilterf", (ALvoid *) alGetFilterf },
|
||||
{ "alGetFilterfv", (ALvoid *) alGetFilterfv },
|
||||
|
||||
{ "alGenEffects", (ALvoid *) alGenEffects },
|
||||
{ "alDeleteEffects", (ALvoid *) alDeleteEffects },
|
||||
{ "alIsEffect", (ALvoid *) alIsEffect },
|
||||
{ "alEffecti", (ALvoid *) alEffecti },
|
||||
{ "alEffectiv", (ALvoid *) alEffectiv },
|
||||
{ "alEffectf", (ALvoid *) alEffectf },
|
||||
{ "alEffectfv", (ALvoid *) alEffectfv },
|
||||
{ "alGetEffecti", (ALvoid *) alGetEffecti },
|
||||
{ "alGetEffectiv", (ALvoid *) alGetEffectiv },
|
||||
{ "alGetEffectf", (ALvoid *) alGetEffectf },
|
||||
{ "alGetEffectfv", (ALvoid *) alGetEffectfv },
|
||||
|
||||
{ "alGenAuxiliaryEffectSlots", (ALvoid *) alGenAuxiliaryEffectSlots },
|
||||
{ "alDeleteAuxiliaryEffectSlots",(ALvoid *) alDeleteAuxiliaryEffectSlots},
|
||||
{ "alIsAuxiliaryEffectSlot", (ALvoid *) alIsAuxiliaryEffectSlot },
|
||||
{ "alAuxiliaryEffectSloti", (ALvoid *) alAuxiliaryEffectSloti },
|
||||
{ "alAuxiliaryEffectSlotiv", (ALvoid *) alAuxiliaryEffectSlotiv },
|
||||
{ "alAuxiliaryEffectSlotf", (ALvoid *) alAuxiliaryEffectSlotf },
|
||||
{ "alAuxiliaryEffectSlotfv", (ALvoid *) alAuxiliaryEffectSlotfv },
|
||||
{ "alGetAuxiliaryEffectSloti", (ALvoid *) alGetAuxiliaryEffectSloti },
|
||||
{ "alGetAuxiliaryEffectSlotiv", (ALvoid *) alGetAuxiliaryEffectSlotiv},
|
||||
{ "alGetAuxiliaryEffectSlotf", (ALvoid *) alGetAuxiliaryEffectSlotf },
|
||||
{ "alGetAuxiliaryEffectSlotfv", (ALvoid *) alGetAuxiliaryEffectSlotfv},
|
||||
|
||||
{ "alBufferSubDataEXT", (ALvoid *) alBufferSubDataEXT },
|
||||
|
||||
{ "alGenDatabuffersEXT", (ALvoid *) alGenDatabuffersEXT },
|
||||
{ "alDeleteDatabuffersEXT", (ALvoid *) alDeleteDatabuffersEXT },
|
||||
{ "alIsDatabufferEXT", (ALvoid *) alIsDatabufferEXT },
|
||||
{ "alDatabufferDataEXT", (ALvoid *) alDatabufferDataEXT },
|
||||
{ "alDatabufferSubDataEXT", (ALvoid *) alDatabufferSubDataEXT },
|
||||
{ "alGetDatabufferSubDataEXT", (ALvoid *) alGetDatabufferSubDataEXT },
|
||||
{ "alDatabufferfEXT", (ALvoid *) alDatabufferfEXT },
|
||||
{ "alDatabufferfvEXT", (ALvoid *) alDatabufferfvEXT },
|
||||
{ "alDatabufferiEXT", (ALvoid *) alDatabufferiEXT },
|
||||
{ "alDatabufferivEXT", (ALvoid *) alDatabufferivEXT },
|
||||
{ "alGetDatabufferfEXT", (ALvoid *) alGetDatabufferfEXT },
|
||||
{ "alGetDatabufferfvEXT", (ALvoid *) alGetDatabufferfvEXT },
|
||||
{ "alGetDatabufferiEXT", (ALvoid *) alGetDatabufferiEXT },
|
||||
{ "alGetDatabufferivEXT", (ALvoid *) alGetDatabufferivEXT },
|
||||
{ "alSelectDatabufferEXT", (ALvoid *) alSelectDatabufferEXT },
|
||||
{ "alMapDatabufferEXT", (ALvoid *) alMapDatabufferEXT },
|
||||
{ "alUnmapDatabufferEXT", (ALvoid *) alUnmapDatabufferEXT },
|
||||
|
||||
{ NULL, (ALvoid *) NULL } };
|
||||
|
||||
static ALenums enumeration[]={
|
||||
// Types
|
||||
{ (ALchar *)"AL_INVALID", AL_INVALID },
|
||||
{ (ALchar *)"AL_NONE", AL_NONE },
|
||||
{ (ALchar *)"AL_FALSE", AL_FALSE },
|
||||
{ (ALchar *)"AL_TRUE", AL_TRUE },
|
||||
|
||||
// Source and Listener Properties
|
||||
{ (ALchar *)"AL_SOURCE_RELATIVE", AL_SOURCE_RELATIVE },
|
||||
{ (ALchar *)"AL_CONE_INNER_ANGLE", AL_CONE_INNER_ANGLE },
|
||||
{ (ALchar *)"AL_CONE_OUTER_ANGLE", AL_CONE_OUTER_ANGLE },
|
||||
{ (ALchar *)"AL_PITCH", AL_PITCH },
|
||||
{ (ALchar *)"AL_POSITION", AL_POSITION },
|
||||
{ (ALchar *)"AL_DIRECTION", AL_DIRECTION },
|
||||
{ (ALchar *)"AL_VELOCITY", AL_VELOCITY },
|
||||
{ (ALchar *)"AL_LOOPING", AL_LOOPING },
|
||||
{ (ALchar *)"AL_BUFFER", AL_BUFFER },
|
||||
{ (ALchar *)"AL_GAIN", AL_GAIN },
|
||||
{ (ALchar *)"AL_MIN_GAIN", AL_MIN_GAIN },
|
||||
{ (ALchar *)"AL_MAX_GAIN", AL_MAX_GAIN },
|
||||
{ (ALchar *)"AL_ORIENTATION", AL_ORIENTATION },
|
||||
{ (ALchar *)"AL_REFERENCE_DISTANCE", AL_REFERENCE_DISTANCE },
|
||||
{ (ALchar *)"AL_ROLLOFF_FACTOR", AL_ROLLOFF_FACTOR },
|
||||
{ (ALchar *)"AL_CONE_OUTER_GAIN", AL_CONE_OUTER_GAIN },
|
||||
{ (ALchar *)"AL_MAX_DISTANCE", AL_MAX_DISTANCE },
|
||||
{ (ALchar *)"AL_SEC_OFFSET", AL_SEC_OFFSET },
|
||||
{ (ALchar *)"AL_SAMPLE_OFFSET", AL_SAMPLE_OFFSET },
|
||||
{ (ALchar *)"AL_BYTE_OFFSET", AL_BYTE_OFFSET },
|
||||
{ (ALchar *)"AL_SOURCE_TYPE", AL_SOURCE_TYPE },
|
||||
{ (ALchar *)"AL_STATIC", AL_STATIC },
|
||||
{ (ALchar *)"AL_STREAMING", AL_STREAMING },
|
||||
{ (ALchar *)"AL_UNDETERMINED", AL_UNDETERMINED },
|
||||
{ (ALchar *)"AL_METERS_PER_UNIT", AL_METERS_PER_UNIT },
|
||||
|
||||
// Source EFX Properties
|
||||
{ (ALchar *)"AL_DIRECT_FILTER", AL_DIRECT_FILTER },
|
||||
{ (ALchar *)"AL_AUXILIARY_SEND_FILTER", AL_AUXILIARY_SEND_FILTER },
|
||||
{ (ALchar *)"AL_AIR_ABSORPTION_FACTOR", AL_AIR_ABSORPTION_FACTOR },
|
||||
{ (ALchar *)"AL_ROOM_ROLLOFF_FACTOR", AL_ROOM_ROLLOFF_FACTOR },
|
||||
{ (ALchar *)"AL_CONE_OUTER_GAINHF", AL_CONE_OUTER_GAINHF },
|
||||
{ (ALchar *)"AL_DIRECT_FILTER_GAINHF_AUTO", AL_DIRECT_FILTER_GAINHF_AUTO },
|
||||
{ (ALchar *)"AL_AUXILIARY_SEND_FILTER_GAIN_AUTO", AL_AUXILIARY_SEND_FILTER_GAIN_AUTO },
|
||||
{ (ALchar *)"AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO", AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO},
|
||||
|
||||
|
||||
// Source State information
|
||||
{ (ALchar *)"AL_SOURCE_STATE", AL_SOURCE_STATE },
|
||||
{ (ALchar *)"AL_INITIAL", AL_INITIAL },
|
||||
{ (ALchar *)"AL_PLAYING", AL_PLAYING },
|
||||
{ (ALchar *)"AL_PAUSED", AL_PAUSED },
|
||||
{ (ALchar *)"AL_STOPPED", AL_STOPPED },
|
||||
|
||||
// Queue information
|
||||
{ (ALchar *)"AL_BUFFERS_QUEUED", AL_BUFFERS_QUEUED },
|
||||
{ (ALchar *)"AL_BUFFERS_PROCESSED", AL_BUFFERS_PROCESSED },
|
||||
|
||||
// Buffer Formats
|
||||
{ (ALchar *)"AL_FORMAT_MONO8", AL_FORMAT_MONO8 },
|
||||
{ (ALchar *)"AL_FORMAT_MONO16", AL_FORMAT_MONO16 },
|
||||
{ (ALchar *)"AL_FORMAT_MONO_FLOAT32", AL_FORMAT_MONO_FLOAT32 },
|
||||
{ (ALchar *)"AL_FORMAT_STEREO8", AL_FORMAT_STEREO8 },
|
||||
{ (ALchar *)"AL_FORMAT_STEREO16", AL_FORMAT_STEREO16 },
|
||||
{ (ALchar *)"AL_FORMAT_STEREO_FLOAT32", AL_FORMAT_STEREO_FLOAT32 },
|
||||
{ (ALchar *)"AL_FORMAT_MONO_IMA4", AL_FORMAT_MONO_IMA4 },
|
||||
{ (ALchar *)"AL_FORMAT_STEREO_IMA4", AL_FORMAT_STEREO_IMA4 },
|
||||
{ (ALchar *)"AL_FORMAT_QUAD8_LOKI", AL_FORMAT_QUAD8_LOKI },
|
||||
{ (ALchar *)"AL_FORMAT_QUAD16_LOKI", AL_FORMAT_QUAD16_LOKI },
|
||||
{ (ALchar *)"AL_FORMAT_QUAD8", AL_FORMAT_QUAD8 },
|
||||
{ (ALchar *)"AL_FORMAT_QUAD16", AL_FORMAT_QUAD16 },
|
||||
{ (ALchar *)"AL_FORMAT_QUAD32", AL_FORMAT_QUAD32 },
|
||||
{ (ALchar *)"AL_FORMAT_51CHN8", AL_FORMAT_51CHN8 },
|
||||
{ (ALchar *)"AL_FORMAT_51CHN16", AL_FORMAT_51CHN16 },
|
||||
{ (ALchar *)"AL_FORMAT_51CHN32", AL_FORMAT_51CHN32 },
|
||||
{ (ALchar *)"AL_FORMAT_61CHN8", AL_FORMAT_61CHN8 },
|
||||
{ (ALchar *)"AL_FORMAT_61CHN16", AL_FORMAT_61CHN16 },
|
||||
{ (ALchar *)"AL_FORMAT_61CHN32", AL_FORMAT_61CHN32 },
|
||||
{ (ALchar *)"AL_FORMAT_71CHN8", AL_FORMAT_71CHN8 },
|
||||
{ (ALchar *)"AL_FORMAT_71CHN16", AL_FORMAT_71CHN16 },
|
||||
{ (ALchar *)"AL_FORMAT_71CHN32", AL_FORMAT_71CHN32 },
|
||||
{ (ALchar *)"AL_FORMAT_REAR8", AL_FORMAT_REAR8 },
|
||||
{ (ALchar *)"AL_FORMAT_REAR16", AL_FORMAT_REAR16 },
|
||||
{ (ALchar *)"AL_FORMAT_REAR32", AL_FORMAT_REAR32 },
|
||||
|
||||
// Buffer attributes
|
||||
{ (ALchar *)"AL_FREQUENCY", AL_FREQUENCY },
|
||||
{ (ALchar *)"AL_BITS", AL_BITS },
|
||||
{ (ALchar *)"AL_CHANNELS", AL_CHANNELS },
|
||||
{ (ALchar *)"AL_SIZE", AL_SIZE },
|
||||
|
||||
// Buffer States (not supported yet)
|
||||
{ (ALchar *)"AL_UNUSED", AL_UNUSED },
|
||||
{ (ALchar *)"AL_PENDING", AL_PENDING },
|
||||
{ (ALchar *)"AL_PROCESSED", AL_PROCESSED },
|
||||
|
||||
// AL Error Messages
|
||||
{ (ALchar *)"AL_NO_ERROR", AL_NO_ERROR },
|
||||
{ (ALchar *)"AL_INVALID_NAME", AL_INVALID_NAME },
|
||||
{ (ALchar *)"AL_INVALID_ENUM", AL_INVALID_ENUM },
|
||||
{ (ALchar *)"AL_INVALID_VALUE", AL_INVALID_VALUE },
|
||||
{ (ALchar *)"AL_INVALID_OPERATION", AL_INVALID_OPERATION },
|
||||
{ (ALchar *)"AL_OUT_OF_MEMORY", AL_OUT_OF_MEMORY },
|
||||
|
||||
// Context strings
|
||||
{ (ALchar *)"AL_VENDOR", AL_VENDOR },
|
||||
{ (ALchar *)"AL_VERSION", AL_VERSION },
|
||||
{ (ALchar *)"AL_RENDERER", AL_RENDERER },
|
||||
{ (ALchar *)"AL_EXTENSIONS", AL_EXTENSIONS },
|
||||
|
||||
// Global states
|
||||
{ (ALchar *)"AL_DOPPLER_FACTOR", AL_DOPPLER_FACTOR },
|
||||
{ (ALchar *)"AL_DOPPLER_VELOCITY", AL_DOPPLER_VELOCITY },
|
||||
{ (ALchar *)"AL_DISTANCE_MODEL", AL_DISTANCE_MODEL },
|
||||
{ (ALchar *)"AL_SPEED_OF_SOUND", AL_SPEED_OF_SOUND },
|
||||
|
||||
// Distance Models
|
||||
{ (ALchar *)"AL_INVERSE_DISTANCE", AL_INVERSE_DISTANCE },
|
||||
{ (ALchar *)"AL_INVERSE_DISTANCE_CLAMPED", AL_INVERSE_DISTANCE_CLAMPED },
|
||||
{ (ALchar *)"AL_LINEAR_DISTANCE", AL_LINEAR_DISTANCE },
|
||||
{ (ALchar *)"AL_LINEAR_DISTANCE_CLAMPED", AL_LINEAR_DISTANCE_CLAMPED },
|
||||
{ (ALchar *)"AL_EXPONENT_DISTANCE", AL_EXPONENT_DISTANCE },
|
||||
{ (ALchar *)"AL_EXPONENT_DISTANCE_CLAMPED", AL_EXPONENT_DISTANCE_CLAMPED },
|
||||
|
||||
// Filter types
|
||||
{ (ALchar *)"AL_FILTER_TYPE", AL_FILTER_TYPE },
|
||||
{ (ALchar *)"AL_FILTER_NULL", AL_FILTER_NULL },
|
||||
{ (ALchar *)"AL_FILTER_LOWPASS", AL_FILTER_LOWPASS },
|
||||
{ (ALchar *)"AL_FILTER_HIGHPASS", AL_FILTER_HIGHPASS },
|
||||
{ (ALchar *)"AL_FILTER_BANDPASS", AL_FILTER_BANDPASS },
|
||||
|
||||
// Filter params
|
||||
{ (ALchar *)"AL_LOWPASS_GAIN", AL_LOWPASS_GAIN },
|
||||
{ (ALchar *)"AL_LOWPASS_GAINHF", AL_LOWPASS_GAINHF },
|
||||
|
||||
// Effect types
|
||||
{ (ALchar *)"AL_EFFECT_TYPE", AL_EFFECT_TYPE },
|
||||
{ (ALchar *)"AL_EFFECT_NULL", AL_EFFECT_NULL },
|
||||
{ (ALchar *)"AL_EFFECT_REVERB", AL_EFFECT_REVERB },
|
||||
{ (ALchar *)"AL_EFFECT_CHORUS", AL_EFFECT_CHORUS },
|
||||
{ (ALchar *)"AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION },
|
||||
{ (ALchar *)"AL_EFFECT_ECHO", AL_EFFECT_ECHO },
|
||||
{ (ALchar *)"AL_EFFECT_FLANGER", AL_EFFECT_FLANGER },
|
||||
{ (ALchar *)"AL_EFFECT_FREQUENCY_SHIFTER", AL_EFFECT_FREQUENCY_SHIFTER },
|
||||
{ (ALchar *)"AL_EFFECT_VOCAL_MORPHER", AL_EFFECT_VOCAL_MORPHER },
|
||||
{ (ALchar *)"AL_EFFECT_PITCH_SHIFTER", AL_EFFECT_PITCH_SHIFTER },
|
||||
{ (ALchar *)"AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
|
||||
{ (ALchar *)"AL_EFFECT_AUTOWAH", AL_EFFECT_AUTOWAH },
|
||||
{ (ALchar *)"AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR },
|
||||
{ (ALchar *)"AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER },
|
||||
|
||||
// Reverb params
|
||||
{ (ALchar *)"AL_REVERB_DENSITY", AL_REVERB_DENSITY },
|
||||
{ (ALchar *)"AL_REVERB_DIFFUSION", AL_REVERB_DIFFUSION },
|
||||
{ (ALchar *)"AL_REVERB_GAIN", AL_REVERB_GAIN },
|
||||
{ (ALchar *)"AL_REVERB_GAINHF", AL_REVERB_GAINHF },
|
||||
{ (ALchar *)"AL_REVERB_DECAY_TIME", AL_REVERB_DECAY_TIME },
|
||||
{ (ALchar *)"AL_REVERB_DECAY_HFRATIO", AL_REVERB_DECAY_HFRATIO },
|
||||
{ (ALchar *)"AL_REVERB_REFLECTIONS_GAIN", AL_REVERB_REFLECTIONS_GAIN },
|
||||
{ (ALchar *)"AL_REVERB_REFLECTIONS_DELAY", AL_REVERB_REFLECTIONS_DELAY },
|
||||
{ (ALchar *)"AL_REVERB_LATE_REVERB_GAIN", AL_REVERB_LATE_REVERB_GAIN },
|
||||
{ (ALchar *)"AL_REVERB_LATE_REVERB_DELAY", AL_REVERB_LATE_REVERB_DELAY },
|
||||
{ (ALchar *)"AL_REVERB_AIR_ABSORPTION_GAINHF", AL_REVERB_AIR_ABSORPTION_GAINHF },
|
||||
{ (ALchar *)"AL_REVERB_ROOM_ROLLOFF_FACTOR", AL_REVERB_ROOM_ROLLOFF_FACTOR },
|
||||
{ (ALchar *)"AL_REVERB_DECAY_HFLIMIT", AL_REVERB_DECAY_HFLIMIT },
|
||||
|
||||
|
||||
// Default
|
||||
{ (ALchar *)NULL, (ALenum)0 }
|
||||
const struct EffectList EffectList[] = {
|
||||
{ "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB },
|
||||
{ "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB },
|
||||
#if 0
|
||||
{ "autowah", AUTOWAH, "AL_EFFECT_AUTOWAH", AL_EFFECT_AUTOWAH },
|
||||
#endif
|
||||
{ "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS },
|
||||
{ "compressor", COMPRESSOR, "AL_EFFECT_COMPRESSOR", AL_EFFECT_COMPRESSOR },
|
||||
{ "distortion", DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION },
|
||||
{ "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO },
|
||||
{ "equalizer", EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER },
|
||||
{ "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER },
|
||||
{ "modulator", MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR },
|
||||
{ "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
||||
{ "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE },
|
||||
{ NULL, 0, NULL, (ALenum)0 }
|
||||
};
|
||||
|
||||
|
||||
|
||||
ALAPI ALboolean ALAPIENTRY alIsExtensionPresent(const ALchar *extName)
|
||||
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
|
||||
{
|
||||
ALboolean bIsSupported = AL_FALSE;
|
||||
ALCcontext *pContext;
|
||||
ALboolean ret = AL_FALSE;
|
||||
ALCcontext *context;
|
||||
const char *ptr;
|
||||
size_t len;
|
||||
|
||||
if (!extName)
|
||||
{
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
return AL_FALSE;
|
||||
}
|
||||
context = GetContextRef();
|
||||
if(!context) return AL_FALSE;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return AL_FALSE;
|
||||
if(!(extName))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
len = strlen(extName);
|
||||
ptr = pContext->ExtensionList;
|
||||
ptr = context->ExtensionList;
|
||||
while(ptr && *ptr)
|
||||
{
|
||||
if(strncasecmp(ptr, extName, len) == 0 &&
|
||||
(ptr[len] == '\0' || isspace(ptr[len])))
|
||||
{
|
||||
bIsSupported = AL_TRUE;
|
||||
ret = AL_TRUE;
|
||||
break;
|
||||
}
|
||||
if((ptr=strchr(ptr, ' ')) != NULL)
|
||||
@@ -373,31 +85,22 @@ ALAPI ALboolean ALAPIENTRY alIsExtensionPresent(const ALchar *extName)
|
||||
}
|
||||
}
|
||||
|
||||
ProcessContext(pContext);
|
||||
|
||||
return bIsSupported;
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
ALAPI ALvoid * ALAPIENTRY alGetProcAddress(const ALchar *funcName)
|
||||
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
|
||||
{
|
||||
ALsizei i = 0;
|
||||
|
||||
while(function[i].funcName &&
|
||||
strcmp((char*)function[i].funcName, (char*)funcName) != 0)
|
||||
i++;
|
||||
|
||||
return function[i].address;
|
||||
if(!funcName)
|
||||
return NULL;
|
||||
return alcGetProcAddress(NULL, funcName);
|
||||
}
|
||||
|
||||
/* NOTE: This function must be able to run without a context! */
|
||||
ALAPI ALenum ALAPIENTRY alGetEnumValue(const ALchar *enumName)
|
||||
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
|
||||
{
|
||||
ALsizei i = 0;
|
||||
|
||||
while(enumeration[i].enumName &&
|
||||
strcmp(enumeration[i].enumName, enumName) != 0)
|
||||
i++;
|
||||
|
||||
return enumeration[i].value;
|
||||
if(!enumName)
|
||||
return (ALenum)0;
|
||||
return alcGetEnumValue(NULL, enumName);
|
||||
}
|
||||
|
||||
+554
-278
@@ -22,404 +22,680 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alFilter.h"
|
||||
#include "alThunk.h"
|
||||
#include "alError.h"
|
||||
|
||||
|
||||
extern inline struct ALfilter *LookupFilter(ALCdevice *device, ALuint id);
|
||||
extern inline struct ALfilter *RemoveFilter(ALCdevice *device, ALuint id);
|
||||
extern inline ALfloat ALfilterState_processSingle(ALfilterState *filter, ALfloat sample);
|
||||
|
||||
static void InitFilterParams(ALfilter *filter, ALenum type);
|
||||
|
||||
|
||||
ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
|
||||
AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALsizei i;
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsizei cur = 0;
|
||||
ALenum err;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if (n > 0)
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALCdevice *device = Context->Device;
|
||||
|
||||
// Check that enough memory has been allocted in the 'filters' array for n Filters
|
||||
if (!IsBadWritePtr((void*)filters, n * sizeof(ALuint)))
|
||||
ALfilter *filter = calloc(1, sizeof(ALfilter));
|
||||
if(!filter)
|
||||
{
|
||||
ALfilter **list = &device->FilterList;
|
||||
while(*list)
|
||||
list = &(*list)->next;
|
||||
|
||||
i = 0;
|
||||
while(i < n)
|
||||
{
|
||||
*list = calloc(1, sizeof(ALfilter));
|
||||
if(!(*list))
|
||||
{
|
||||
// We must have run out or memory
|
||||
alDeleteFilters(i, filters);
|
||||
alSetError(AL_OUT_OF_MEMORY);
|
||||
break;
|
||||
}
|
||||
|
||||
filters[i] = (ALuint)ALTHUNK_ADDENTRY(*list);
|
||||
(*list)->filter = filters[i];
|
||||
|
||||
InitFilterParams(*list, AL_FILTER_NULL);
|
||||
device->FilterCount++;
|
||||
i++;
|
||||
|
||||
list = &(*list)->next;
|
||||
}
|
||||
alDeleteFilters(cur, filters);
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
}
|
||||
InitFilterParams(filter, AL_FILTER_NULL);
|
||||
|
||||
err = NewThunkEntry(&filter->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntry(&device->FilterMap, filter->id, filter);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
FreeThunkEntry(filter->id);
|
||||
memset(filter, 0, sizeof(ALfilter));
|
||||
free(filter);
|
||||
|
||||
alDeleteFilters(cur, filters);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
}
|
||||
|
||||
filters[cur] = filter->id;
|
||||
}
|
||||
|
||||
ProcessContext(Context);
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, ALuint *filters)
|
||||
AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALfilter *ALFilter;
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALfilter *filter;
|
||||
ALsizei i;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if (n >= 0)
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
ALCdevice *device = Context->Device;
|
||||
|
||||
// Check that all filters are valid
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (!alIsFilter(filters[i]))
|
||||
{
|
||||
alSetError(AL_INVALID_NAME);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == n)
|
||||
{
|
||||
// All filters are valid
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
// Recheck that the filter is valid, because there could be duplicated names
|
||||
if (filters[i] && alIsFilter(filters[i]))
|
||||
{
|
||||
ALfilter **list;
|
||||
|
||||
ALFilter = ((ALfilter*)ALTHUNK_LOOKUPENTRY(filters[i]));
|
||||
|
||||
// Remove Source from list of Sources
|
||||
list = &device->FilterList;
|
||||
while(*list && *list != ALFilter)
|
||||
list = &(*list)->next;
|
||||
|
||||
if(*list)
|
||||
*list = (*list)->next;
|
||||
ALTHUNK_REMOVEENTRY(ALFilter->filter);
|
||||
|
||||
memset(ALFilter, 0, sizeof(ALfilter));
|
||||
free(ALFilter);
|
||||
|
||||
device->FilterCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(filters[i] && LookupFilter(device, filters[i]) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if((filter=RemoveFilter(device, filters[i])) == NULL)
|
||||
continue;
|
||||
FreeThunkEntry(filter->id);
|
||||
|
||||
ProcessContext(Context);
|
||||
memset(filter, 0, sizeof(*filter));
|
||||
free(filter);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
ALboolean AL_APIENTRY alIsFilter(ALuint filter)
|
||||
AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALfilter *list;
|
||||
ALboolean result;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
Context = GetContextRef();
|
||||
if(!Context) return AL_FALSE;
|
||||
|
||||
list = Context->Device->FilterList;
|
||||
while(list && list->filter != filter)
|
||||
list = list->next;
|
||||
result = ((!filter || LookupFilter(Context->Device, filter)) ?
|
||||
AL_TRUE : AL_FALSE);
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(Context);
|
||||
|
||||
return ((list || !filter) ? AL_TRUE : AL_FALSE);
|
||||
return result;
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue)
|
||||
AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *Device;
|
||||
ALfilter *ALFilter;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
Context = GetContextRef();
|
||||
if(!Context) return;
|
||||
|
||||
if (filter && alIsFilter(filter))
|
||||
Device = Context->Device;
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
else
|
||||
{
|
||||
ALfilter *ALFilter = (ALfilter*)ALTHUNK_LOOKUPENTRY(filter);
|
||||
|
||||
switch(param)
|
||||
if(param == AL_FILTER_TYPE)
|
||||
{
|
||||
case AL_FILTER_TYPE:
|
||||
if(iValue == AL_FILTER_NULL ||
|
||||
iValue == AL_FILTER_LOWPASS)
|
||||
InitFilterParams(ALFilter, iValue);
|
||||
if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS ||
|
||||
value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
|
||||
InitFilterParams(ALFilter, value);
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
alSetError(Context, AL_INVALID_VALUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_SetParami(ALFilter, Context, param, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, ALint *piValues)
|
||||
AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *Device;
|
||||
ALfilter *ALFilter;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
if(!Context) return;
|
||||
|
||||
if (filter && alIsFilter(filter))
|
||||
switch(param)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FILTER_TYPE:
|
||||
alFilteri(filter, param, piValues[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
alFilteri(filter, param, values[0]);
|
||||
return;
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
Context = GetContextRef();
|
||||
if(!Context) return;
|
||||
|
||||
if (filter && alIsFilter(filter))
|
||||
{
|
||||
ALfilter *ALFilter = (ALfilter*)ALTHUNK_LOOKUPENTRY(filter);
|
||||
|
||||
switch(ALFilter->type)
|
||||
{
|
||||
case AL_FILTER_LOWPASS:
|
||||
switch(param)
|
||||
{
|
||||
case AL_LOWPASS_GAIN:
|
||||
if(flValue >= 0.0f && flValue <= 1.0f)
|
||||
ALFilter->Gain = flValue;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
case AL_LOWPASS_GAINHF:
|
||||
if(flValue >= 0.0f && flValue <= 1.0f)
|
||||
ALFilter->GainHF = flValue;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Device = Context->Device;
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_SetParamiv(ALFilter, Context, param, values);
|
||||
}
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, ALfloat *pflValues)
|
||||
AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *Device;
|
||||
ALfilter *ALFilter;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
Context = GetContextRef();
|
||||
if(!Context) return;
|
||||
|
||||
if (filter && alIsFilter(filter))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alFilterf(filter, param, pflValues[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Device = Context->Device;
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_SetParamf(ALFilter, Context, param, value);
|
||||
}
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue)
|
||||
AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *Device;
|
||||
ALfilter *ALFilter;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
Context = GetContextRef();
|
||||
if(!Context) return;
|
||||
|
||||
if (filter && alIsFilter(filter))
|
||||
Device = Context->Device;
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
else
|
||||
{
|
||||
ALfilter *ALFilter = (ALfilter*)ALTHUNK_LOOKUPENTRY(filter);
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_SetParamfv(ALFilter, Context, param, values);
|
||||
}
|
||||
|
||||
switch(param)
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *Device;
|
||||
ALfilter *ALFilter;
|
||||
|
||||
Context = GetContextRef();
|
||||
if(!Context) return;
|
||||
|
||||
Device = Context->Device;
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
else
|
||||
{
|
||||
if(param == AL_FILTER_TYPE)
|
||||
*value = ALFilter->type;
|
||||
else
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_GetParami(ALFilter, Context, param, value);
|
||||
}
|
||||
}
|
||||
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *Device;
|
||||
ALfilter *ALFilter;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_FILTER_TYPE:
|
||||
*piValue = ALFilter->type;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
alGetFilteri(filter, param, values);
|
||||
return;
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
|
||||
ProcessContext(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
Context = GetContextRef();
|
||||
if(!Context) return;
|
||||
|
||||
if (filter && alIsFilter(filter))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FILTER_TYPE:
|
||||
alGetFilteri(filter, param, piValues);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Device = Context->Device;
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_GetParamiv(ALFilter, Context, param, values);
|
||||
}
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue)
|
||||
AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *Device;
|
||||
ALfilter *ALFilter;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
Context = GetContextRef();
|
||||
if(!Context) return;
|
||||
|
||||
if (filter && alIsFilter(filter))
|
||||
{
|
||||
ALfilter *ALFilter = (ALfilter*)ALTHUNK_LOOKUPENTRY(filter);
|
||||
|
||||
switch(ALFilter->type)
|
||||
{
|
||||
case AL_FILTER_LOWPASS:
|
||||
switch(param)
|
||||
{
|
||||
case AL_LOWPASS_GAIN:
|
||||
*pflValue = ALFilter->Gain;
|
||||
break;
|
||||
|
||||
case AL_LOWPASS_GAINHF:
|
||||
*pflValue = ALFilter->GainHF;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Device = Context->Device;
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_GetParamf(ALFilter, Context, param, value);
|
||||
}
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues)
|
||||
AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
|
||||
{
|
||||
ALCcontext *Context;
|
||||
ALCdevice *Device;
|
||||
ALfilter *ALFilter;
|
||||
|
||||
Context = GetContextSuspended();
|
||||
Context = GetContextRef();
|
||||
if(!Context) return;
|
||||
|
||||
if (filter && alIsFilter(filter))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alGetFilterf(filter, param, pflValues);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Device = Context->Device;
|
||||
if((ALFilter=LookupFilter(Device, filter)) == NULL)
|
||||
alSetError(Context, AL_INVALID_NAME);
|
||||
else
|
||||
alSetError(AL_INVALID_NAME);
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALfilter_GetParamfv(ALFilter, Context, param, values);
|
||||
}
|
||||
|
||||
ProcessContext(Context);
|
||||
ALCcontext_DecRef(Context);
|
||||
}
|
||||
|
||||
|
||||
void ALfilterState_clear(ALfilterState *filter)
|
||||
{
|
||||
filter->x[0] = 0.0f;
|
||||
filter->x[1] = 0.0f;
|
||||
filter->y[0] = 0.0f;
|
||||
filter->y[1] = 0.0f;
|
||||
}
|
||||
|
||||
void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat gain, ALfloat freq_mult, ALfloat bandwidth)
|
||||
{
|
||||
ALfloat alpha;
|
||||
ALfloat w0;
|
||||
|
||||
// Limit gain to -100dB
|
||||
gain = maxf(gain, 0.00001f);
|
||||
|
||||
w0 = F_2PI * freq_mult;
|
||||
|
||||
/* Calculate filter coefficients depending on filter type */
|
||||
switch(type)
|
||||
{
|
||||
case ALfilterType_HighShelf:
|
||||
alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f);
|
||||
filter->b[0] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha);
|
||||
filter->b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cosf(w0) );
|
||||
filter->b[2] = gain*((gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha);
|
||||
filter->a[0] = (gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha;
|
||||
filter->a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cosf(w0) );
|
||||
filter->a[2] = (gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha;
|
||||
break;
|
||||
case ALfilterType_LowShelf:
|
||||
alpha = sinf(w0)/2.0f*sqrtf((gain + 1.0f/gain)*(1.0f/0.75f - 1.0f) + 2.0f);
|
||||
filter->b[0] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha);
|
||||
filter->b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cosf(w0) );
|
||||
filter->b[2] = gain*((gain+1.0f) - (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha);
|
||||
filter->a[0] = (gain+1.0f) + (gain-1.0f)*cosf(w0) + 2.0f*sqrtf(gain)*alpha;
|
||||
filter->a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cosf(w0) );
|
||||
filter->a[2] = (gain+1.0f) + (gain-1.0f)*cosf(w0) - 2.0f*sqrtf(gain)*alpha;
|
||||
break;
|
||||
case ALfilterType_Peaking:
|
||||
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
|
||||
filter->b[0] = 1.0f + alpha * gain;
|
||||
filter->b[1] = -2.0f * cosf(w0);
|
||||
filter->b[2] = 1.0f - alpha * gain;
|
||||
filter->a[0] = 1.0f + alpha / gain;
|
||||
filter->a[1] = -2.0f * cosf(w0);
|
||||
filter->a[2] = 1.0f - alpha / gain;
|
||||
break;
|
||||
|
||||
case ALfilterType_LowPass:
|
||||
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
|
||||
filter->b[0] = (1.0f - cosf(w0)) / 2.0f;
|
||||
filter->b[1] = 1.0f - cosf(w0);
|
||||
filter->b[2] = (1.0f - cosf(w0)) / 2.0f;
|
||||
filter->a[0] = 1.0f + alpha;
|
||||
filter->a[1] = -2.0f * cosf(w0);
|
||||
filter->a[2] = 1.0f - alpha;
|
||||
break;
|
||||
case ALfilterType_HighPass:
|
||||
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
|
||||
filter->b[0] = (1.0f + cosf(w0)) / 2.0f;
|
||||
filter->b[1] = 1.0f + cosf(w0);
|
||||
filter->b[2] = (1.0f + cosf(w0)) / 2.0f;
|
||||
filter->a[0] = 1.0f + alpha;
|
||||
filter->a[1] = -2.0f * cosf(w0);
|
||||
filter->a[2] = 1.0f - alpha;
|
||||
break;
|
||||
case ALfilterType_BandPass:
|
||||
alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
|
||||
filter->b[0] = alpha;
|
||||
filter->b[1] = 0;
|
||||
filter->b[2] = -alpha;
|
||||
filter->a[0] = 1.0f + alpha;
|
||||
filter->a[1] = -2.0f * cosf(w0);
|
||||
filter->a[2] = 1.0f - alpha;
|
||||
break;
|
||||
}
|
||||
|
||||
filter->b[2] /= filter->a[0];
|
||||
filter->b[1] /= filter->a[0];
|
||||
filter->b[0] /= filter->a[0];
|
||||
filter->a[2] /= filter->a[0];
|
||||
filter->a[1] /= filter->a[0];
|
||||
filter->a[0] /= filter->a[0];
|
||||
|
||||
filter->process = ALfilterState_processC;
|
||||
}
|
||||
|
||||
|
||||
static void lp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void lp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void lp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_LOWPASS_GAIN:
|
||||
if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
||||
case AL_LOWPASS_GAINHF:
|
||||
if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
filter->GainHF = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
static void lp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
lp_SetParamf(filter, context, param, vals[0]);
|
||||
}
|
||||
|
||||
static void lp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void lp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void lp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_LOWPASS_GAIN:
|
||||
*val = filter->Gain;
|
||||
break;
|
||||
|
||||
case AL_LOWPASS_GAINHF:
|
||||
*val = filter->GainHF;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
static void lp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
lp_GetParamf(filter, context, param, vals);
|
||||
}
|
||||
|
||||
|
||||
static void hp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void hp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void hp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_HIGHPASS_GAIN:
|
||||
if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
||||
case AL_HIGHPASS_GAINLF:
|
||||
if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
filter->GainLF = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
static void hp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
hp_SetParamf(filter, context, param, vals[0]);
|
||||
}
|
||||
|
||||
static void hp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void hp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void hp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_HIGHPASS_GAIN:
|
||||
*val = filter->Gain;
|
||||
break;
|
||||
|
||||
case AL_HIGHPASS_GAINLF:
|
||||
*val = filter->GainLF;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
static void hp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
hp_GetParamf(filter, context, param, vals);
|
||||
}
|
||||
|
||||
|
||||
static void bp_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void bp_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void bp_SetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_BANDPASS_GAIN:
|
||||
if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
||||
case AL_BANDPASS_GAINHF:
|
||||
if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
filter->GainHF = val;
|
||||
break;
|
||||
|
||||
case AL_BANDPASS_GAINLF:
|
||||
if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
|
||||
filter->GainLF = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
static void bp_SetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
bp_SetParamf(filter, context, param, vals[0]);
|
||||
}
|
||||
|
||||
static void bp_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void bp_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void bp_GetParamf(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_BANDPASS_GAIN:
|
||||
*val = filter->Gain;
|
||||
break;
|
||||
|
||||
case AL_BANDPASS_GAINHF:
|
||||
*val = filter->GainHF;
|
||||
break;
|
||||
|
||||
case AL_BANDPASS_GAINLF:
|
||||
*val = filter->GainLF;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
|
||||
}
|
||||
}
|
||||
static void bp_GetParamfv(ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
bp_GetParamf(filter, context, param, vals);
|
||||
}
|
||||
|
||||
|
||||
static void null_SetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_SetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_SetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_SetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), const ALfloat *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
|
||||
static void null_GetParami(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_GetParamiv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_GetParamf(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(val))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
static void null_GetParamfv(ALfilter *UNUSED(filter), ALCcontext *context, ALenum UNUSED(param), ALfloat *UNUSED(vals))
|
||||
{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
|
||||
|
||||
|
||||
ALvoid ReleaseALFilters(ALCdevice *device)
|
||||
{
|
||||
ALfilter *list = device->FilterList;
|
||||
while(list)
|
||||
ALsizei i;
|
||||
for(i = 0;i < device->FilterMap.size;i++)
|
||||
{
|
||||
ALfilter *temp = list;
|
||||
list = list->next;
|
||||
ALfilter *temp = device->FilterMap.array[i].value;
|
||||
device->FilterMap.array[i].value = NULL;
|
||||
|
||||
// Release filter structure
|
||||
FreeThunkEntry(temp->id);
|
||||
memset(temp, 0, sizeof(ALfilter));
|
||||
free(temp);
|
||||
}
|
||||
device->FilterList = NULL;
|
||||
device->FilterCount = 0;
|
||||
}
|
||||
|
||||
|
||||
static void InitFilterParams(ALfilter *filter, ALenum type)
|
||||
{
|
||||
filter->type = type;
|
||||
if(type == AL_FILTER_LOWPASS)
|
||||
{
|
||||
filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
|
||||
filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
|
||||
filter->HFReference = LOWPASSFREQREF;
|
||||
filter->GainLF = 1.0f;
|
||||
filter->LFReference = HIGHPASSFREQREF;
|
||||
|
||||
filter->Gain = 1.0;
|
||||
filter->GainHF = 1.0;
|
||||
filter->SetParami = lp_SetParami;
|
||||
filter->SetParamiv = lp_SetParamiv;
|
||||
filter->SetParamf = lp_SetParamf;
|
||||
filter->SetParamfv = lp_SetParamfv;
|
||||
filter->GetParami = lp_GetParami;
|
||||
filter->GetParamiv = lp_GetParamiv;
|
||||
filter->GetParamf = lp_GetParamf;
|
||||
filter->GetParamfv = lp_GetParamfv;
|
||||
}
|
||||
else if(type == AL_FILTER_HIGHPASS)
|
||||
{
|
||||
filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
|
||||
filter->GainHF = 1.0f;
|
||||
filter->HFReference = LOWPASSFREQREF;
|
||||
filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
|
||||
filter->LFReference = HIGHPASSFREQREF;
|
||||
|
||||
filter->SetParami = hp_SetParami;
|
||||
filter->SetParamiv = hp_SetParamiv;
|
||||
filter->SetParamf = hp_SetParamf;
|
||||
filter->SetParamfv = hp_SetParamfv;
|
||||
filter->GetParami = hp_GetParami;
|
||||
filter->GetParamiv = hp_GetParamiv;
|
||||
filter->GetParamf = hp_GetParamf;
|
||||
filter->GetParamfv = hp_GetParamfv;
|
||||
}
|
||||
else if(type == AL_FILTER_BANDPASS)
|
||||
{
|
||||
filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
|
||||
filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
|
||||
filter->HFReference = LOWPASSFREQREF;
|
||||
filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
|
||||
filter->LFReference = HIGHPASSFREQREF;
|
||||
|
||||
filter->SetParami = bp_SetParami;
|
||||
filter->SetParamiv = bp_SetParamiv;
|
||||
filter->SetParamf = bp_SetParamf;
|
||||
filter->SetParamfv = bp_SetParamfv;
|
||||
filter->GetParami = bp_GetParami;
|
||||
filter->GetParamiv = bp_GetParamiv;
|
||||
filter->GetParamf = bp_GetParamf;
|
||||
filter->GetParamfv = bp_GetParamfv;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter->Gain = 1.0f;
|
||||
filter->GainHF = 1.0f;
|
||||
filter->HFReference = LOWPASSFREQREF;
|
||||
filter->GainLF = 1.0f;
|
||||
filter->LFReference = HIGHPASSFREQREF;
|
||||
|
||||
filter->SetParami = null_SetParami;
|
||||
filter->SetParamiv = null_SetParamiv;
|
||||
filter->SetParamf = null_SetParamf;
|
||||
filter->SetParamfv = null_SetParamfv;
|
||||
filter->GetParami = null_GetParami;
|
||||
filter->GetParamiv = null_GetParamiv;
|
||||
filter->GetParamf = null_GetParamf;
|
||||
filter->GetParamfv = null_GetParamfv;
|
||||
}
|
||||
filter->type = type;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+353
-360
@@ -24,430 +24,423 @@
|
||||
#include "AL/alc.h"
|
||||
#include "alError.h"
|
||||
#include "alListener.h"
|
||||
#include "alSource.h"
|
||||
|
||||
ALAPI ALvoid ALAPIENTRY alListenerf(ALenum eParam, ALfloat flValue)
|
||||
AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALCcontext *context;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
switch(eParam)
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
if(!(value >= 0.0f && isfinite(value)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
context->Listener->Gain = value;
|
||||
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
if(!(value >= 0.0f && isfinite(value)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
context->Listener->MetersPerUnit = value;
|
||||
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
LockContext(context);
|
||||
context->Listener->Position[0] = value1;
|
||||
context->Listener->Position[1] = value2;
|
||||
context->Listener->Position[2] = value3;
|
||||
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
LockContext(context);
|
||||
context->Listener->Velocity[0] = value1;
|
||||
context->Listener->Velocity[1] = value2;
|
||||
context->Listener->Velocity[2] = value3;
|
||||
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
if(values)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
if(flValue >= 0.0f)
|
||||
pContext->Listener.Gain = flValue;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
if(flValue > 0.0f)
|
||||
pContext->Listener.MetersPerUnit = flValue;
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
alListenerf(param, values[0]);
|
||||
return;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALAPI ALvoid ALAPIENTRY alListener3f(ALenum eParam, ALfloat flValue1, ALfloat flValue2, ALfloat flValue3)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
switch(eParam)
|
||||
{
|
||||
case AL_POSITION:
|
||||
pContext->Listener.Position[0] = flValue1;
|
||||
pContext->Listener.Position[1] = flValue2;
|
||||
pContext->Listener.Position[2] = flValue3;
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
pContext->Listener.Velocity[0] = flValue1;
|
||||
pContext->Listener.Velocity[1] = flValue2;
|
||||
pContext->Listener.Velocity[2] = flValue3;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALAPI ALvoid ALAPIENTRY alListenerfv(ALenum eParam, const ALfloat *pflValues)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(pflValues)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
case AL_GAIN:
|
||||
if(pflValues[0] >= 0.0f)
|
||||
pContext->Listener.Gain = pflValues[0];
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
if(pflValues[0] > 0.0f)
|
||||
pContext->Listener.MetersPerUnit = pflValues[0];
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
break;
|
||||
|
||||
case AL_POSITION:
|
||||
pContext->Listener.Position[0] = pflValues[0];
|
||||
pContext->Listener.Position[1] = pflValues[1];
|
||||
pContext->Listener.Position[2] = pflValues[2];
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
pContext->Listener.Velocity[0] = pflValues[0];
|
||||
pContext->Listener.Velocity[1] = pflValues[1];
|
||||
pContext->Listener.Velocity[2] = pflValues[2];
|
||||
break;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
// AT then UP
|
||||
pContext->Listener.Forward[0] = pflValues[0];
|
||||
pContext->Listener.Forward[1] = pflValues[1];
|
||||
pContext->Listener.Forward[2] = pflValues[2];
|
||||
pContext->Listener.Up[0] = pflValues[3];
|
||||
pContext->Listener.Up[1] = pflValues[4];
|
||||
pContext->Listener.Up[2] = pflValues[5];
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALAPI ALvoid ALAPIENTRY alListeneri(ALenum eParam, ALint lValue)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
(void)lValue;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
switch(eParam)
|
||||
{
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALAPI void ALAPIENTRY alListener3i(ALenum eParam, ALint lValue1, ALint lValue2, ALint lValue3)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
switch(eParam)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(eParam, (ALfloat)lValue1, (ALfloat)lValue2, (ALfloat)lValue3);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
|
||||
|
||||
ALAPI void ALAPIENTRY alListeneriv( ALenum eParam, const ALint* plValues )
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALfloat flValues[6];
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(plValues)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
flValues[0] = (ALfloat)plValues[0];
|
||||
flValues[1] = (ALfloat)plValues[1];
|
||||
flValues[2] = (ALfloat)plValues[2];
|
||||
alListenerfv(eParam, flValues);
|
||||
break;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
flValues[0] = (ALfloat)plValues[0];
|
||||
flValues[1] = (ALfloat)plValues[1];
|
||||
flValues[2] = (ALfloat)plValues[2];
|
||||
flValues[3] = (ALfloat)plValues[3];
|
||||
flValues[4] = (ALfloat)plValues[4];
|
||||
flValues[5] = (ALfloat)plValues[5];
|
||||
alListenerfv(eParam, flValues);
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
alListener3f(param, values[0], values[1], values[2]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(values))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
|
||||
isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5])))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
LockContext(context);
|
||||
/* AT then UP */
|
||||
context->Listener->Forward[0] = values[0];
|
||||
context->Listener->Forward[1] = values[1];
|
||||
context->Listener->Forward[2] = values[2];
|
||||
context->Listener->Up[0] = values[3];
|
||||
context->Listener->Up[1] = values[4];
|
||||
context->Listener->Up[2] = values[5];
|
||||
ATOMIC_STORE(&context->UpdateSources, AL_TRUE);
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
ALAPI ALvoid ALAPIENTRY alGetListenerf(ALenum eParam, ALfloat *pflValue)
|
||||
AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value))
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALCcontext *context;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(pflValue)
|
||||
switch(param)
|
||||
{
|
||||
switch(eParam)
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, (ALfloat)value1, (ALfloat)value2, (ALfloat)value3);
|
||||
return;
|
||||
}
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
if(values)
|
||||
{
|
||||
ALfloat fvals[6];
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
*pflValue = pContext->Listener.Gain;
|
||||
break;
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, (ALfloat)values[0], (ALfloat)values[1], (ALfloat)values[2]);
|
||||
return;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
*pflValue = pContext->Listener.MetersPerUnit;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
case AL_ORIENTATION:
|
||||
fvals[0] = (ALfloat)values[0];
|
||||
fvals[1] = (ALfloat)values[1];
|
||||
fvals[2] = (ALfloat)values[2];
|
||||
fvals[3] = (ALfloat)values[3];
|
||||
fvals[4] = (ALfloat)values[4];
|
||||
fvals[5] = (ALfloat)values[5];
|
||||
alListenerfv(param, fvals);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
}
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
|
||||
ALAPI ALvoid ALAPIENTRY alGetListener3f(ALenum eParam, ALfloat *pflValue1, ALfloat *pflValue2, ALfloat *pflValue3)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(pflValue1 && pflValue2 && pflValue3)
|
||||
if(!(values))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*pflValue1 = pContext->Listener.Position[0];
|
||||
*pflValue2 = pContext->Listener.Position[1];
|
||||
*pflValue3 = pContext->Listener.Position[2];
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
*pflValue1 = pContext->Listener.Velocity[0];
|
||||
*pflValue2 = pContext->Listener.Velocity[1];
|
||||
*pflValue3 = pContext->Listener.Velocity[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
ALAPI ALvoid ALAPIENTRY alGetListenerfv(ALenum eParam, ALfloat *pflValues)
|
||||
AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALCcontext *context;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(pflValues)
|
||||
if(!(value))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
case AL_GAIN:
|
||||
pflValues[0] = pContext->Listener.Gain;
|
||||
break;
|
||||
case AL_GAIN:
|
||||
*value = context->Listener->Gain;
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
pflValues[0] = pContext->Listener.MetersPerUnit;
|
||||
break;
|
||||
case AL_METERS_PER_UNIT:
|
||||
*value = context->Listener->MetersPerUnit;
|
||||
break;
|
||||
|
||||
case AL_POSITION:
|
||||
pflValues[0] = pContext->Listener.Position[0];
|
||||
pflValues[1] = pContext->Listener.Position[1];
|
||||
pflValues[2] = pContext->Listener.Position[2];
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
pflValues[0] = pContext->Listener.Velocity[0];
|
||||
pflValues[1] = pContext->Listener.Velocity[1];
|
||||
pflValues[2] = pContext->Listener.Velocity[2];
|
||||
break;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
// AT then UP
|
||||
pflValues[0] = pContext->Listener.Forward[0];
|
||||
pflValues[1] = pContext->Listener.Forward[1];
|
||||
pflValues[2] = pContext->Listener.Forward[2];
|
||||
pflValues[3] = pContext->Listener.Up[0];
|
||||
pflValues[4] = pContext->Listener.Up[1];
|
||||
pflValues[5] = pContext->Listener.Up[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
ALAPI ALvoid ALAPIENTRY alGetListeneri(ALenum eParam, ALint *plValue)
|
||||
AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALCcontext *context;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(plValue)
|
||||
if(!(value1 && value2 && value3))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
case AL_POSITION:
|
||||
LockContext(context);
|
||||
*value1 = context->Listener->Position[0];
|
||||
*value2 = context->Listener->Position[1];
|
||||
*value3 = context->Listener->Position[2];
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
ProcessContext(pContext);
|
||||
case AL_VELOCITY:
|
||||
LockContext(context);
|
||||
*value1 = context->Listener->Velocity[0];
|
||||
*value2 = context->Listener->Velocity[1];
|
||||
*value3 = context->Listener->Velocity[2];
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
ALAPI void ALAPIENTRY alGetListener3i(ALenum eParam, ALint *plValue1, ALint *plValue2, ALint *plValue3)
|
||||
AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALCcontext *context;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
|
||||
if(plValue1 && plValue2 && plValue3)
|
||||
switch(param)
|
||||
{
|
||||
switch (eParam)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*plValue1 = (ALint)pContext->Listener.Position[0];
|
||||
*plValue2 = (ALint)pContext->Listener.Position[1];
|
||||
*plValue3 = (ALint)pContext->Listener.Position[2];
|
||||
break;
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alGetListenerf(param, values);
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
*plValue1 = (ALint)pContext->Listener.Velocity[0];
|
||||
*plValue2 = (ALint)pContext->Listener.Velocity[1];
|
||||
*plValue3 = (ALint)pContext->Listener.Velocity[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alGetListener3f(param, values+0, values+1, values+2);
|
||||
return;
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(values))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
LockContext(context);
|
||||
// AT then UP
|
||||
values[0] = context->Listener->Forward[0];
|
||||
values[1] = context->Listener->Forward[1];
|
||||
values[2] = context->Listener->Forward[2];
|
||||
values[3] = context->Listener->Up[0];
|
||||
values[4] = context->Listener->Up[1];
|
||||
values[5] = context->Listener->Up[2];
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
ALAPI void ALAPIENTRY alGetListeneriv(ALenum eParam, ALint* plValues)
|
||||
AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
|
||||
{
|
||||
ALCcontext *pContext;
|
||||
ALCcontext *context;
|
||||
|
||||
pContext = GetContextSuspended();
|
||||
if(!pContext) return;
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(plValues)
|
||||
if(!(value))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
{
|
||||
switch(eParam)
|
||||
{
|
||||
case AL_POSITION:
|
||||
plValues[0] = (ALint)pContext->Listener.Position[0];
|
||||
plValues[1] = (ALint)pContext->Listener.Position[1];
|
||||
plValues[2] = (ALint)pContext->Listener.Position[2];
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
plValues[0] = (ALint)pContext->Listener.Velocity[0];
|
||||
plValues[1] = (ALint)pContext->Listener.Velocity[1];
|
||||
plValues[2] = (ALint)pContext->Listener.Velocity[2];
|
||||
break;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
// AT then UP
|
||||
plValues[0] = (ALint)pContext->Listener.Forward[0];
|
||||
plValues[1] = (ALint)pContext->Listener.Forward[1];
|
||||
plValues[2] = (ALint)pContext->Listener.Forward[2];
|
||||
plValues[3] = (ALint)pContext->Listener.Up[0];
|
||||
plValues[4] = (ALint)pContext->Listener.Up[1];
|
||||
plValues[5] = (ALint)pContext->Listener.Up[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(AL_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
else
|
||||
alSetError(AL_INVALID_VALUE);
|
||||
|
||||
ProcessContext(pContext);
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(value1 && value2 && value3))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch (param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
LockContext(context);
|
||||
*value1 = (ALint)context->Listener->Position[0];
|
||||
*value2 = (ALint)context->Listener->Position[1];
|
||||
*value3 = (ALint)context->Listener->Position[2];
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
LockContext(context);
|
||||
*value1 = (ALint)context->Listener->Velocity[0];
|
||||
*value2 = (ALint)context->Listener->Velocity[1];
|
||||
*value3 = (ALint)context->Listener->Velocity[2];
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
|
||||
{
|
||||
ALCcontext *context;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alGetListener3i(param, values+0, values+1, values+2);
|
||||
return;
|
||||
}
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(values))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
LockContext(context);
|
||||
// AT then UP
|
||||
values[0] = (ALint)context->Listener->Forward[0];
|
||||
values[1] = (ALint)context->Listener->Forward[1];
|
||||
values[2] = (ALint)context->Listener->Forward[2];
|
||||
values[3] = (ALint)context->Listener->Up[0];
|
||||
values[4] = (ALint)context->Listener->Up[1];
|
||||
values[5] = (ALint)context->Listener->Up[2];
|
||||
UnlockContext(context);
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alMidi.h"
|
||||
#include "alError.h"
|
||||
#include "alThunk.h"
|
||||
#include "evtqueue.h"
|
||||
#include "rwlock.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
|
||||
MidiSynth *SynthCreate(ALCdevice *device)
|
||||
{
|
||||
MidiSynth *synth = NULL;
|
||||
if(!synth) synth = SSynth_create(device);
|
||||
if(!synth) synth = FSynth_create(device);
|
||||
if(!synth) synth = DSynth_create(device);
|
||||
return synth;
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alMidiSoundfontSOFT(ALuint id)
|
||||
{
|
||||
alMidiSoundfontvSOFT(1, &id);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiSoundfontvSOFT(ALsizei count, const ALuint *ids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(count < 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
synth = device->Synth;
|
||||
|
||||
WriteLock(&synth->Lock);
|
||||
if(synth->State == AL_PLAYING || synth->State == AL_PAUSED)
|
||||
alSetError(context, AL_INVALID_OPERATION);
|
||||
else
|
||||
{
|
||||
err = V(synth,selectSoundfonts)(context, count, ids);
|
||||
if(err != AL_NO_ERROR)
|
||||
alSetError(context, err);
|
||||
}
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alMidiEventSOFT(ALuint64SOFT time, ALenum event, ALsizei channel, ALsizei param1, ALsizei param2)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(event == AL_NOTEOFF_SOFT || event == AL_NOTEON_SOFT ||
|
||||
event == AL_KEYPRESSURE_SOFT || event == AL_CONTROLLERCHANGE_SOFT ||
|
||||
event == AL_PROGRAMCHANGE_SOFT || event == AL_CHANNELPRESSURE_SOFT ||
|
||||
event == AL_PITCHBEND_SOFT))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
if(!(channel >= 0 && channel <= 15))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
if(!(param1 >= 0 && param1 <= 127))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
if(!(param2 >= 0 && param2 <= 127))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
ALCdevice_Lock(device);
|
||||
err = MidiSynth_insertEvent(device->Synth, time, event|channel, param1, param2);
|
||||
ALCdevice_Unlock(device);
|
||||
if(err != AL_NO_ERROR)
|
||||
alSetError(context, err);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiSysExSOFT(ALuint64SOFT time, const ALbyte *data, ALsizei size)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!data || size < 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
ALCdevice_Lock(device);
|
||||
err = MidiSynth_insertSysExEvent(device->Synth, time, data, size);
|
||||
ALCdevice_Unlock(device);
|
||||
if(err != AL_NO_ERROR)
|
||||
alSetError(context, err);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiPlaySOFT(void)
|
||||
{
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
synth = context->Device->Synth;
|
||||
WriteLock(&synth->Lock);
|
||||
MidiSynth_setState(synth, AL_PLAYING);
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiPauseSOFT(void)
|
||||
{
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
synth = context->Device->Synth;
|
||||
WriteLock(&synth->Lock);
|
||||
MidiSynth_setState(synth, AL_PAUSED);
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiStopSOFT(void)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
synth = device->Synth;
|
||||
|
||||
WriteLock(&synth->Lock);
|
||||
MidiSynth_setState(synth, AL_STOPPED);
|
||||
|
||||
ALCdevice_Lock(device);
|
||||
V0(synth,stop)();
|
||||
ALCdevice_Unlock(device);
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alMidiResetSOFT(void)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
MidiSynth *synth;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
synth = device->Synth;
|
||||
|
||||
WriteLock(&synth->Lock);
|
||||
MidiSynth_setState(synth, AL_INITIAL);
|
||||
|
||||
ALCdevice_Lock(device);
|
||||
V0(synth,reset)();
|
||||
ALCdevice_Unlock(device);
|
||||
WriteUnlock(&synth->Lock);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alMidiGainSOFT(ALfloat value)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(value >= 0.0f && isfinite(value)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
V(device->Synth,setGain)(value);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alMidi.h"
|
||||
#include "alError.h"
|
||||
#include "alThunk.h"
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
|
||||
extern inline struct ALsfpreset *LookupPreset(ALCdevice *device, ALuint id);
|
||||
extern inline struct ALsfpreset *RemovePreset(ALCdevice *device, ALuint id);
|
||||
|
||||
static void ALsfpreset_Construct(ALsfpreset *self);
|
||||
static void ALsfpreset_Destruct(ALsfpreset *self);
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGenPresetsSOFT(ALsizei n, ALuint *ids)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALsizei cur = 0;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALsfpreset *preset = NewPreset(context);
|
||||
if(!preset)
|
||||
{
|
||||
alDeletePresetsSOFT(cur, ids);
|
||||
break;
|
||||
}
|
||||
|
||||
ids[cur] = preset->id;
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alDeletePresetsSOFT(ALsizei n, const ALuint *ids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
/* Check for valid ID */
|
||||
if((preset=LookupPreset(device, ids[i])) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&preset->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if((preset=LookupPreset(device, ids[i])) != NULL)
|
||||
DeletePreset(device, preset);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsPresetSOFT(ALuint id)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALboolean ret;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return AL_FALSE;
|
||||
|
||||
ret = LookupPreset(context->Device, id) ? AL_TRUE : AL_FALSE;
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alPresetiSOFT(ALuint id, ALenum param, ALint value)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if((preset=LookupPreset(device, id)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&preset->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_MIDI_PRESET_SOFT:
|
||||
if(!(value >= 0 && value <= 127))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
preset->Preset = value;
|
||||
break;
|
||||
|
||||
case AL_MIDI_BANK_SOFT:
|
||||
if(!(value >= 0 && value <= 128))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
preset->Bank = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alPresetivSOFT(ALuint id, ALenum param, const ALint *values)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_MIDI_PRESET_SOFT:
|
||||
case AL_MIDI_BANK_SOFT:
|
||||
alPresetiSOFT(id, param, values[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if((preset=LookupPreset(device, id)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&preset->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alGetPresetivSOFT(ALuint id, ALenum param, ALint *values)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if((preset=LookupPreset(device, id)) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_MIDI_PRESET_SOFT:
|
||||
values[0] = preset->Preset;
|
||||
break;
|
||||
|
||||
case AL_MIDI_BANK_SOFT:
|
||||
values[0] = preset->Bank;
|
||||
break;
|
||||
|
||||
case AL_FONTSOUNDS_SIZE_SOFT:
|
||||
values[0] = preset->NumSounds;
|
||||
break;
|
||||
|
||||
case AL_FONTSOUNDS_SOFT:
|
||||
for(i = 0;i < preset->NumSounds;i++)
|
||||
values[i] = preset->Sounds[i]->id;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alPresetFontsoundsSOFT(ALuint id, ALsizei count, const ALuint *fsids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsfpreset *preset;
|
||||
ALfontsound **sounds;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if(!(preset=LookupPreset(device, id)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(count < 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
if(ReadRef(&preset->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
|
||||
if(count == 0)
|
||||
sounds = NULL;
|
||||
else
|
||||
{
|
||||
sounds = calloc(count, sizeof(sounds[0]));
|
||||
if(!sounds)
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
if(!(sounds[i]=LookupFontsound(device, fsids[i])))
|
||||
{
|
||||
free(sounds);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
IncrementRef(&sounds[i]->ref);
|
||||
|
||||
sounds = ExchangePtr((XchgPtr*)&preset->Sounds, sounds);
|
||||
count = ExchangeInt(&preset->NumSounds, count);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
DecrementRef(&sounds[i]->ref);
|
||||
free(sounds);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
ALsfpreset *NewPreset(ALCcontext *context)
|
||||
{
|
||||
ALCdevice *device = context->Device;
|
||||
ALsfpreset *preset;
|
||||
ALenum err;
|
||||
|
||||
preset = calloc(1, sizeof(*preset));
|
||||
if(!preset)
|
||||
SET_ERROR_AND_RETURN_VALUE(context, AL_OUT_OF_MEMORY, NULL);
|
||||
ALsfpreset_Construct(preset);
|
||||
|
||||
err = NewThunkEntry(&preset->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntry(&device->PresetMap, preset->id, preset);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
ALsfpreset_Destruct(preset);
|
||||
memset(preset, 0, sizeof(*preset));
|
||||
free(preset);
|
||||
|
||||
SET_ERROR_AND_RETURN_VALUE(context, err, NULL);
|
||||
}
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
void DeletePreset(ALCdevice *device, ALsfpreset *preset)
|
||||
{
|
||||
RemovePreset(device, preset->id);
|
||||
|
||||
ALsfpreset_Destruct(preset);
|
||||
memset(preset, 0, sizeof(*preset));
|
||||
free(preset);
|
||||
}
|
||||
|
||||
|
||||
static void ALsfpreset_Construct(ALsfpreset *self)
|
||||
{
|
||||
InitRef(&self->ref, 0);
|
||||
|
||||
self->Preset = 0;
|
||||
self->Bank = 0;
|
||||
|
||||
self->Sounds = NULL;
|
||||
self->NumSounds = 0;
|
||||
|
||||
self->id = 0;
|
||||
}
|
||||
|
||||
static void ALsfpreset_Destruct(ALsfpreset *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
FreeThunkEntry(self->id);
|
||||
self->id = 0;
|
||||
|
||||
for(i = 0;i < self->NumSounds;i++)
|
||||
DecrementRef(&self->Sounds[i]->ref);
|
||||
free(self->Sounds);
|
||||
self->Sounds = NULL;
|
||||
self->NumSounds = 0;
|
||||
}
|
||||
|
||||
|
||||
/* ReleaseALPresets
|
||||
*
|
||||
* Called to destroy any presets that still exist on the device
|
||||
*/
|
||||
void ReleaseALPresets(ALCdevice *device)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < device->PresetMap.size;i++)
|
||||
{
|
||||
ALsfpreset *temp = device->PresetMap.array[i].value;
|
||||
device->PresetMap.array[i].value = NULL;
|
||||
|
||||
ALsfpreset_Destruct(temp);
|
||||
|
||||
memset(temp, 0, sizeof(*temp));
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alMidi.h"
|
||||
#include "alThunk.h"
|
||||
#include "alError.h"
|
||||
#include <alBuffer.h>
|
||||
|
||||
#include "midi/base.h"
|
||||
|
||||
|
||||
extern inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id);
|
||||
extern inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id);
|
||||
|
||||
static void ALsoundfont_Construct(ALsoundfont *self);
|
||||
static void ALsoundfont_Destruct(ALsoundfont *self);
|
||||
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
|
||||
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
|
||||
static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr);
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsizei cur = 0;
|
||||
ALenum err;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(cur = 0;cur < n;cur++)
|
||||
{
|
||||
ALsoundfont *sfont = calloc(1, sizeof(ALsoundfont));
|
||||
if(!sfont)
|
||||
{
|
||||
alDeleteSoundfontsSOFT(cur, ids);
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
}
|
||||
ALsoundfont_Construct(sfont);
|
||||
|
||||
err = NewThunkEntry(&sfont->id);
|
||||
if(err == AL_NO_ERROR)
|
||||
err = InsertUIntMapEntry(&device->SfontMap, sfont->id, sfont);
|
||||
if(err != AL_NO_ERROR)
|
||||
{
|
||||
ALsoundfont_Destruct(sfont);
|
||||
memset(sfont, 0, sizeof(ALsoundfont));
|
||||
free(sfont);
|
||||
|
||||
alDeleteSoundfontsSOFT(cur, ids);
|
||||
SET_ERROR_AND_GOTO(context, err, done);
|
||||
}
|
||||
|
||||
ids[cur] = sfont->id;
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALvoid AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsoundfont *sfont;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
if(!(n >= 0))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
device = context->Device;
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
/* Check for valid soundfont ID */
|
||||
if(ids[i] == 0)
|
||||
{
|
||||
if(!(sfont=device->DefaultSfont))
|
||||
continue;
|
||||
}
|
||||
else if((sfont=LookupSfont(device, ids[i])) == NULL)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(ReadRef(&sfont->ref) != 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
|
||||
for(i = 0;i < n;i++)
|
||||
{
|
||||
if(ids[i] == 0)
|
||||
{
|
||||
MidiSynth *synth = device->Synth;
|
||||
WriteLock(&synth->Lock);
|
||||
if(device->DefaultSfont != NULL)
|
||||
ALsoundfont_deleteSoundfont(device->DefaultSfont, device);
|
||||
device->DefaultSfont = NULL;
|
||||
WriteUnlock(&synth->Lock);
|
||||
continue;
|
||||
}
|
||||
else if((sfont=RemoveSfont(device, ids[i])) == NULL)
|
||||
continue;
|
||||
|
||||
ALsoundfont_Destruct(sfont);
|
||||
|
||||
memset(sfont, 0, sizeof(*sfont));
|
||||
free(sfont);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id)
|
||||
{
|
||||
ALCcontext *context;
|
||||
ALboolean ret;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return AL_FALSE;
|
||||
|
||||
ret = ((!id || LookupSfont(context->Device, id)) ?
|
||||
AL_TRUE : AL_FALSE);
|
||||
|
||||
ALCcontext_DecRef(context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsoundfont *sfont;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if(id == 0)
|
||||
sfont = ALsoundfont_getDefSoundfont(context);
|
||||
else if(!(sfont=LookupSfont(device, id)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
switch(param)
|
||||
{
|
||||
case AL_PRESETS_SIZE_SOFT:
|
||||
values[0] = sfont->NumPresets;
|
||||
break;
|
||||
|
||||
case AL_PRESETS_SOFT:
|
||||
for(i = 0;i < sfont->NumPresets;i++)
|
||||
values[i] = sfont->Presets[i]->id;
|
||||
break;
|
||||
|
||||
default:
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
|
||||
}
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsoundfont *sfont;
|
||||
ALsfpreset **presets;
|
||||
ALsizei i;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if(id == 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
if(!(sfont=LookupSfont(device, id)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
if(count < 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
|
||||
WriteLock(&sfont->Lock);
|
||||
if(ReadRef(&sfont->ref) != 0)
|
||||
{
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
|
||||
if(count == 0)
|
||||
presets = NULL;
|
||||
else
|
||||
{
|
||||
presets = calloc(count, sizeof(presets[0]));
|
||||
if(!presets)
|
||||
{
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
if(!(presets[i]=LookupPreset(device, pids[i])))
|
||||
{
|
||||
free(presets);
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
IncrementRef(&presets[i]->ref);
|
||||
|
||||
presets = ExchangePtr((XchgPtr*)&sfont->Presets, presets);
|
||||
count = ExchangeInt(&sfont->NumPresets, count);
|
||||
WriteUnlock(&sfont->Lock);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
DecrementRef(&presets[i]->ref);
|
||||
free(presets);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user)
|
||||
{
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
ALsoundfont *sfont;
|
||||
Reader reader;
|
||||
|
||||
context = GetContextRef();
|
||||
if(!context) return;
|
||||
|
||||
device = context->Device;
|
||||
if(id == 0)
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
if(!(sfont=LookupSfont(device, id)))
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
|
||||
|
||||
WriteLock(&sfont->Lock);
|
||||
if(ReadRef(&sfont->ref) != 0)
|
||||
{
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
if(sfont->NumPresets > 0)
|
||||
{
|
||||
WriteUnlock(&sfont->Lock);
|
||||
SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
|
||||
}
|
||||
|
||||
reader.cb = cb;
|
||||
reader.ptr = user;
|
||||
reader.error = 0;
|
||||
loadSf2(&reader, sfont, context);
|
||||
WriteUnlock(&sfont->Lock);
|
||||
|
||||
done:
|
||||
ALCcontext_DecRef(context);
|
||||
}
|
||||
|
||||
|
||||
static void ALsoundfont_Construct(ALsoundfont *self)
|
||||
{
|
||||
InitRef(&self->ref, 0);
|
||||
|
||||
self->Presets = NULL;
|
||||
self->NumPresets = 0;
|
||||
|
||||
RWLockInit(&self->Lock);
|
||||
|
||||
self->id = 0;
|
||||
}
|
||||
|
||||
static void ALsoundfont_Destruct(ALsoundfont *self)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
FreeThunkEntry(self->id);
|
||||
self->id = 0;
|
||||
|
||||
for(i = 0;i < self->NumPresets;i++)
|
||||
{
|
||||
DecrementRef(&self->Presets[i]->ref);
|
||||
self->Presets[i] = NULL;
|
||||
}
|
||||
free(self->Presets);
|
||||
self->Presets = NULL;
|
||||
self->NumPresets = 0;
|
||||
}
|
||||
|
||||
ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context)
|
||||
{
|
||||
ALCdevice *device = context->Device;
|
||||
al_string fname = AL_STRING_INIT_STATIC();
|
||||
const char *namelist;
|
||||
|
||||
if(device->DefaultSfont)
|
||||
return device->DefaultSfont;
|
||||
|
||||
device->DefaultSfont = calloc(1, sizeof(device->DefaultSfont[0]));
|
||||
ALsoundfont_Construct(device->DefaultSfont);
|
||||
|
||||
namelist = getenv("ALSOFT_SOUNDFONT");
|
||||
if(!namelist || !namelist[0])
|
||||
ConfigValueStr("midi", "soundfont", &namelist);
|
||||
while(namelist && namelist[0])
|
||||
{
|
||||
const char *next, *end;
|
||||
FILE *f;
|
||||
|
||||
while(*namelist && (isspace(*namelist) || *namelist == ','))
|
||||
namelist++;
|
||||
if(!*namelist)
|
||||
break;
|
||||
next = strchr(namelist, ',');
|
||||
end = next ? next++ : (namelist+strlen(namelist));
|
||||
while(--end != namelist && isspace(*end)) {
|
||||
}
|
||||
if(end == namelist)
|
||||
continue;
|
||||
al_string_append_range(&fname, namelist, end+1);
|
||||
namelist = next;
|
||||
|
||||
f = OpenDataFile(al_string_get_cstr(fname), "openal/soundfonts");
|
||||
if(f == NULL)
|
||||
ERR("Failed to open %s\n", al_string_get_cstr(fname));
|
||||
else
|
||||
{
|
||||
Reader reader;
|
||||
reader.cb = ALsoundfont_read;
|
||||
reader.ptr = f;
|
||||
reader.error = 0;
|
||||
TRACE("Loading %s\n", al_string_get_cstr(fname));
|
||||
loadSf2(&reader, device->DefaultSfont, context);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
al_string_clear(&fname);
|
||||
}
|
||||
AL_STRING_DEINIT(fname);
|
||||
|
||||
return device->DefaultSfont;
|
||||
}
|
||||
|
||||
void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device)
|
||||
{
|
||||
ALsfpreset **presets;
|
||||
ALsizei num_presets;
|
||||
VECTOR(ALbuffer*) buffers;
|
||||
ALsizei i;
|
||||
|
||||
VECTOR_INIT(buffers);
|
||||
presets = ExchangePtr((XchgPtr*)&self->Presets, NULL);
|
||||
num_presets = ExchangeInt(&self->NumPresets, 0);
|
||||
|
||||
for(i = 0;i < num_presets;i++)
|
||||
{
|
||||
ALsfpreset *preset = presets[i];
|
||||
ALfontsound **sounds;
|
||||
ALsizei num_sounds;
|
||||
ALboolean deleting;
|
||||
ALsizei j;
|
||||
|
||||
sounds = ExchangePtr((XchgPtr*)&preset->Sounds, NULL);
|
||||
num_sounds = ExchangeInt(&preset->NumSounds, 0);
|
||||
|
||||
DeletePreset(device, preset);
|
||||
preset = NULL;
|
||||
|
||||
for(j = 0;j < num_sounds;j++)
|
||||
DecrementRef(&sounds[j]->ref);
|
||||
/* Some fontsounds may not be immediately deletable because they're
|
||||
* linked to another fontsound. When those fontsounds are deleted
|
||||
* they should become deletable, so use a loop until all fontsounds
|
||||
* are deleted. */
|
||||
do {
|
||||
deleting = AL_FALSE;
|
||||
for(j = 0;j < num_sounds;j++)
|
||||
{
|
||||
if(sounds[j] && ReadRef(&sounds[j]->ref) == 0)
|
||||
{
|
||||
deleting = AL_TRUE;
|
||||
if(sounds[j]->Buffer)
|
||||
{
|
||||
ALbuffer *buffer = sounds[j]->Buffer;
|
||||
ALbuffer **iter;
|
||||
|
||||
#define MATCH_BUFFER(_i) (buffer == *(_i))
|
||||
VECTOR_FIND_IF(iter, ALbuffer*, buffers, MATCH_BUFFER);
|
||||
if(iter == VECTOR_ITER_END(buffers))
|
||||
VECTOR_PUSH_BACK(buffers, buffer);
|
||||
#undef MATCH_BUFFER
|
||||
}
|
||||
DeleteFontsound(device, sounds[j]);
|
||||
sounds[j] = NULL;
|
||||
}
|
||||
}
|
||||
} while(deleting);
|
||||
free(sounds);
|
||||
}
|
||||
|
||||
ALsoundfont_Destruct(self);
|
||||
free(self);
|
||||
|
||||
#define DELETE_BUFFER(iter) do { \
|
||||
assert(ReadRef(&(*(iter))->ref) == 0); \
|
||||
DeleteBuffer(device, *(iter)); \
|
||||
} while(0)
|
||||
VECTOR_FOR_EACH(ALbuffer*, buffers, DELETE_BUFFER);
|
||||
VECTOR_DEINIT(buffers);
|
||||
#undef DELETE_BUFFER
|
||||
}
|
||||
|
||||
|
||||
static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr)
|
||||
{
|
||||
return fread(buf, 1, bytes, (FILE*)ptr);
|
||||
}
|
||||
|
||||
|
||||
/* ReleaseALSoundfonts
|
||||
*
|
||||
* Called to destroy any soundfonts that still exist on the device
|
||||
*/
|
||||
void ReleaseALSoundfonts(ALCdevice *device)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < device->SfontMap.size;i++)
|
||||
{
|
||||
ALsoundfont *temp = device->SfontMap.array[i].value;
|
||||
device->SfontMap.array[i].value = NULL;
|
||||
|
||||
ALsoundfont_Destruct(temp);
|
||||
|
||||
memset(temp, 0, sizeof(*temp));
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
+2667
-1927
File diff suppressed because it is too large
Load Diff
+640
-410
File diff suppressed because it is too large
Load Diff
+41
-62
@@ -25,86 +25,65 @@
|
||||
#include "alMain.h"
|
||||
#include "alThunk.h"
|
||||
|
||||
typedef struct {
|
||||
ALvoid *ptr;
|
||||
ALboolean InUse;
|
||||
} ThunkEntry;
|
||||
|
||||
static ThunkEntry *g_ThunkArray;
|
||||
static ALuint g_ThunkArraySize;
|
||||
static ALenum *ThunkArray;
|
||||
static ALuint ThunkArraySize;
|
||||
static RWLock ThunkLock;
|
||||
|
||||
static CRITICAL_SECTION g_ThunkLock;
|
||||
|
||||
void alThunkInit(void)
|
||||
void ThunkInit(void)
|
||||
{
|
||||
InitializeCriticalSection(&g_ThunkLock);
|
||||
g_ThunkArraySize = 1;
|
||||
g_ThunkArray = calloc(1, g_ThunkArraySize * sizeof(ThunkEntry));
|
||||
RWLockInit(&ThunkLock);
|
||||
ThunkArraySize = 1;
|
||||
ThunkArray = calloc(1, ThunkArraySize * sizeof(*ThunkArray));
|
||||
}
|
||||
|
||||
void alThunkExit(void)
|
||||
void ThunkExit(void)
|
||||
{
|
||||
free(g_ThunkArray);
|
||||
g_ThunkArray = NULL;
|
||||
g_ThunkArraySize = 0;
|
||||
DeleteCriticalSection(&g_ThunkLock);
|
||||
free(ThunkArray);
|
||||
ThunkArray = NULL;
|
||||
ThunkArraySize = 0;
|
||||
}
|
||||
|
||||
ALuint alThunkAddEntry(ALvoid *ptr)
|
||||
ALenum NewThunkEntry(ALuint *index)
|
||||
{
|
||||
ALuint index;
|
||||
ALenum *NewList;
|
||||
ALuint i;
|
||||
|
||||
EnterCriticalSection(&g_ThunkLock);
|
||||
|
||||
for(index = 0;index < g_ThunkArraySize;index++)
|
||||
ReadLock(&ThunkLock);
|
||||
for(i = 0;i < ThunkArraySize;i++)
|
||||
{
|
||||
if(g_ThunkArray[index].InUse == AL_FALSE)
|
||||
break;
|
||||
}
|
||||
|
||||
if(index == g_ThunkArraySize)
|
||||
{
|
||||
ThunkEntry *NewList;
|
||||
|
||||
NewList = realloc(g_ThunkArray, g_ThunkArraySize*2 * sizeof(ThunkEntry));
|
||||
if(!NewList)
|
||||
if(ExchangeInt(&ThunkArray[i], AL_TRUE) == AL_FALSE)
|
||||
{
|
||||
LeaveCriticalSection(&g_ThunkLock);
|
||||
return 0;
|
||||
ReadUnlock(&ThunkLock);
|
||||
*index = i+1;
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
memset(&NewList[g_ThunkArraySize], 0, g_ThunkArraySize*sizeof(ThunkEntry));
|
||||
g_ThunkArraySize *= 2;
|
||||
g_ThunkArray = NewList;
|
||||
}
|
||||
ReadUnlock(&ThunkLock);
|
||||
|
||||
g_ThunkArray[index].ptr = ptr;
|
||||
g_ThunkArray[index].InUse = AL_TRUE;
|
||||
WriteLock(&ThunkLock);
|
||||
NewList = realloc(ThunkArray, ThunkArraySize*2 * sizeof(*ThunkArray));
|
||||
if(!NewList)
|
||||
{
|
||||
WriteUnlock(&ThunkLock);
|
||||
ERR("Realloc failed to increase to %u entries!\n", ThunkArraySize*2);
|
||||
return AL_OUT_OF_MEMORY;
|
||||
}
|
||||
memset(&NewList[ThunkArraySize], 0, ThunkArraySize*sizeof(*ThunkArray));
|
||||
ThunkArraySize *= 2;
|
||||
ThunkArray = NewList;
|
||||
|
||||
LeaveCriticalSection(&g_ThunkLock);
|
||||
ThunkArray[i] = AL_TRUE;
|
||||
WriteUnlock(&ThunkLock);
|
||||
|
||||
return index+1;
|
||||
*index = i+1;
|
||||
return AL_NO_ERROR;
|
||||
}
|
||||
|
||||
void alThunkRemoveEntry(ALuint index)
|
||||
void FreeThunkEntry(ALuint index)
|
||||
{
|
||||
EnterCriticalSection(&g_ThunkLock);
|
||||
|
||||
if(index > 0 && index <= g_ThunkArraySize)
|
||||
g_ThunkArray[index-1].InUse = AL_FALSE;
|
||||
|
||||
LeaveCriticalSection(&g_ThunkLock);
|
||||
}
|
||||
|
||||
ALvoid *alThunkLookupEntry(ALuint index)
|
||||
{
|
||||
ALvoid *ptr = NULL;
|
||||
|
||||
EnterCriticalSection(&g_ThunkLock);
|
||||
|
||||
if(index > 0 && index <= g_ThunkArraySize)
|
||||
ptr = g_ThunkArray[index-1].ptr;
|
||||
|
||||
LeaveCriticalSection(&g_ThunkLock);
|
||||
|
||||
return ptr;
|
||||
ReadLock(&ThunkLock);
|
||||
if(index > 0 && index <= ThunkArraySize)
|
||||
ExchangeInt(&ThunkArray[index-1], AL_FALSE);
|
||||
ReadUnlock(&ThunkLock);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user