diff --git a/Cargo.lock b/Cargo.lock index 4678e2d0..fd228349 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,9 +293,9 @@ dependencies = [ [[package]] name = "arboard" -version = "3.6.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +checksum = "aeb9553e62312c8756a5979254fc6d7da8bbeb5986a1e17037fab932f7037917" dependencies = [ "clipboard-win", "image", @@ -307,11 +307,20 @@ dependencies = [ "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", - "windows-sys 0.60.2", + "windows-sys 0.61.2", "wl-clipboard-rs", "x11rb", ] +[[package]] +name = "arc-swap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e" +dependencies = [ + "rustversion", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -370,9 +379,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.50" +version = "0.4.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee19bd99b43e3691acbad4e840420a4881cea6c0b66a208125a824f8fd53f5a1" +checksum = "9d5755640120b24961f8231e05892e4a4f7e0d04e8aeca161508c9c397946970" dependencies = [ "compression-codecs", "compression-core", @@ -449,7 +458,7 @@ checksum = "4f8abc12baad266b1c8cec146854c195b5864b4221d4b2ca7296a7ae82d9e451" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -484,7 +493,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -714,7 +723,7 @@ checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -766,18 +775,42 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "calloop" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e" +dependencies = [ + "bitflags 2.13.2", + "polling", + "rustix 1.1.5", + "tracing", +] + [[package]] name = "calloop-wayland-source" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop", + "calloop 0.13.0", "rustix 0.38.44", "wayland-backend", "wayland-client", ] +[[package]] +name = "calloop-wayland-source" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" +dependencies = [ + "calloop 0.14.5", + "rustix 1.1.5", + "wayland-backend", + "wayland-client", +] + [[package]] name = "cbc" version = "0.2.1" @@ -789,9 +822,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.6.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630" +checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0" dependencies = [ "find-msvc-tools", "jobserver", @@ -899,9 +932,9 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "5.4.1" +version = "5.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +checksum = "052af7878466feccda12ba7a6a67d019d712f296809559b47c12c8cc196d3a9a" dependencies = [ "error-code", ] @@ -981,9 +1014,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" -version = "4.6.8" +version = "4.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +checksum = "4356eba6976435498d05a311ee9fc486da3bc81c200d3f0d7dd9703b41913f92" dependencies = [ "bytes", "memchr", @@ -991,9 +1024,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.45" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98fc98460ba0ad5317075d3632b8dfc45d0be8c4a49347c2a38272019717614a" +checksum = "cae36683101b2899f48bd31f8db9839437356188e3b1ff23096e73dab09ba8fe" dependencies = [ "compression-core", "flate2", @@ -1002,9 +1035,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8ccc4ea9f6acc32d102c0f6d471d11d913ad15f20c04de743374861fa1d414" +checksum = "2ebdda89edc534bf8d5dd2857562633b6495557e5846552ade994b99ed13c36a" [[package]] name = "concat-string" @@ -1229,9 +1262,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" +checksum = "0ba106e844045ddc02c20768ac4b0b7cb45f2cc2ecbd833ac9f5dacb183c2055" dependencies = [ "cfg-if 1.0.5", ] @@ -1254,6 +1287,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e8bd762f7479489c70ed6c768ddca99d7296857de437a68dcb2a94365b3fae" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.23" @@ -1298,9 +1340,9 @@ dependencies = [ [[package]] name = "ctutils" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574" +checksum = "fe3e9d21d39e6f39ff46e03d5b621424f88ee23d8a1672c6bfc9ccc7f2da4f6a" dependencies = [ "cmov", ] @@ -1337,9 +1379,9 @@ checksum = "7eed2c4702fa172d1ce21078faa7c5203e69f5394d48cc436d25928394a867a2" [[package]] name = "defmt" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +checksum = "e4a0795ac740cb54d7b3775c9c17345f6aaecf884c01733b39d6017738e0e3e1" dependencies = [ "bitflags 1.3.2", "defmt-macros", @@ -1347,30 +1389,30 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +checksum = "840bb056bd99c495c9188198a258ab787c79f306d864db3e6e8451442d6d8366" dependencies = [ "defmt-parser", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.9", ] [[package]] name = "defmt-parser" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +checksum = "71958f69fb2de856c5dd51ac9377e986ca1aa5919f931bf5395a807277f889a0" dependencies = [ "thiserror 2.0.21", ] [[package]] name = "deranged" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e" [[package]] name = "derive_more" @@ -1408,9 +1450,9 @@ dependencies = [ [[package]] name = "dimpl" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b8184e6c079d7c35d10649505f6788117dae289585e3ae86764bf48dc9c970" +checksum = "0e769119713c7cf11efb6dd594258095c4a5959fa2bb8d4a7f89978efbd0c08b" dependencies = [ "arrayvec", "log", @@ -1468,7 +1510,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -1713,9 +1755,9 @@ dependencies = [ [[package]] name = "emojis" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1514ced566c94991ed9563258ecf61e311fd773bf0a3f20606830386bf66e3" +checksum = "589871e639330783bc20ad89d32ac1b049e02c273d96c907ca27478c8bc674a2" dependencies = [ "phf 0.13.1", ] @@ -1848,9 +1890,9 @@ source = "git+https://github.com/crmne/egui?rev=ba6790fe7cf46e58e8d27ce1524cbfde [[package]] name = "equivalent" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" [[package]] name = "errno" @@ -2060,9 +2102,9 @@ checksum = "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693" [[package]] name = "find-msvc-tools" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" [[package]] name = "fixedbitset" @@ -2119,9 +2161,9 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "font-types" -version = "0.12.6" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c92d9081c7ab3bd83ef1eecdfad090f3d966e3ce74106779664114cfe1a6274" +checksum = "835ae9d781464a79026cde387f9cbc650c9a4909d7cf7e658b127e3c35d2f35d" dependencies = [ "bytemuck", "serde", @@ -2154,7 +2196,7 @@ checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -2178,15 +2220,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - [[package]] name = "futures-channel" version = "0.3.34" @@ -2229,7 +2262,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -2700,9 +2733,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" dependencies = [ "displaydoc", "potential_utf", @@ -2714,9 +2747,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" dependencies = [ "displaydoc", "litemap", @@ -2727,9 +2760,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -2741,15 +2774,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" +checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc" [[package]] name = "icu_properties" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f" dependencies = [ "displaydoc", "icu_collections", @@ -2762,15 +2795,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" +checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf" [[package]] name = "icu_provider" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" dependencies = [ "displaydoc", "icu_locale_core", @@ -2881,9 +2914,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +checksum = "3b5cf4372d022092674a83b8febf5865f7a573cc0c5baec5e49c879b6a20978f" dependencies = [ "block-padding", "hybrid-array", @@ -3127,9 +3160,9 @@ dependencies = [ [[package]] name = "keyring-core" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e621458ca9c51aa110bd0339d4751a056b9576bf1253aee1aa560dda0fc9d" +checksum = "29fbf4b551298c67925f38cf341a1ada797a75f1837bdf8ae1fc8e2ebc6efb59" dependencies = [ "log", ] @@ -3172,10 +3205,12 @@ dependencies = [ [[package]] name = "ksni" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "814b44c24cd2cb236c3b8a41c7f08237b452a8e76ecaa81f1cec40b5b678215b" +checksum = "1bcc1cb43797140f74b711f1c4a49f48a7a7429f211c5fb78264e7bcd1e99caf" dependencies = [ + "arc-swap", + "crossbeam-queue", "futures-util", "pastey", "serde", @@ -3220,9 +3255,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.190" +version = "0.2.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" [[package]] name = "libloading" @@ -3249,7 +3284,7 @@ dependencies = [ "bitflags 2.13.2", "libc", "plain", - "redox_syscall 0.9.4", + "redox_syscall 0.9.5", ] [[package]] @@ -3341,9 +3376,9 @@ checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "litrs" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70" [[package]] name = "lock_api" @@ -3416,9 +3451,9 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" +checksum = "6b0ca1155a9ecc25aa12e8313a119f32edf0255fea2f44839c4ac0a12b2ac157" dependencies = [ "libc", ] @@ -3776,7 +3811,6 @@ dependencies = [ "notify-rust", "open", "openssl", - "opus-decoder", "opus-rs", "percent-encoding", "prost", @@ -3844,20 +3878,21 @@ version = "0.1.0" [[package]] name = "notify" -version = "8.2.0" +version = "8.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +checksum = "82f812702fbc75d35416d3f884e04925be7e9498f4eceea2e24ba34559284674" dependencies = [ "bitflags 2.13.2", - "fsevent-sys", "inotify", "kqueue", "libc", "log", "mio", "notify-types", + "objc2-core-foundation", + "objc2-core-services", "walkdir", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4274,6 +4309,16 @@ dependencies = [ "objc2-core-video", ] +[[package]] +name = "objc2-core-services" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583300ad934cba24ff5292aee751ecc070f7ca6b39a574cc21b7b5e588e06a0b" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "objc2-core-text" version = "0.3.2" @@ -4528,9 +4573,9 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "open" -version = "5.4.4" +version = "5.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa576c76302b7b808eecc68061e67336c47833ef9d22caa74dda10fa9675eebc" +checksum = "8d8c8b8e725ad26404d6896eb2f064294a0a7b4f1d9120210d85544684c4ca95" dependencies = [ "is-wsl", "libc", @@ -4595,18 +4640,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "opus-decoder" -version = "0.1.1" -dependencies = [ - "thiserror 2.0.21", -] - [[package]] name = "opus-rs" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1880f411a21320dcb546c8f38f05ffb75643362ee0235437487999ed543e26ae" +checksum = "5d664b01dda81206fd3c80582eafd8dfef0e936eea1c2ac90ed408823bbb410c" [[package]] name = "orbclient" @@ -5268,9 +5306,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e" +checksum = "580c875845b41ecb1564ad1ae4703044cef774145e3d7a94dc2b3fa7c97868fd" dependencies = [ "bitflags 2.13.2", ] @@ -5523,9 +5561,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +checksum = "4a073f5dc7469f984c52ad2752b63b0807745133b6de880b7b64c1ac4c48aec4" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -5545,9 +5583,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.15" +version = "0.103.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" dependencies = [ "ring", "rustls-pki-types", @@ -5562,15 +5600,15 @@ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rusty_h264-accel" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18110c8f692668ccf004393bee04e369d4f0f173dd0ea4b515080258e6f9c599" +checksum = "ecb2772a6bc2a2ca811ba4bff42cc0e10f25b411bc4ca1793c90f3265566f609" [[package]] name = "rusty_h264-common" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf982ae23ecf8f13015ec1c5576fe2935f4e4296b097ebc4233b5872623af2a" +checksum = "5b9ab840d02dd582db8dc0a2f5e68fa75e222246ba378af567b578098c2b4cfc" dependencies = [ "bytemuck", "once_cell", @@ -5581,18 +5619,18 @@ dependencies = [ [[package]] name = "rusty_h264-decoder" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4deb1a3a5e928cbaa1dff7db6bc46262c850aedeb75cb5aeab890494c18a5" +checksum = "f08d16e3c51b0b190f731c8a214ade76a1ac30b94ba3933f93b740dd909f8155" dependencies = [ "rusty_h264-common", ] [[package]] name = "rusty_h264-encoder" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e037f6341a02f54021f7077735bd12e2fd61a749e1ee7248eee00466ef5fa4ba" +checksum = "99277b8dcb73a794d7084ee17fa4953ac9845b568d350ceede534fa2984a631e" dependencies = [ "rusty_h264-accel", "rusty_h264-common", @@ -5624,9 +5662,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +checksum = "9cb23c424d8ee841bb4d813677ca237e2a610067d594fec130f87d6093495ab5" dependencies = [ "windows-sys 0.61.2", ] @@ -5645,9 +5683,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sctp-proto" -version = "0.10.5" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250132c5814c3d4f01269ee7a261450bc38dd5119280262d3c13a40062cfde55" +checksum = "bba218fe5119005820ab5aa74ffae0f1267763ae242541f5787ef42427cff262" dependencies = [ "bytes", "crc", @@ -5739,14 +5777,14 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" dependencies = [ "itoa", "memchr", @@ -5763,7 +5801,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -5800,9 +5838,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +checksum = "47d7069beb7d6ac7b9acd1039986e73443f24234f41074da099d6f994ac9ad19" dependencies = [ "cfg-if 1.0.5", "cpufeatures", @@ -5895,9 +5933,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.16.2" +version = "1.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" +checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8" dependencies = [ "serde", ] @@ -5909,8 +5947,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.13.2", - "calloop", - "calloop-wayland-source", + "calloop 0.13.0", + "calloop-wayland-source 0.3.0", "cursor-icon", "libc", "log", @@ -5927,14 +5965,41 @@ dependencies = [ "xkeysym", ] +[[package]] +name = "smithay-client-toolkit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" +dependencies = [ + "bitflags 2.13.2", + "calloop 0.14.5", + "calloop-wayland-source 0.4.1", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 1.1.5", + "thiserror 2.0.21", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-experimental", + "wayland-protocols-misc", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + [[package]] name = "smithay-clipboard" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" +checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226" dependencies = [ "libc", - "smithay-client-toolkit", + "smithay-client-toolkit 0.20.0", "wayland-backend", ] @@ -6295,9 +6360,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.6" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" dependencies = [ "proc-macro2", "quote", @@ -6321,7 +6386,7 @@ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -6434,7 +6499,7 @@ checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -6509,9 +6574,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" dependencies = [ "displaydoc", "zerovec", @@ -6525,9 +6590,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "tokio" -version = "1.53.2" +version = "1.54.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" dependencies = [ "bytes", "libc", @@ -6548,7 +6613,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -6579,9 +6644,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b" dependencies = [ "bytes", "futures-core", @@ -6795,9 +6860,9 @@ dependencies = [ [[package]] name = "typed-path" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" +checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e" [[package]] name = "typeid" @@ -6842,9 +6907,9 @@ checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f" [[package]] name = "unicode-ident" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" [[package]] name = "unicode-segmentation" @@ -6928,9 +6993,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.27.0" +version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97277d36b9c3ace13e58fa6e753f8b0bbbf302a18dd193240d70f9e29681059a" +checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -7080,7 +7145,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "wasm-bindgen-shared", ] @@ -7166,6 +7231,32 @@ dependencies = [ "wayland-scanner", ] +[[package]] +name = "wayland-protocols-experimental" +version = "20250721.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-misc" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + [[package]] name = "wayland-protocols-plasma" version = "0.3.12" @@ -7796,7 +7887,7 @@ dependencies = [ "bitflags 2.13.2", "block2 0.5.1", "bytemuck", - "calloop", + "calloop 0.13.0", "cfg_aliases", "concurrent-queue", "core-foundation 0.9.4", @@ -7817,7 +7908,7 @@ dependencies = [ "raw-window-handle", "redox_syscall 0.4.1", "rustix 0.38.44", - "smithay-client-toolkit", + "smithay-client-toolkit 0.19.2", "smol_str", "tracing", "unicode-segmentation", @@ -7846,9 +7937,9 @@ dependencies = [ [[package]] name = "winresource" -version = "0.1.31" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087" +checksum = "a2bc6d36480b829272a5bdabf08e6dca8004761c1da7e35efef800540c66221b" dependencies = [ "version_check", ] @@ -7996,9 +8087,9 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "yoke" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -8013,7 +8104,7 @@ checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "synstructure", ] @@ -8106,7 +8197,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "zbus_names", "zvariant", "zvariant_utils", @@ -8114,9 +8205,9 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.4" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" +checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd" dependencies = [ "serde", "winnow", @@ -8146,18 +8237,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.62" +version = "0.8.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86502bf56ac7c77571a32e2647bb2a15894565e981fb2a48d7bde2d91c965a9d" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.62" +version = "0.8.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5457206954b06561e2608c7e19cf58b1926586d999c246eebe4502f7e2039d1a" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" dependencies = [ "proc-macro2", "quote", @@ -8181,7 +8272,7 @@ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "synstructure", ] @@ -8204,9 +8295,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" dependencies = [ "yoke", "zerofrom", @@ -8215,27 +8306,29 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] name = "zip" -version = "9.0.5" +version = "9.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e04976da629d3b04204ba8105b396a193af8ad2583b2c79bfb3ab8af580a11e" +checksum = "ec2e3161de27091f18a831d9494e9c53378a34833af5c1dda35ad85849da18d6" dependencies = [ "crc32fast", "flate2", + "getrandom 0.4.3", "indexmap", "memchr", "nix", "typed-path", + "windows-sys 0.61.2", "zopfli", ] @@ -8253,9 +8346,9 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zopfli" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +checksum = "aaf7fc5d30c28483d93805c4a5e12b05bbb52407fa67c5f8bd552374cd01fb11" dependencies = [ "bumpalo", "crc32fast", @@ -8280,9 +8373,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.15.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147" +checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7" dependencies = [ "endi", "enumflags2", @@ -8295,14 +8388,14 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.15.0" +version = "5.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" +checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "zvariant_utils", ] @@ -8315,6 +8408,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 3.0.6", + "syn 3.0.9", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 9ae2c325..5b678464 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,8 +127,6 @@ str0m = { version = "0.24.1", default-features = false, features = ["openssl"] } # The certificate Chime's media server shows, read for the log only # (str0m-openssl's own OpenSSL, so no crate is added). openssl = "0.10.78" -# Opus in pure Rust, without unsafe code or a C library to find. -opus-decoder = "0.1.1" # The TURN client's long-term credential: HMAC-SHA1 (sha1 is above) keyed # with MD5. hmac = "0.13" @@ -141,14 +139,16 @@ rustls-native-certs = "0.8" tracing = { version = "0.1.44", default-features = false, features = ["log", "std"] } # Talking in a huddle: the microphone through cpal (rodio's), WebRTC's -# audio processing and an Opus encoder, all pure Rust. -# opus-rs: libopus 1.6 ported (BSD-3-Clause, no dependencies); its unsafe -# is SIMD and unchecked indexing. sonora: WebRTC's AEC3, noise +# audio processing and Opus, all pure Rust. +# opus-rs: libopus 1.6 ported (BSD-3-Clause, no dependencies), encoding +# what we send and decoding what we hear (0.1.39 plays a mono packet in a +# stereo decoder; 0.1.40 decodes in-band FEC and conceals loss like +# libopus); its unsafe is SIMD and unchecked indexing. sonora: WebRTC's AEC3, noise # suppression, AGC2 and high-pass filter, M145, ported (BSD-3-Clause); # its unsafe is SIMD behind runtime CPU detection. sonora-agc2's RNN # voice detector decides what DTX leaves out; sonora-common-audio's sinc # resampler brings a 44.1 kHz microphone to 48. Both come with sonora. -opus-rs = "0.1.34" +opus-rs = "0.1.40" sonora = "0.2.0" sonora-agc2 = "0.2.0" sonora-common-audio = "0.2.0" @@ -369,11 +369,6 @@ opt-level = 2 debug = false [patch.crates-io] -# opus-decoder 0.1.1 with two fixes (vendor/opus-decoder/VENDORED.md): a -# hybrid packet naming more redundancy bytes than it holds panicked (in -# release too, which aborts the app), and frames with 16 short blocks -# overflowed a shift in debug builds. Drop once upstream has both. -opus-decoder = { path = "vendor/opus-decoder" } # crmne/egui apps-0.36, shared with ZapFast and Spotifast: egui 0.36.1 with # bidi shaping, egui's busy-loop fix and Wayland frame pacing. Every egui # crate comes from the same revision so they share one emath and epaint. diff --git a/TODO.md b/TODO.md index 3e46255c..d6a63137 100644 --- a/TODO.md +++ b/TODO.md @@ -808,7 +808,9 @@ engineering, as for the rest of the session sign-in. decided it. - Opus: **`opus-decoder`** (pure Rust, `forbid(unsafe_code)`, no C, MIT/Apache, passes the 12 RFC 8251 vectors by its own account, - but young: 0.1, March 2026). libopus through the `opus` crate + but young: 0.1, March 2026). Since 2026-10-23 playback is `opus-rs` + instead, which also encodes: one dependency, and its SILK + concealment measured close to libopus where opus-decoder's was not. libopus through the `opus` crate builds its bundled C with CMake on Windows and macOS (or needs the system's library on Linux, which the Flatpak runtime has); it is the fallback if the decoder sounds wrong. It has not. @@ -865,19 +867,10 @@ engineering, as for the rest of the session sign-in. 440 Hz tone instead of the microphone, logging what was sent and Chime's RTCP receiver reports every 5 s. The loopback test has the pretend Chime decode our Opus and hear the mute. - - In-band FEC is off, an upstream `opus-rs` bug to report: with - 0.1.34, `OpusEncoder::new(48000, 1, Application::Voip)`, - `use_inband_fec = true` and `packet_loss_perc = 10`, one second of - a 0.3 sine at 440 Hz decodes to 3.9 times the energy put in (5.2 - times for a 120 Hz harmonic "vowel"; 2.7 to 3.3 with CBR; 1.8 to - 4.6 across 16–40 kbit/s), frame levels swinging from -1 to -50 - dBFS where -14 went in. `opus-decoder` and `opus-rs`'s own - decoder agree sample for sample, so the encoder's LBRR is at - fault. With FEC off: 0.95 to 0.98, as it should be; white noise - is fine either way. Turn it on when fixed; - `encoded_speech_decodes_back_with_both_decoders` shows it. - Playing stays on `opus-decoder`: `opus-rs`'s decoder conceals a - loss but cannot decode FEC. + - In-band FEC is on since `opus-rs` 0.1.36 fixed its LBRR (0.1.34 + decoded voiced sound at up to 5.2 times the energy put in); + `encoded_speech_decodes_back` guards it, and the speaker plays the + far end's copies (`a_lost_frame_plays_from_the_copy_in_the_next`). - Cost: `opus-rs` (no dependencies; its unsafe is SIMD and unchecked indexing), `sonora` and its six crates (unsafe only in SIMD behind runtime CPU detection), `derive_more`; all @@ -935,119 +928,11 @@ engineering, as for the rest of the session sign-in. devices instead. - [ ] Cameras of one model: `/dev/v4l/by-id/` ids from the helper would tell them apart across replugging. -- [ ] **File upstream: opus-decoder's collapse mask overflows.** A real - huddle stopped playing in a debug build: opus-decoder panicked on - the sound device's thread. Fixed in our patched copy - (`vendor/opus-decoder`, see its VENDORED.md; still unfixed on - Rusopus main `ecb22cf` as of 2026-10-07); drop the copy once a - fixed release is out. To file at - : - - > **`extract_collapse_mask` overflows its `u8` with 16 short blocks - > (attempt to shift left with overflow, celt/vq.rs:118)** - > - > opus-decoder 0.1.1, `src/celt/vq.rs`: - > - > ```rust - > fn extract_collapse_mask(iy: &[i32], n: usize, b: usize) -> u8 { - > ... - > let mut mask = 0u8; - > for i in 0..b { - > ... - > if nonzero != 0 { - > mask |= 1 << i; // line 118 - > ``` - > - > `b` can be 16: a transient 20 ms CELT frame has 8 short blocks, - > and a negative `tf_change` doubles them once more (the - > `time_divide` step before `quant_partition_mono`, undone later by - > `post_tf_collapse_mask`). With overflow checks on - > (any debug build) `1 << i` panics for `i >= 8`: - > - > ``` - > thread 'cpal_alsa_out' panicked at - > opus-decoder-0.1.1/src/celt/vq.rs:118:21: - > attempt to shift left with overflow - > ``` - > - > libopus keeps this mask in an `unsigned` (`celt/vq.c`, - > `static unsigned extract_collapse_mask(int *iy, int N, int B)`, - > and `alg_unquant` returns `unsigned`); the bits above 8 are folded - > down by `quant_band`'s `cm |= cm >> B` after the time-divide - > Haar steps, and only the final mask is stored as `unsigned char`. - > This crate's `post_tf_collapse_mask` already works on a `u32`, so - > the fix is to make `AlgUnquantResult::collapse_mask` and - > `extract_collapse_mask` `u32` (the `as u32` at bands.rs:1301 then - > goes). - > - > Reproduce: decode this 55-byte packet with - > `OpusDecoder::new(48_000, 2)` and `decode_float(.., false)` in a - > debug build: - > - > ``` - > f8 75 d5 48 6a 8c cf b8 7d b1 d2 3b 43 7d 6b 6b 17 c1 14 fe 7d - > a5 ae 93 56 58 c4 69 d1 30 da 3f 75 ab 8e ab 1c 2c 0e f2 e7 e0 - > 6b f5 08 84 7d 51 67 f9 16 30 90 1d 29 - > ``` - > - > About 1.4% of random 20 ms CELT packets hit it. In release builds - > the shift wraps to bit `i % 8`; after the fold that gives the same - > final mask, so the output matched a `u32` build bit for bit over - > 20,000 random packets. The panic is the only harm, but it takes - > down the audio thread of whatever is decoding. -- [ ] **File upstream: a malformed hybrid packet panics opus-decoder in - release builds too.** Fixed in our patched copy - (`vendor/opus-decoder`, libopus's `len*8 < ec_tell` check; still - unfixed on Rusopus main `ecb22cf`). Found by - fuzzing while fixing the above: about 1 in 80,000 random packets. - Our release builds abort on a panic, so any huddle participant (or - Chime) sending such a packet closes NoSlacking for everyone - listening; SRTP rules out corruption on the way, not a hostile - sender. Debug builds catch it now (`audio::guard`). Until upstream - fixes it we carry the copy. To file: - - > **Hybrid redundancy longer than the frame: `range start index - > out of range` (lib.rs:676 and :724)** - > - > In `OpusMode::Hybrid`, `redundancy_bytes = ec.dec_uint(256) + 2` - > is read from the packet and then used as - > `&frame[frame.len() - redundancy_bytes..]` without checking it - > against `frame.len()`. A frame shorter than the redundancy it - > claims underflows the subtraction and panics ("range start index - > 18446744073709551534 out of range for slice of length 76" in a - > release build). - > - > libopus (`src/opus_decoder.c`, `opus_decode_frame`) checks - > right after reading it: - > - > ```c - > len -= redundancy_bytes; - > /* This is a sanity check. It should never happen for a valid - > packet, so the exact behaviour is not normative. */ - > if (len*8 < ec_tell(&dec)) - > { - > len = 0; - > redundancy_bytes = 0; - > redundancy = 0; - > } - > ``` - > - > Reproduce with a fresh `OpusDecoder::new(48_000, 1)` and - > `decode_float(.., false)`, release or debug, on this 153-byte - > packet (TOC 0x69, two 76-byte hybrid frames): - > - > ``` - > 69 af 0e e6 85 96 57 1b b5 8c c1 a2 21 35 a5 94 3d 0d 19 d1 c8 e7 - > 7c d0 63 03 bd c4 31 df 4e 76 64 f8 27 93 e6 c1 2e 44 06 6c 2e a8 - > df a2 5d 8f b0 e1 a8 8f ce 1f d7 8a 47 af 68 f8 71 37 f5 9e 65 a3 - > 2a 18 28 26 82 e1 88 a7 b8 27 ad 60 fa 63 9d 18 42 b4 b7 92 e3 60 - > 34 5e 40 7e 7c ee 8b 98 8f 1c de 63 ad 44 ce 75 0b 2f 15 f7 be 4f - > 2d a3 9e 57 bb a8 dd bc f9 0a 3a 14 c2 73 f7 34 98 bb 54 28 c4 db - > fe 4d 7f 97 0a 58 92 0b 47 59 37 15 85 30 e0 b1 78 d8 9f 26 72 - > ``` - > - > A decoder fed by the network should turn this into an error (or - > libopus's behaviour), never a panic. +- [ ] **opus-rs from crates.io again.** Playing uses our fork + (Yannicked/opus-rs, pinned in `[patch.crates-io]`) for in-band FEC + decoding (restsend/opus-rs#67) and libopus's concealment (#68). Drop + the patch and the fork's line in deny.toml once a release has both. + The Rusopus PRs (#5–#7) stay open for opus-decoder's sake. ## Research notes diff --git a/deny.toml b/deny.toml index db331586..3a015701 100644 --- a/deny.toml +++ b/deny.toml @@ -69,8 +69,8 @@ allow-wildcard-paths = true unknown-registry = "deny" unknown-git = "deny" allow-registry = ["https://github.com/rust-lang/crates.io-index"] -# The shared fastframe crates and the egui and winit forks they build on -# (see [patch.crates-io] in Cargo.toml). Nothing else may come from git. +# The shared fastframe crates and the egui and winit forks they build on. +# Nothing else may come from git. allow-git = [ "https://github.com/crmne/fastframe", "https://github.com/crmne/egui", diff --git a/src/backend/teams_call.rs b/src/backend/teams_call.rs index 693003ac..df3f6b51 100644 --- a/src/backend/teams_call.rs +++ b/src/backend/teams_call.rs @@ -145,14 +145,23 @@ struct Wanted { /// A call's controls: for the caller to keep, and for the call's task /// (hang-up and mute for the call, and what its devices should do). +/// A call starts unmuted; a meeting (`muted`) muted, as a huddle does, +/// so joining one is never heard before you choose to speak. fn controls( chosen: &crate::devices::Chosen, + muted: bool, ) -> (Running, mpsc::UnboundedReceiver, Wanted) { let (control, controls) = mpsc::unbounded_channel(); let (devices, chosen) = watch::channel(chosen.clone()); - let (muted, microphone) = watch::channel(true); - // Unmuted from the start: the microphone task opens it on this. - let _ = muted.send(false); + let (mute, microphone) = watch::channel(true); + if muted { + // The call mutes its sound and tells the meeting as soon as it + // takes its controls, as when you mute. + let _ = control.send(Control::Mute(true)); + } else { + // Unmuted from the start: the microphone task opens it on this. + let _ = mute.send(false); + } // The camera starts off. #[cfg(feature = "huddle-camera")] let (camera_sender, camera) = watch::channel(false); @@ -165,7 +174,7 @@ fn controls( devices, call_id: None, control, - muted, + muted: mute, #[cfg(feature = "huddle-camera")] camera: camera_sender, #[cfg(feature = "huddle-share")] @@ -194,7 +203,7 @@ impl Caller { sink: Sink, ) { self.stop(); - let (mut running, controls, wanted) = controls(&self.chosen); + let (mut running, controls, wanted) = controls(&self.chosen, false); running.team.clone_from(&team); tokio::spawn(run( client, @@ -226,7 +235,7 @@ impl Caller { if let Some(old) = self.ringing.take() { let _ = old.decide.send(Decision::Decline); } - let (mut running, controls, wanted) = controls(&self.chosen); + let (mut running, controls, wanted) = controls(&self.chosen, false); running.team.clone_from(&team); let (decide, decisions) = oneshot::channel(); let call_id = call.call_id.clone(); @@ -242,7 +251,7 @@ impl Caller { /// ending the last call first. pub fn join_meeting(&mut self, client: TeamsClient, team: String, join: Join, sink: Sink) { self.stop(); - let (mut running, controls, wanted) = controls(&self.chosen); + let (mut running, controls, wanted) = controls(&self.chosen, true); running.team.clone_from(&team); tokio::spawn(run_meeting(client, team, join, controls, wanted, sink)); self.running = Some(running); @@ -413,7 +422,9 @@ fn roster(callee: &str, far: FarEnd) -> Roster { /// the interface knows people by, their names for any it does not), and /// you. The meeting mixes everyone's sound, so whoever it says spoke /// last (`speaker`, by MRI) is speaking while that sound is loud -/// (`far.speaking`); you as `far.me_speaking` says. +/// (`far.speaking`), unless muted since: the speaker is often only the +/// one from before we joined, and a muted one cannot be the sound; you +/// as `far.me_speaking` says. fn meeting_roster(people: &[Attendee], speaker: Option<&str>, far: FarEnd) -> Roster { let mut everyone: Vec = people .iter() @@ -425,6 +436,7 @@ fn meeting_roster(people: &[Attendee], speaker: Option<&str>, far: FarEnd) -> Ro me: false, muted: attendee.muted, speaking: far.speaking + && !attendee.muted && speaker.is_some_and(|s| s.eq_ignore_ascii_case(&attendee.mri)), name: attendee.name.clone(), waiting: attendee.waiting, @@ -998,7 +1010,7 @@ impl Shown<'_, T> { (self.tell)(Listen::Roster(self.roster())); } CallEvent::Speaker(mri) => { - self.speaker = Some(mri); + self.speaker = mri; (self.tell)(Listen::Roster(self.roster())); } CallEvent::Ended { result, .. } => self.ended = Some(result), @@ -1284,6 +1296,16 @@ mod tests { // No one named yet: no one. let roster = meeting_roster(&people, None, loud); assert!(roster.people.iter().all(|p| !p.speaking)); + // The last one named has muted since: someone else is the sound. + let people = [ + attendee("8:orgid:ana"), + Attendee { + muted: true, + ..attendee("8:orgid:bo") + }, + ]; + let roster = meeting_roster(&people, Some("8:orgid:bo"), loud); + assert!(roster.people.iter().all(|p| !p.speaking)); } #[test] diff --git a/src/huddle_audio.rs b/src/huddle_audio.rs index 78541742..e911d169 100644 --- a/src/huddle_audio.rs +++ b/src/huddle_audio.rs @@ -51,6 +51,7 @@ pub mod bitstream; #[cfg(feature = "huddle-camera")] pub mod camera_send; pub mod cameras; +pub mod capture; pub mod chime; #[cfg(feature = "huddle-video")] pub mod decode; diff --git a/src/huddle_audio/capture.rs b/src/huddle_audio/capture.rs new file mode 100644 index 00000000..cf54aea8 --- /dev/null +++ b/src/huddle_audio/capture.rs @@ -0,0 +1,159 @@ +//! A capture of what a call's speaker is given and plays, for finding +//! faults in the sound offline: opt in by naming a directory in +//! `NOSLACKING_AUDIO_CAPTURE`. Off otherwise, and never on by itself, +//! since it holds what was said. +//! +//! Three files go in the directory, written on a thread of their own so +//! the sound device's thread never waits on the disk: +//! +//! - `packets.bin`: each Opus packet as it came, as microseconds since +//! the capture began (u64), its RTP timestamp (u32) and its length +//! (u16), little-endian, then its bytes; +//! - `pulls.txt`: what the jitter buffer gave for each pull, by the +//! samples per channel played before it: `frame `, `lost` or +//! `silence`; +//! - `played.f32`: the sound as handed to the device, 48 kHz stereo, +//! interleaved little-endian `f32`. + +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::{Path, PathBuf}; +use std::sync::mpsc; +use std::time::Instant; + +use super::jitter::Pull; + +/// The variable naming the directory to capture into. +pub const VARIABLE: &str = "NOSLACKING_AUDIO_CAPTURE"; + +/// One thing to write. +enum Item { + Packet { + at: Instant, + timestamp: u32, + payload: Vec, + }, + Pull(String), + Played(Vec), +} + +/// The capture's end the speaker writes to. +#[derive(Debug)] +pub struct Capture { + items: mpsc::Sender, +} + +impl Capture { + /// A capture into the directory `NOSLACKING_AUDIO_CAPTURE` names, if + /// it names one that can be written to. + pub fn from_env() -> Option { + let dir = PathBuf::from(std::env::var_os(VARIABLE)?); + match Self::start(&dir) { + Ok(capture) => { + log::info!( + "huddle audio: capturing the far end's sound in {}", + dir.display() + ); + Some(capture) + } + Err(error) => { + log::warn!("huddle audio: no capture in {}: {error}", dir.display()); + None + } + } + } + + fn start(dir: &Path) -> std::io::Result { + std::fs::create_dir_all(dir)?; + let open = |name: &str| File::create(dir.join(name)).map(BufWriter::new); + let mut files = Files { + packets: open("packets.bin")?, + pulls: open("pulls.txt")?, + played: open("played.f32")?, + start: Instant::now(), + samples: 0, + }; + let (items, received) = mpsc::channel(); + std::thread::Builder::new() + .name("audio capture".into()) + .spawn(move || { + for item in received { + if let Err(error) = files.write(item) { + log::warn!("huddle audio: the capture stopped: {error}"); + return; + } + } + let _ = files.flush(); + })?; + Ok(Self { items }) + } + + /// A packet as it came. + pub fn packet(&self, timestamp: u32, payload: &[u8]) { + let _ = self.items.send(Item::Packet { + at: Instant::now(), + timestamp, + payload: payload.to_vec(), + }); + } + + /// What the jitter buffer gave. + pub fn pull(&self, pull: &Pull) { + let text = match pull { + Pull::Frame(payload) => format!("frame {}", payload.len()), + Pull::Lost => "lost".to_owned(), + Pull::Recover(next) => format!("recover {}", next.len()), + Pull::Silence => "silence".to_owned(), + }; + let _ = self.items.send(Item::Pull(text)); + } + + /// Sound as handed to the device, interleaved stereo. + pub fn played(&self, samples: &[f32]) { + let _ = self.items.send(Item::Played(samples.to_vec())); + } +} + +/// The files, on the capture's thread. +struct Files { + packets: BufWriter, + pulls: BufWriter, + played: BufWriter, + start: Instant, + /// Samples per channel played so far. + samples: u64, +} + +impl Files { + fn write(&mut self, item: Item) -> std::io::Result<()> { + match item { + Item::Packet { + at, + timestamp, + payload, + } => { + let micros = + u64::try_from(at.duration_since(self.start).as_micros()).unwrap_or(u64::MAX); + let length = u16::try_from(payload.len()).unwrap_or(u16::MAX); + self.packets.write_all(µs.to_le_bytes())?; + self.packets.write_all(×tamp.to_le_bytes())?; + self.packets.write_all(&length.to_le_bytes())?; + self.packets.write_all(&payload[..usize::from(length)])?; + } + Item::Pull(text) => writeln!(self.pulls, "{} {text}", self.samples)?, + Item::Played(samples) => { + self.samples += (samples.len() / 2) as u64; + for sample in samples { + self.played.write_all(&sample.to_le_bytes())?; + } + } + } + Ok(()) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.packets.flush()?; + self.pulls.flush()?; + self.played.flush() + } +} diff --git a/src/huddle_audio/encoder.rs b/src/huddle_audio/encoder.rs index d4971acf..94a77aeb 100644 --- a/src/huddle_audio/encoder.rs +++ b/src/huddle_audio/encoder.rs @@ -9,17 +9,15 @@ //! loss. `opus-rs` has no DTX of its own; [`super::uplink::Dtx`] leaves //! quiet frames out instead. //! -//! In-band FEC is off, where a browser has it on: with it, `opus-rs` -//! 0.1.34's VOIP packets at 48 kHz decode wrong whenever the sound is -//! voiced (a tone, a vowel), up to five times the energy put in, the same -//! in `opus-decoder` and in `opus-rs`'s own decoder, so the encoder's -//! low-bitrate copies are at fault. Noise came through; speech would not. -//! `encoded_speech_decodes_back_with_both_decoders` would catch it coming -//! back. Losses are concealed by the far end's decoder instead. +//! In-band FEC is on, as a browser has it: each packet carries a +//! low-bitrate copy of the one before, which the far end's decoder (libopus +//! in Chime and Teams) plays when that one is lost. `opus-rs` before 0.1.36 +//! got the copies wrong, so voiced sound decoded at up to five times the +//! energy put in; `encoded_speech_decodes_back` would catch that coming +//! back. //! -//! Playing stays with `opus-decoder` ([`super::speaker`]): `opus-rs`'s -//! decoder conceals a lost frame, but it cannot decode the FEC copy a -//! packet carries of the one before, which `opus-decoder` can. +//! `opus-rs` decodes what we hear too ([`super::speaker`]), and plays the +//! far end's FEC copies the same way. use super::jitter::CLOCK; use super::uplink::TWENTY_MS; @@ -51,14 +49,14 @@ impl std::fmt::Debug for OpusRs { } impl OpusRs { - /// The speech encoder: mono, VOIP, [`BITRATE`], no FEC (see above). + /// The speech encoder: mono, VOIP, [`BITRATE`], in-band FEC (see above). pub fn speech() -> Result { let rate = i32::try_from(CLOCK).map_err(|e| e.to_string())?; let mut inner = opus_rs::OpusEncoder::new(rate, 1, opus_rs::Application::Voip) .map_err(|e| format!("no Opus encoder: {e}"))?; inner.bitrate_bps = BITRATE; inner.use_cbr = false; - inner.use_inband_fec = false; + inner.use_inband_fec = true; inner.packet_loss_perc = EXPECTED_LOSS; Ok(Self { inner, @@ -116,6 +114,25 @@ mod tests { (pcm, packets) } + #[test] + fn packets_carry_a_copy_of_the_one_before() { + let (_, with) = encoded(440.0); + let mut encoder = OpusRs::speech().expect("an encoder"); + encoder.inner.use_inband_fec = false; + let mut tone = Tone::new(440.0, 0.3); + let mut without = 0; + for _ in 0..50 { + let mut frame = vec![0.0; TWENTY_MS]; + tone.fill(&mut frame); + without += encoder.encode(&frame).expect("encoded").len(); + } + let with: usize = with.iter().map(Vec::len).sum(); + assert!( + with > without + 50 * 3, + "{with} bytes with FEC, {without} without" + ); + } + #[test] fn packets_are_twenty_ms_and_near_the_bitrate() { let (_, packets) = encoded(440.0); @@ -132,32 +149,23 @@ mod tests { ); } - /// What `opus-rs` encodes, the decoder that plays huddles - /// (`opus-decoder`) and `opus-rs`'s own both decode to the tone. + /// What `opus-rs` encodes decodes back to the tone. #[test] - fn encoded_speech_decodes_back_with_both_decoders() { + fn encoded_speech_decodes_back() { let (pcm, packets) = encoded(440.0); - - let mut ours = opus_decoder::OpusDecoder::new(CLOCK, 1).expect("a decoder"); - let mut theirs = opus_rs::OpusDecoder::new(48_000, 1).expect("a decoder"); - let mut by_ours = Vec::new(); - let mut by_theirs = Vec::new(); + let mut decoder = opus_rs::OpusDecoder::new(48_000, 1).expect("a decoder"); + let mut decoded = Vec::new(); for packet in &packets { - let mut out = vec![0.0; ours.max_frame_size_per_channel()]; - let n = ours.decode_float(packet, &mut out, false).expect("decoded"); - by_ours.extend_from_slice(&out[..n]); let mut out = vec![0.0; TWENTY_MS]; - let n = theirs.decode(packet, TWENTY_MS, &mut out).expect("decoded"); - by_theirs.extend_from_slice(&out[..n]); + let n = decoder + .decode(packet, TWENTY_MS, &mut out) + .expect("decoded"); + decoded.extend_from_slice(&out[..n]); } - assert_eq!(by_ours.len(), pcm.len()); - assert_eq!(by_theirs.len(), pcm.len()); + assert_eq!(decoded.len(), pcm.len()); // Past the first 100 ms, where the encoder settles; the codec // delays by a few milliseconds. let settled = &pcm[4800..pcm.len() - 960]; - assert!(residual(settled, &by_ours[4800..], 960) < 0.1); - assert!(residual(settled, &by_theirs[4800..], 960) < 0.1); - // Both decoders agree with each other too. - assert!(residual(&by_ours[4800..40_000], &by_theirs[4800..], 64) < 0.05); + assert!(residual(settled, &decoded[4800..], 960) < 0.1); } } diff --git a/src/huddle_audio/jitter.rs b/src/huddle_audio/jitter.rs index 239ee9d7..8fe402e8 100644 --- a/src/huddle_audio/jitter.rs +++ b/src/huddle_audio/jitter.rs @@ -3,13 +3,28 @@ //! //! It holds back a little (60 ms by default) before it starts, so packets //! that come late or out of order still play in their place. A frame that -//! never came is played as a loss, which the decoder conceals; when the -//! buffer runs dry it conceals a little more ([`DRY_CONCEALED`]), which -//! bridges a short stall and otherwise fades the sound out, then waits to -//! fill again, playing silence; when it grows too long (the device's clock -//! slower than the sender's, or a burst after a stall) the oldest frames -//! go, so the delay stays short, with one concealed frame across the cut. -//! Each of those would otherwise be a step in the sound: a pop. +//! never came is played as a loss, which the decoder conceals, unless the +//! frame after it is here: that one carries a low-bitrate copy of it +//! (in-band FEC), which the decoder plays instead ([`Pull::Recover`]). +//! It never conceals more than [`DRY_CONCEALED`] in a row, as a guess +//! stretched further turns to buzzing noise. A longer gap (the sender pausing, or a stall +//! that ran the buffer dry) goes silent after that, and the sound after +//! it fades in. +//! +//! How much it holds follows the network, as WebRTC's NetEQ does: it +//! times each frame against the sound device's clock, and a frame later +//! than the fastest raises the target by that much at once, forgotten +//! over a minute or so. A network that stalls now and then is given room +//! for the next stall; a calm one keeps the delay short. +//! +//! The burst that follows a stall is kept, not cut: everything said is +//! heard, a little late. The delay then comes back down in the pauses: +//! a gap between frames (the sender sends nothing while its speaker is +//! quiet) is shortened while more is held than the target, which nobody +//! misses, as no frame is passed over. Only when far too much is held ([`Jitter::new`]'s limit: the device's clock slower than +//! the sender's for long, or a stall of seconds) do the oldest frames go +//! at once, with one concealed frame across the cut. Each of those would +//! otherwise be a step in the sound: a pop. use std::collections::BTreeMap; @@ -20,6 +35,14 @@ pub const FRAME: u32 = CLOCK / 50; /// How many frames are concealed when the buffer runs dry before it goes /// silent: 60 ms, enough to bridge a short stall. pub const DRY_CONCEALED: u32 = 3; +/// The silence played per pull while the buffer fills: 10 ms. +pub const SILENCE: u32 = CLOCK / 100; +/// How fast a late frame is forgotten: one sample of headroom for every +/// this many played, so 280 ms of it lasts a little over a minute. +const FORGET: u64 = 256; +/// How far over the target the buffer may be before a pause is +/// shortened: two frames, so it does not chase the network's ripples. +const SLACK: u64 = 2 * FRAME as u64; /// How many 48 kHz samples one Opus packet holds, from its TOC byte and /// frame count (RFC 6716 §3.1). `None` for an empty or broken packet. @@ -49,6 +72,9 @@ pub enum Pull { Frame(Vec), /// The frame due was lost: conceal one frame's length. Lost, + /// The frame due was lost but the next one is here: play the copy of + /// the lost one it carries (in-band FEC), one frame's length. + Recover(Vec), /// Nothing to play yet: silence while the buffer fills. Silence, } @@ -64,10 +90,16 @@ pub struct Counts { pub late: u64, /// Frames thrown away to keep the delay short. pub trimmed: u64, + /// Frames' worth of pauses cut short to bring the delay back down. + pub skipped: u64, /// Lost frames concealed. pub concealed: u64, + /// Lost frames played from the next one's copy (in-band FEC). + pub recovered: u64, /// Times the buffer ran dry while playing. pub underruns: u64, + /// How much the buffer means to hold now, in milliseconds. + pub target_ms: u64, } /// The buffer. @@ -79,9 +111,19 @@ pub struct Jitter { last: Option<(u32, u64)>, /// The timestamp due next, while playing. next: Option, - /// How much to hold before playing, and the most to hold, in samples. - target: u64, + /// The least to hold before playing, and the most to hold, in + /// samples. + base: u64, limit: u64, + /// Held on top of `base`, in samples: how late frames came lately, + /// the latest at once, forgotten slowly. + headroom: u64, + /// Samples played (silence too): the sound device's clock. + now: u64, + /// The least delay seen, `now` less a frame's timestamp: a frame on + /// time. It creeps up a little every pull, so a sender's clock + /// slower than the device's is not taken for frames ever later. + fastest: Option, /// The length of the last frame, for concealing a lost one. frame: u64, /// Frames concealed since the buffer ran dry. @@ -95,21 +137,25 @@ pub struct Jitter { impl Default for Jitter { fn default() -> Self { - Self::new(60, 300) + Self::new(60, 1000) } } impl Jitter { - /// A buffer that holds `target_ms` before it plays and at most - /// `limit_ms`. + /// A buffer that holds at least `target_ms` before it plays, more + /// while the network is uneven (up to half of `limit_ms`), and at + /// most `limit_ms`. pub fn new(target_ms: u32, limit_ms: u32) -> Self { let per_ms = u64::from(CLOCK / 1000); Self { frames: BTreeMap::new(), last: None, next: None, - target: u64::from(target_ms) * per_ms, + base: u64::from(target_ms) * per_ms, limit: u64::from(limit_ms.max(target_ms)) * per_ms, + headroom: 0, + now: 0, + fastest: None, frame: u64::from(FRAME), dry: 0, bridge: false, @@ -120,7 +166,18 @@ impl Jitter { /// What happened to the frames so far. pub fn counts(&self) -> Counts { - self.counts + Counts { + target_ms: self.target() / u64::from(CLOCK / 1000), + ..self.counts + } + } + + /// How much to hold, in samples: the base and the headroom, the two + /// together no more than half the limit. + pub fn target(&self) -> u64 { + (self.base + self.headroom) + .min(self.limit / 2) + .max(self.base) } /// Whether the frame just pulled is the first after silence, so the @@ -164,6 +221,7 @@ impl Jitter { } let at = self.unwrap(timestamp); self.counts.pushed += 1; + self.time(at); if let Some(next) = self.next.filter(|next| at < *next) { // Run dry, the buffer conceals ahead of frames that are only // slow; one of those plays from its own time, a little later @@ -179,9 +237,10 @@ impl Jitter { } } self.frames.insert(at, payload); - // Too much held: drop the oldest until back at the target. + // Far too much held: drop the oldest until back at the target. if self.held() > self.limit { - while self.held() > self.target && self.frames.len() > 1 { + let target = self.target(); + while self.held() > target && self.frames.len() > 1 { self.frames.pop_first(); self.counts.trimmed += 1; } @@ -192,10 +251,43 @@ impl Jitter { } } + /// Times a frame due at `at` against the device's clock: later than + /// the fastest frame, it raises the headroom by that much. + fn time(&mut self, at: u64) { + let delay = i128::from(self.now) - i128::from(at); + match self.fastest { + Some(fastest) if delay >= fastest => { + let late = u64::try_from(delay - fastest).unwrap_or(u64::MAX); + if late > self.limit { + // The sender's clock went back: time from here. + self.fastest = Some(delay); + } else { + self.headroom = self.headroom.max(late); + } + } + _ => self.fastest = Some(delay), + } + } + /// What to play next. pub fn pull(&mut self) -> Pull { + let pull = self.choose(); + let played = match &pull { + Pull::Frame(payload) => u64::from(opus_samples(payload).unwrap_or(FRAME)), + Pull::Lost | Pull::Recover(_) => self.frame, + Pull::Silence => u64::from(SILENCE), + }; + self.now += played; + self.headroom = self.headroom.saturating_sub(played / FORGET); + if let Some(fastest) = &mut self.fastest { + *fastest += 1; + } + pull + } + + fn choose(&mut self) -> Pull { let Some(next) = self.next else { - if self.held() < self.target { + if self.held() < self.target() { return Pull::Silence; } let Some((at, payload)) = self.frames.pop_first() else { @@ -227,16 +319,46 @@ impl Jitter { }; return self.play(at, payload); } - if first - next > self.limit { + let gap = first - next; + if gap > self.limit { // A jump (the sender restarted its clock): carry on from there. let Some((at, payload)) = self.frames.pop_first() else { return Pull::Silence; }; return self.play(at, payload); } - self.next = Some(next + self.frame); - self.counts.concealed += 1; - Pull::Lost + // A pause, not a lost frame or so, with more ahead than wanted: + // cut it short, as far as the excess goes. + let over = (self.held() + gap).saturating_sub(self.target()); + if gap >= 2 * self.frame && over > SLACK { + let cut = gap.min(over); + self.next = Some(next + cut); + self.counts.skipped += cut / self.frame.max(1); + self.resumed = true; + return self.choose(); + } + // One frame lost, and the one after it here: it carries a copy. + // Not once gone silent: then the sender paused, and sent none. + if gap == self.frame + && self.dry < DRY_CONCEALED + && let Some(after) = self.frames.get(&first) + { + self.dry = 0; + self.next = Some(first); + self.counts.recovered += 1; + return Pull::Recover(after.clone()); + } + if self.dry < DRY_CONCEALED { + self.dry += 1; + self.next = Some(next + self.frame); + self.counts.concealed += 1; + return Pull::Lost; + } + // Concealed as long as it sounds right: silence to the next frame, + // which fades in. + self.next = Some((next + u64::from(SILENCE)).min(first)); + self.resumed = true; + Pull::Silence } fn play(&mut self, at: u64, payload: Vec) -> Pull { @@ -266,6 +388,98 @@ mod tests { } } + /// A 20 ms CELT frame `len` bytes long, named `n`. + fn sized(n: u8, len: usize) -> Vec { + let mut payload = frame(n); + payload.resize(len, 0); + payload + } + + #[test] + fn a_stall_raises_the_target_and_the_burst_after_it_is_kept() { + let mut jitter = Jitter::new(60, 1000); + // Frames every 20 ms, but 50 to 63 held up and then all at once + // with 64: a 280 ms stall, as Teams' relay does now and then. + let arrives = |n: u64| if (50..64).contains(&n) { 64 } else { n }; + let mut sent = 0u64; + let mut heard = Vec::new(); + while jitter.now < 200 * u64::from(FRAME) { + while arrives(sent) * u64::from(FRAME) <= jitter.now { + let n = u8::try_from(sent).expect("under 256 frames"); + jitter.push(u32::from(n) * FRAME, sized(n, 40)); + sent += 1; + } + heard.extend(played(jitter.pull())); + } + let counts = jitter.counts(); + assert_eq!((counts.trimmed, counts.late, counts.skipped), (0, 0, 0)); + assert!(heard.windows(2).all(|w| w[1] == w[0] + 1), "{heard:?}"); + assert!((40..=64).all(|n| heard.contains(&n)), "the stall heard"); + assert!(counts.target_ms >= 300, "{counts:?}"); + // So the next one like it is ridden out without a sound missing. + let underruns = counts.underruns; + let concealed = counts.concealed; + let arrives = |n: u64| if (150..164).contains(&n) { 164 } else { n }; + while jitter.now < 400 * u64::from(FRAME) { + while arrives(sent) * u64::from(FRAME) <= jitter.now { + let n = u8::try_from(sent % 256).expect("modulo 256"); + jitter.push((sent * u64::from(FRAME)) as u32, sized(n, 40)); + sent += 1; + } + jitter.pull(); + } + assert_eq!(jitter.counts().underruns, underruns); + assert_eq!(jitter.counts().concealed, concealed); + } + + #[test] + fn the_delay_comes_back_down_in_the_pauses() { + let mut jitter = Jitter::new(20, 1000); + jitter.push(0, frame(0)); + assert_eq!(played(jitter.pull()), Some(0)); + // 140 ms of speech come at once, on time: 120 more than the + // target. Speech is never cut. + for n in 1..8 { + jitter.push(u32::from(n) * FRAME, frame(n)); + } + for n in 1..4 { + assert_eq!(played(jitter.pull()), Some(n)); + } + // Then a pause of 200 ms before frame 18: it is cut short, so + // frame 18 follows 7 sooner than its time, and fades in. + jitter.push(18 * FRAME, frame(18)); + for n in 4..8 { + assert_eq!(played(jitter.pull()), Some(n)); + } + assert_eq!(played(jitter.pull()), Some(18)); + assert!(jitter.take_resumed()); + assert!(jitter.counts().skipped >= 2, "{:?}", jitter.counts()); + assert_eq!(jitter.counts().concealed, 0); + } + + #[test] + fn a_long_gap_is_concealed_a_little_then_silent() { + // A deep buffer: the sender pauses 300 ms, and what follows is + // already here, held as long as wanted, no longer. + let mut jitter = Jitter::new(300, 1000); + jitter.push(0, frame(0)); + jitter.push(16 * FRAME, frame(16)); + assert_eq!(played(jitter.pull()), Some(0)); + let mut pulls = Vec::new(); + loop { + let pull = jitter.pull(); + if let Some(n) = played(pull.clone()) { + assert_eq!(n, 16); + break; + } + pulls.push(pull); + } + let lost = pulls.iter().filter(|p| **p == Pull::Lost).count(); + assert_eq!(lost, DRY_CONCEALED as usize, "{pulls:?}"); + assert!(pulls.iter().skip(lost).all(|p| *p == Pull::Silence)); + assert!(jitter.take_resumed(), "the sound after fades in"); + } + #[test] fn opus_packets_say_how_long_they_are() { assert_eq!(opus_samples(&[0xF8, 0xFF, 0xFE]), Some(960), "20 ms CELT"); @@ -285,12 +499,15 @@ mod tests { let mut jitter = Jitter::new(60, 300); jitter.push(1000, frame(1)); assert_eq!(jitter.pull(), Pull::Silence, "20 ms held, 60 wanted"); - // Out of order: 3 before 2. + // Out of order: 3 before 2. That is 2 late by a frame, so it + // wants a frame more before it plays. jitter.push(1000 + 2 * FRAME, frame(3)); jitter.push(1000 + FRAME, frame(2)); - assert_eq!(played(jitter.pull()), Some(1)); - assert_eq!(played(jitter.pull()), Some(2)); - assert_eq!(played(jitter.pull()), Some(3)); + assert_eq!(jitter.pull(), Pull::Silence, "60 ms held, 80 wanted"); + jitter.push(1000 + 3 * FRAME, frame(4)); + for n in 1..=4 { + assert_eq!(played(jitter.pull()), Some(n)); + } // Dry: concealed a while, then silence. for _ in 0..DRY_CONCEALED { assert_eq!(jitter.pull(), Pull::Lost); @@ -354,17 +571,34 @@ mod tests { } #[test] - fn a_missing_frame_is_concealed_and_a_late_one_dropped() { + fn a_missing_frame_is_recovered_from_the_next_and_a_late_one_dropped() { let mut jitter = Jitter::new(20, 300); jitter.push(0, frame(1)); assert_eq!(played(jitter.pull()), Some(1)); jitter.push(2 * FRAME, frame(3)); - assert_eq!(jitter.pull(), Pull::Lost); + // Frame 3 carries a copy of 2, then plays itself. + assert_eq!(jitter.pull(), Pull::Recover(frame(3))); assert_eq!(played(jitter.pull()), Some(3)); // Frame 2 shows up after its turn. jitter.push(FRAME, frame(2)); assert_eq!(jitter.counts().late, 1); - assert_eq!(jitter.counts().concealed, 1); + assert_eq!(jitter.counts().recovered, 1); + assert_eq!(jitter.counts().concealed, 0); + } + + #[test] + fn of_two_missing_frames_the_first_is_concealed_the_second_recovered() { + let mut jitter = Jitter::new(20, 300); + jitter.push(0, frame(1)); + assert_eq!(played(jitter.pull()), Some(1)); + jitter.push(3 * FRAME, frame(4)); + assert_eq!(jitter.pull(), Pull::Lost, "frame 2: no copy of it here"); + assert_eq!(jitter.pull(), Pull::Recover(frame(4)), "frame 3"); + assert_eq!(played(jitter.pull()), Some(4)); + assert_eq!( + (jitter.counts().concealed, jitter.counts().recovered), + (1, 1) + ); } #[test] diff --git a/src/huddle_audio/media.rs b/src/huddle_audio/media.rs index 2fc24251..67826768 100644 --- a/src/huddle_audio/media.rs +++ b/src/huddle_audio/media.rs @@ -2753,12 +2753,12 @@ mod tests { .iter() .all(|(time, _, _)| time % 960 == heard[0].0 % 960) ); - let mut decoder = opus_decoder::OpusDecoder::new(48_000, 1).expect("a decoder"); + let mut decoder = opus_rs::OpusDecoder::new(48_000, 1).expect("a decoder"); let mut loud = 0; for (_, level, payload) in &heard { - let mut pcm = vec![0.0; decoder.max_frame_size_per_channel()]; + let mut pcm = vec![0.0; 5760]; let n = decoder - .decode_float(payload, &mut pcm, false) + .decode(payload, 5760, &mut pcm) .expect("our Opus decodes"); assert_eq!(n, 960); if super::super::uplink::level(&pcm[..n]) < 30 { diff --git a/src/huddle_audio/speaker.rs b/src/huddle_audio/speaker.rs index 3eae7086..1fb5a865 100644 --- a/src/huddle_audio/speaker.rs +++ b/src/huddle_audio/speaker.rs @@ -7,13 +7,12 @@ //! but a stream that never ends instead of a file. The level is left as //! decoded, so the system's volume is the one that counts. //! -//! Decoding is `opus-decoder`: pure Rust, no unsafe code, no C library to -//! find on Linux, macOS or Windows. It passes the RFC 8251 test vectors -//! by its own account; it is young, and libopus (the `opus` crate) is the -//! fallback if it ever decodes wrong. `opus-rs`, which encodes what we -//! send, decodes too, but not well enough to play with: it has no CELT -//! loss concealment, refuses a mono packet in a stereo decoder, has no -//! FEC, and reads the network's bytes with unchecked indexing. +//! Decoding is `opus-rs`, which also encodes what we send: libopus 1.6 +//! ported to Rust, no C library to find on Linux, macOS or Windows. A lost +//! frame plays from the copy the next one carries (in-band FEC) when that +//! one is here, and is concealed otherwise, both as libopus does (our fork +//! until a release has them; see Cargo.toml). libopus (the `opus` crate) +//! is the fallback if it ever decodes wrong. //! //! A panic in the decoder is caught ([`crate::audio::guard`]): that frame //! is lost and a fresh decoder takes the next. The session checks @@ -36,7 +35,7 @@ use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use super::jitter::{CLOCK, Counts, FRAME, Jitter, Pull, opus_samples}; +use super::jitter::{self, CLOCK, Counts, FRAME, Jitter, Pull, opus_samples}; use super::processing::RenderTap; use crate::audio::guard::{self, Health}; use crate::sync::lock_or_reset; @@ -45,7 +44,7 @@ use crate::sync::lock_or_reset; /// both. const CHANNELS: u16 = 2; /// Silence played while the buffer fills, per pull: 10 ms. -const SILENCE: usize = (CLOCK / 100) as usize * CHANNELS as usize; +const SILENCE: usize = jitter::SILENCE as usize * CHANNELS as usize; /// How long a device may go without asking for sound before it counts as /// stopped. It asks every few milliseconds while it plays. const STALL: Duration = Duration::from_secs(2); @@ -63,6 +62,9 @@ struct Shared { /// asked: its RMS, as `f32` bits (which order as the values do, for /// these never negative). loudest: AtomicU32, + /// Where what comes and what plays is captured, if asked for (see + /// [`super::capture`]). + capture: Option, } impl Shared { @@ -103,6 +105,9 @@ pub struct Feed { impl Feed { /// One Opus frame with its RTP timestamp. pub fn push(&self, timestamp: u32, payload: &[u8]) { + if let Some(capture) = &self.shared.capture { + capture.packet(timestamp, payload); + } self.shared.jitter().push(timestamp, payload.to_vec()); } @@ -122,8 +127,7 @@ impl Feed { } } -/// What decodes the huddle's frames: opus-decoder, or a stand-in in -/// tests. +/// What decodes the huddle's frames: opus-rs, or a stand-in in tests. trait Opus: Send { /// The most samples per channel one packet decodes to. fn room(&self) -> usize; @@ -131,15 +135,43 @@ trait Opus: Send { /// `out`; how many samples per channel came out, or `None` if it /// would not decode. fn decode(&mut self, packet: &[u8], out: &mut [f32]) -> Option; + /// Plays a lost frame from the copy `next`, the packet after it, + /// carries (in-band FEC), or conceals it where `next` has none. + fn recover(&mut self, next: &[u8], out: &mut [f32]) -> Option { + let _ = next; + self.decode(&[], out) + } +} + +/// The most one packet holds: 120 ms. +const ROOM: usize = CLOCK as usize / 1000 * 120; + +/// opus-rs's decoder, and how long the last packet was: a lost frame is +/// concealed for as long (opus-rs asks; libopus's API does too). +struct OpusRs { + inner: opus_rs::OpusDecoder, + last: usize, } -impl Opus for opus_decoder::OpusDecoder { +impl Opus for OpusRs { fn room(&self) -> usize { - self.max_frame_size_per_channel() + ROOM } fn decode(&mut self, packet: &[u8], out: &mut [f32]) -> Option { - self.decode_float(packet, out, false).ok() + let size = if packet.is_empty() { self.last } else { ROOM }; + let n = self.inner.decode(packet, size, out).ok()?; + if !packet.is_empty() { + self.last = n; + } + Some(n) + } + + fn recover(&mut self, next: &[u8], out: &mut [f32]) -> Option { + match self.inner.decode_fec(next, self.last, out) { + Ok(n) => Some(n), + Err(_) => self.decode(&[], out), + } } } @@ -162,9 +194,13 @@ fn rms(samples: &[f32]) -> f32 { type MakeOpus = fn() -> Result, String>; fn opus_decoder() -> Result, String> { - let decoder = opus_decoder::OpusDecoder::new(CLOCK, usize::from(CHANNELS)) + let rate = i32::try_from(CLOCK).map_err(|e| e.to_string())?; + let inner = opus_rs::OpusDecoder::new(rate, usize::from(CHANNELS)) .map_err(|e| format!("no Opus decoder: {e}"))?; - Ok(Box::new(decoder)) + Ok(Box::new(OpusRs { + inner, + last: FRAME as usize, + })) } /// Decodes and hands out samples as the device asks for them. @@ -210,6 +246,9 @@ impl Decoded { if let Some(tap) = &self.tap { tap.push(&self.samples, usize::from(CHANNELS)); } + if let Some(capture) = &self.shared.capture { + capture.played(&self.samples); + } } /// Decodes what comes next into `self.samples`. @@ -217,15 +256,19 @@ impl Decoded { let (pull, resumed) = { let mut jitter = self.shared.jitter(); let pull = jitter.pull(); + if let Some(capture) = &self.shared.capture { + capture.pull(&pull); + } let resumed = matches!(pull, Pull::Frame(_)) && jitter.take_resumed(); (pull, resumed) }; let room = self.decoder.room() * usize::from(CHANNELS); self.samples.resize(room, 0.0); self.at = 0; - let payload: &[u8] = match &pull { - Pull::Frame(payload) => payload, - Pull::Lost => &[], + let (payload, fec): (&[u8], bool) = match &pull { + Pull::Frame(payload) => (payload, false), + Pull::Lost => (&[], false), + Pull::Recover(next) => (next, true), Pull::Silence => { self.samples.clear(); self.samples.resize(SILENCE, 0.0); @@ -233,11 +276,13 @@ impl Decoded { } }; let (decoder, samples) = (&mut self.decoder, &mut self.samples); - let decoded = match self - .device - .health - .catch(|| decoder.decode(payload, samples)) - { + let decoded = match self.device.health.catch(|| { + if fec { + decoder.recover(payload, samples) + } else { + decoder.decode(payload, samples) + } + }) { Some(decoded) => decoded, None => { // Its state is suspect: a fresh one for the next frame. @@ -406,7 +451,10 @@ impl Speaker { tap: Option, choice: Option, ) -> Result<(Opened, Feed), String> { - let shared = Arc::new(Shared::default()); + let shared = Arc::new(Shared { + capture: super::capture::Capture::from_env(), + ..Shared::default() + }); let opened = Self::start(shared.clone(), tap, choice)?; Ok((opened, Feed { shared })) } @@ -504,9 +552,8 @@ mod tests { /// Opus's shortest silence: a 20 ms CELT frame with nothing in it. const SILENT_FRAME: [u8; 3] = [0xF8, 0xFF, 0xFE]; - /// A 20 ms CELT frame (found by feeding opus-decoder noise) whose - /// band has 16 short blocks: the one that overflowed opus-decoder's - /// u8 collapse mask on the device's thread in debug builds. + /// A 20 ms CELT frame (found by feeding a decoder noise) whose band has + /// 16 short blocks: one overflowed opus-decoder's collapse mask. const SIXTEEN_BLOCKS: [u8; 55] = [ 0xf8, 0x75, 0xd5, 0x48, 0x6a, 0x8c, 0xcf, 0xb8, 0x7d, 0xb1, 0xd2, 0x3b, 0x43, 0x7d, 0x6b, 0x6b, 0x17, 0xc1, 0x14, 0xfe, 0x7d, 0xa5, 0xae, 0x93, 0x56, 0x58, 0xc4, 0x69, 0xd1, 0x30, @@ -518,7 +565,7 @@ mod tests { const POISON: [u8; 2] = [0xF8, 0xDE]; /// Decodes every packet to 20 ms of a constant, panicking on - /// [`POISON`] as opus-decoder did on [`SIXTEEN_BLOCKS`]. + /// [`POISON`] as a decoder might on a malformed packet. struct Brittle; impl Opus for Brittle { @@ -547,12 +594,20 @@ mod tests { #[test] fn a_frame_with_sixteen_short_blocks_decodes() { - // Cargo.toml turns opus-decoder's overflow checks off for this; - // with them on, this panics in src/celt/vq.rs. - let mut decoder = opus_decoder::OpusDecoder::new(CLOCK, 2).expect("a decoder"); - let mut out = vec![0.0; decoder.max_frame_size_per_channel() * 2]; - let decoded = decoder.decode_float(&SIXTEEN_BLOCKS, &mut out, false); - assert_eq!(decoded.ok(), Some(960)); + let mut decoder = opus_decoder().expect("a decoder"); + let mut out = vec![0.0; decoder.room() * usize::from(CHANNELS)]; + assert_eq!(decoder.decode(&SIXTEEN_BLOCKS, &mut out), Some(960)); + } + + #[test] + fn a_mono_packet_plays_on_both_channels() { + // Teams sends mono SILK; the decoder is stereo. + const QUIET: [u8; 10] = [0x48, 0x09, 0x9e, 0xee, 0x99, 0xb6, 0xe5, 0x73, 0xb3, 0xad]; + let mut decoder = opus_decoder().expect("a decoder"); + let mut out = vec![0.0; decoder.room() * usize::from(CHANNELS)]; + assert_eq!(decoder.decode(&QUIET, &mut out), Some(960)); + // A loss conceals as long as the last packet. + assert_eq!(decoder.decode(&[], &mut out), Some(960)); } #[test] @@ -630,11 +685,60 @@ mod tests { assert!(!device.health.thread_gone()); } + #[test] + fn concealing_after_silence_stays_silent() { + // A SILK wideband packet of random bytes that decodes to near + // silence, as Teams' talk spurts end before it sends nothing: + // opus-decoder's concealment once played white noise after it. + const QUIET: [u8; 10] = [0x48, 0x09, 0x9e, 0xee, 0x99, 0xb6, 0xe5, 0x73, 0xb3, 0xad]; + let mut decoder = opus_decoder().expect("a decoder"); + let mut out = vec![0.0; decoder.room() * usize::from(CHANNELS)]; + let mut loudest = 0.0f32; + for _ in 0..3 { + let n = decoder.decode(&QUIET, &mut out).expect("decodes"); + loudest = loudest.max(rms(&out[..n * usize::from(CHANNELS)])); + } + assert!(loudest < 0.01, "near silence: {loudest}"); + for lost in 0..3 { + let n = decoder.decode(&[], &mut out).expect("conceals"); + let concealed = rms(&out[..n * usize::from(CHANNELS)]); + assert!( + concealed <= loudest + 0.001, + "lost frame {lost}: {concealed}" + ); + } + } + + #[test] + fn a_lost_frame_plays_from_the_copy_in_the_next() { + use super::super::encoder::{Encoder, OpusRs}; + use super::super::uplink::{TWENTY_MS, Tone}; + let mut encoder = OpusRs::speech().expect("an encoder"); + let mut tone = Tone::new(440.0, 0.3); + let (feed, _, mut decoded) = decoded(None, opus_decoder); + for n in 0..20u32 { + let mut frame = vec![0.0; TWENTY_MS]; + tone.fill(&mut frame); + let packet = encoder.encode(&frame).expect("encoded"); + // Frame 12 is lost on the way. + if n != 12 { + feed.push(n * FRAME, &packet); + } + } + let samples: Vec = decoded.by_ref().take(20 * 960 * 2).collect(); + let played = feed.played(); + assert_eq!((played.jitter.recovered, played.jitter.concealed), (1, 0)); + // Its 20 ms are sound, no louder than the tone (0.3 peak: 0.21 + // RMS): a low-bitrate copy, 0.128 RMS here, as libopus decodes it. + let at = 12 * 960 * 2; + let level = rms(&samples[at..at + 960 * 2]); + assert!((0.08..0.22).contains(&level), "{level}"); + } + #[test] fn a_hybrid_frame_claiming_too_much_redundancy_plays() { // Two 76-byte hybrid frames claiming more redundancy than they - // hold: opus-decoder 0.1.1 sliced out of range on it; the patched - // copy (vendor/opus-decoder) drops the redundancy as libopus does. + // hold: opus-decoder 0.1.1 sliced out of range on it. const BAD_HYBRID: [u8; 153] = [ 0x69, 0xaf, 0x0e, 0xe6, 0x85, 0x96, 0x57, 0x1b, 0xb5, 0x8c, 0xc1, 0xa2, 0x21, 0x35, 0xa5, 0x94, 0x3d, 0x0d, 0x19, 0xd1, 0xc8, 0xe7, 0x7c, 0xd0, 0x63, 0x03, 0xbd, 0xc4, diff --git a/src/huddles/listen.rs b/src/huddles/listen.rs index e0fa25c1..3326afef 100644 --- a/src/huddles/listen.rs +++ b/src/huddles/listen.rs @@ -211,10 +211,10 @@ impl Listening { } } - /// Joining a Teams meeting in `team`: unmuted, as a call is. + /// Joining a Teams meeting in `team`: muted, as a huddle is (the + /// worker starts it so too). pub fn meeting(team: &str) -> Self { Self { - mic: crate::huddle_mic::Mic::Opening, meeting: true, ..Self::new(team, crate::meetings::MEETING_CHANNEL) } diff --git a/src/teams/calling/call.rs b/src/teams/calling/call.rs index 2c45773f..30f0f871 100644 --- a/src/teams/calling/call.rs +++ b/src/teams/calling/call.rs @@ -121,10 +121,10 @@ pub enum CallEvent { /// Whose camera the meeting is asked to send us on each camera line /// (by MRI), or no one's. Watching(Vec>), - /// Who in the meeting spoke last (by MRI), as its data channel says: - /// the meeting mixes everyone's sound, so this is who the bar marks - /// as speaking while it is loud. - Speaker(String), + /// Who in the meeting spoke last (by MRI), as its data channel says, + /// or no one we know: the meeting mixes everyone's sound, so this is + /// who the bar marks as speaking while it is loud. + Speaker(Option), /// An incoming call stopped ringing because another device of yours /// (or another delivery of the same call here) picked it up: not a /// missed call. @@ -565,8 +565,12 @@ struct InMeeting { /// Whose camera was last asked for on each camera line, by MRI and /// source id. watching: Vec>, - /// Who spoke last, by MRI, as the meeting says. + /// Who spoke last, by MRI, as the meeting says: whose camera is + /// kept in view. speaker: Option, + /// Who the bar was last told speaks: the newest speaker, or no one + /// when the meeting names a source not in the roster. + marked: Option, /// Whose screen share was last asked for, by MRI and source id; the /// outer `None` while none was ever asked for. watching_share: Option>, @@ -1598,8 +1602,11 @@ impl Call { } /// Who spoke, as a speaker history (`dsh`) from `way` says: the - /// newest last (seen: the first marked whoever spoke long ago); - /// ourselves (not in the list) aside. + /// newest first (recorded: each new speaker moves to the front, and + /// the meeting sends it again whenever one does). Only the newest + /// counts: one not in the roster (a phone, say) is marked as no one, + /// not as whoever spoke before, who may have muted since. The video + /// asked for follows the last speaker known. fn spoke( &mut self, history: &[i64], @@ -1610,23 +1617,25 @@ impl Call { let Some(meeting) = &mut self.meeting else { return; }; - let speaker = history.iter().rev().find_map(|&source| { - meeting - .people - .list() - .into_iter() + let people = meeting.people.list(); + let speaker = history.first().and_then(|&source| { + people + .iter() .find(|a| a.audio == Some(source)) - .map(|a| a.mri) + .map(|a| a.mri.clone()) }); log::debug!( - "Teams meeting: spoke, by audio source ({way}): {history:?}; marking {}", - speaker.as_deref().unwrap_or("no one") + "Teams meeting: spoke, by audio source ({way}): {history:?}; marking {} ({} of {} in the roster with an audio source)", + speaker.as_deref().unwrap_or("no one"), + people.iter().filter(|a| a.audio.is_some()).count(), + people.len() ); + if speaker != meeting.marked { + meeting.marked.clone_from(&speaker); + tell(CallEvent::Speaker(speaker.clone())); + } if speaker.is_some() && speaker != meeting.speaker { - meeting.speaker = speaker.clone(); - if let Some(mri) = speaker { - tell(CallEvent::Speaker(mri)); - } + meeting.speaker = speaker; self.ask_for_video(session, tell); } } diff --git a/src/teams/calling/channel.rs b/src/teams/calling/channel.rs index 6ace61f8..67045f9d 100644 --- a/src/teams/calling/channel.rs +++ b/src/teams/calling/channel.rs @@ -23,9 +23,9 @@ /// The channel's label. pub const LABEL: &str = "main-channel"; -/// The header's first bytes. +/// The header's first bytes, with one recipient (see [`decode`]). const MAGIC: [u8; 3] = [0x10, 0x0f, 0x92]; -/// The header's length. +/// The header's length, with one recipient. const HEADER: usize = 16; /// The meeting's media server's id. pub const SERVER: i32 = -4; @@ -116,9 +116,18 @@ pub fn encode(seq: u16, from: i32, to: i32, messages: &[serde_json::Value]) -> V out } -/// Reads one message; `None` for one not in this shape. +/// Reads one message; `None` for one not in this shape. The header's +/// second byte is its length less one, and the byte after the sender +/// counts the recipients, four bytes each: the server sends some news +/// (the speaker history, recorded) to several at once, in a longer +/// header. `to` is the first. pub fn decode(bytes: &[u8]) -> Option { - if bytes.len() < HEADER || bytes[..3] != MAGIC { + let header = usize::from(*bytes.get(1)?) + 1; + let recipients = usize::from(*bytes.get(10)?); + if bytes[0] != MAGIC[0] || bytes[2] != MAGIC[2] || recipients == 0 { + return None; + } + if header != 11 + 4 * recipients + 1 || bytes.len() < header { return None; } let id = |at: usize| -> Option { @@ -126,7 +135,7 @@ pub fn decode(bytes: &[u8]) -> Option { }; let seq = u16::from_be_bytes([bytes[3], bytes[4]]); let (from, to) = (id(6)?, id(11)?); - let messages = match serde_json::from_slice(&bytes[HEADER..]).ok()? { + let messages = match serde_json::from_slice(&bytes[header..]).ok()? { serde_json::Value::Array(messages) => messages, one => vec![one], }; @@ -145,7 +154,8 @@ pub enum Heard { Ready, /// A source request of ours was answered. SourceAnswered { sequence: u64, ok: bool }, - /// Who spoke, by their audio's source id: a history, the newest last. + /// Who spoke, by their audio's source id: a history, the newest first + /// (recorded: whoever starts talking moves to the front). Speakers(Vec), } @@ -179,7 +189,14 @@ impl Channel { /// Takes in what the server sent. pub fn heard(&mut self, bytes: &[u8]) -> Vec { let Some(frame) = decode(bytes) else { - log::debug!("meeting channel: a message in an unknown shape"); + if log::log_enabled!(log::Level::Debug) { + let start: String = bytes.iter().take(48).map(|b| format!("{b:02x}")).collect(); + log::debug!( + "meeting channel: a message in an unknown shape, {} bytes: {start}…; as text: {}", + bytes.len(), + String::from_utf8_lossy(&bytes[..bytes.len().min(300)]) + ); + } return Vec::new(); }; let mut heard = Vec::new(); @@ -348,6 +365,30 @@ mod tests { assert!(channel.heard(b"not a frame").is_empty()); } + #[test] + fn news_for_several_recipients_reads() { + // The server's speaker history to three of us at once: a 24-byte + // header naming three recipients (recorded shape). + let mut bytes = vec![ + 0x10, 0x17, 0x92, 0x2c, 0x1b, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x03, + ]; + bytes.extend_from_slice(&[ + 0x00, 0x98, 0x9b, 0x8e, 0x00, 0x98, 0x9e, 0xda, 0x00, 0x98, 0x9f, 0xab, 0x01, + ]); + bytes.extend_from_slice(br#"[{"history":[4035,10001704,3183],"type":"dsh"}]"#); + let frame = decode(&bytes).expect("reads"); + assert_eq!(frame.from, SERVER); + assert_eq!(frame.to, 0x0098_9b8e); + let mut channel = Channel::default(); + assert_eq!( + channel.heard(&bytes), + [Heard::Speakers(vec![4035, 10_001_704, 3183])] + ); + // A length that does not match the recipients is not read. + bytes[1] = 0x0f; + assert!(decode(&bytes).is_none()); + } + #[test] fn the_servers_sequence_numbers_past_255_read() { // As recorded on a server whose count had run on: 0x086e. diff --git a/src/teams/calling/media.rs b/src/teams/calling/media.rs index 972a1a6a..1bbe41c7 100644 --- a/src/teams/calling/media.rs +++ b/src/teams/calling/media.rs @@ -47,6 +47,7 @@ use str0m::crypto::Fingerprint; use str0m::format::{Codec, FormatParams}; use str0m::media::{Frequency, MediaKind, Mid, Pt}; use str0m::net::Protocol; +use str0m::rtp::Extension; use str0m::{ Candidate as IceCandidate, CandidateKind as IceKind, Event as RtcEvent, IceConnectionState, IceCreds, Rtc, RtcConfig, @@ -951,6 +952,11 @@ fn new_rtc(opus_pt: u8, video: &[(u8, Option)], controlling: bool, now: Inst if !video.is_empty() { config = config.enable_bwe(Some(str0m::bwe::Bitrate::kbps(BWE_START_KBPS))); } + // A meeting's media server, offering after the lobby, numbers + // transport-cc 5 where we number it 3 (recorded); str0m's map is set + // here, before its SDP comes, so the packets it sends us are read at + // either. Ours still go out at 3, the first. + config = config.set_extension(5, Extension::TransportSequenceNumber); config.codec_config().add_config( Pt::from(opus_pt), None, @@ -989,6 +995,11 @@ fn new_rtc(opus_pt: u8, video: &[(u8, Option)], controlling: bool, now: Inst } let mut rtc = config.build(now); rtc.direct_api().set_ice_controlling(controlling); + // What arrives is reported back (transport-cc feedback), as the + // offer's `rtcp-fb:* transport-cc` promises: without it the media + // server cannot tell what our link takes and sends us video slowly. + // str0m's direct API leaves it off. + rtc.direct_api().enable_twcc_feedback(); rtc } @@ -2301,6 +2312,7 @@ mod tests { }, ], video_streams: Vec::new(), + transport_cc: None, } } @@ -2590,6 +2602,7 @@ mod tests { share_video: None, lines: vec![audio_line(Direction::SendRecv)], video_streams: Vec::new(), + transport_cc: None, } } diff --git a/src/teams/calling/mod.rs b/src/teams/calling/mod.rs index 792f995c..770806ec 100644 --- a/src/teams/calling/mod.rs +++ b/src/teams/calling/mod.rs @@ -147,6 +147,9 @@ pub struct RemoteMedia { /// mid (`a=x-source-streamid`): where a source request asks for /// someone's video to come. pub video_streams: Vec<(String, u32)>, + /// The id the far end gives transport-cc (`a=extmap`), if it names + /// it: an answer to its offer must use its number. + pub transport_cc: Option, } impl RemoteMedia { diff --git a/src/teams/calling/sdp.rs b/src/teams/calling/sdp.rs index 65a248d0..3b49a4af 100644 --- a/src/teams/calling/sdp.rs +++ b/src/teams/calling/sdp.rs @@ -42,6 +42,11 @@ pub enum SdpError { const ABS_SEND_TIME: &str = r"http:\\www.webrtc.org\experiments\rtp-hdrext\abs-send-time"; const TRANSPORT_CC: &str = r"http:\\www.ietf.org\id\draft-holmer-rmcat-transport-wide-cc-extensions-01"; +/// The header extensions our offer gives a video line, at str0m's own +/// numbers, as the web client gives every video line: transport-cc is +/// what the media server stamps the video it sends us with, and our +/// feedback on it is how it learns what our link can take. +const VIDEO_EXTENSIONS: [(u8, &str); 2] = [(2, ABS_SEND_TIME), (3, TRANSPORT_CC)]; /// One m-line as written, before it means anything. struct RawLine<'a> { @@ -193,6 +198,11 @@ pub fn read(sdp: &str) -> Result { .iter() .find(|l| l.media == "video" && l.port != 0 && l.attr("label") == Some(SHARE_LABEL)) .and_then(h264_of); + let transport_cc = raw + .iter() + .flat_map(|l| &l.attrs) + .filter(|(n, _)| *n == "extmap") + .find_map(|(_, v)| transport_cc_of(v)); let session_direction = direction_in(&session); let lines = raw @@ -230,9 +240,21 @@ pub fn read(sdp: &str) -> Result { share_video, lines, video_streams, + transport_cc, }) } +/// The id of an `a=extmap` value (`5 http:\\…` or `5/sendrecv …`) if it +/// names transport-cc, in Microsoft's backslashes or the usual slashes. +fn transport_cc_of(value: &str) -> Option { + let (id, uri) = value.split_once(char::is_whitespace)?; + let slashed = |uri: &str| uri.trim().replace('\\', "/"); + if slashed(uri) != slashed(TRANSPORT_CC) { + return None; + } + id.split('/').next()?.parse().ok() +} + /// The first H.264 at packetization mode 1 a video line lists, and its /// retransmission's payload type. fn h264_of(line: &RawLine<'_>) -> Option { @@ -505,7 +527,14 @@ pub fn offer(local: &LocalMedia) -> String { direction: local.video_direction, share: false, }; - video_line(&mut out, local, &line, setup_text(local.setup), false); + video_line( + &mut out, + local, + &line, + &VIDEO_EXTENSIONS, + setup_text(local.setup), + false, + ); continue; } // The share line carries our screen while we share; a meeting's @@ -529,7 +558,14 @@ pub fn offer(local: &LocalMedia) -> String { }, share: true, }; - video_line(&mut out, local, &line, setup_text(local.setup), false); + video_line( + &mut out, + local, + &line, + &VIDEO_EXTENSIONS, + setup_text(local.setup), + false, + ); continue; } push(&mut out, &format!("m=video {port} RTP/SAVP {h264} {rtx}")); @@ -567,7 +603,14 @@ pub fn offer(local: &LocalMedia) -> String { rtx: local.video_rtx, }; let line = more_camera(camera, codec); - video_line(&mut out, local, &line, setup_text(local.setup), false); + video_line( + &mut out, + local, + &line, + &VIDEO_EXTENSIONS, + setup_text(local.setup), + false, + ); } if let Some(ssrc) = local.data_ssrc { data_line(&mut out, local, "3", ssrc, setup_text(local.setup), false); @@ -601,9 +644,9 @@ fn more_camera(camera: &MoreCamera, codec: VideoCodec) -> VideoLine<'_> { /// while `local.data_ssrc` is set, which is kept as the web client keeps /// it. The bundle groups the lines we keep. /// -/// No header extensions are answered: an answer must use the offerer's -/// extension ids, which [`RemoteMedia`] does not carry, and audio works -/// without them. +/// Of the header extensions only transport-cc is answered, on the video +/// lines, at the offer's number: the media server's estimate of what +/// our link can take rests on our feedback on it. Audio works without. pub fn answer(local: &LocalMedia, remote: &RemoteMedia) -> String { lines_for(local, remote, Role::Answer) } @@ -652,6 +695,11 @@ fn lines_for(local: &LocalMedia, remote: &RemoteMedia, role: Role) -> String { // The share line's H.264: its own, or, re-enabling a line that was // dropped, the camera's (one codec, one number in a bundle). let share_codec = remote.share_video.or(remote.video); + let extensions: Vec<(u8, &str)> = remote + .transport_cc + .map(|id| (id, TRANSPORT_CC)) + .into_iter() + .collect(); let sends = |d: Direction| matches!(d, Direction::SendRecv | Direction::SendOnly); // What we want of the share line, and what it comes to. let share_direction = |theirs: Direction| match role { @@ -759,7 +807,7 @@ fn lines_for(local: &LocalMedia, remote: &RemoteMedia, role: Role) -> String { direction: share_direction(line.direction), share: true, }; - video_line(&mut out, local, &line_spec, setup, first); + video_line(&mut out, local, &line_spec, &extensions, setup, first); } } (LineKind::Video, _, _) if local.receive_cameras.iter().any(|c| c.mid == line.mid) => { @@ -771,7 +819,7 @@ fn lines_for(local: &LocalMedia, remote: &RemoteMedia, role: Role) -> String { }; let mut line_spec = more_camera(camera, codec); line_spec.direction = direction(Direction::SendRecv); - video_line(&mut out, local, &line_spec, setup, first); + video_line(&mut out, local, &line_spec, &extensions, setup, first); } } (LineKind::Video, _, _) => { @@ -789,7 +837,7 @@ fn lines_for(local: &LocalMedia, remote: &RemoteMedia, role: Role) -> String { direction: direction(local.video_direction), share: false, }; - video_line(&mut out, local, &line_spec, setup, first); + video_line(&mut out, local, &line_spec, &extensions, setup, first); } } // `accepted` keeps nothing else. @@ -893,6 +941,7 @@ fn video_line( out: &mut String, local: &LocalMedia, line: &VideoLine<'_>, + extensions: &[(u8, &str)], setup: &str, candidates: bool, ) { @@ -935,6 +984,9 @@ fn video_line( push(out, "a=rtcp-fb:* nack"); } push(out, "a=rtcp-fb:* nack pli"); + for (id, uri) in extensions { + push(out, &format!("a=extmap:{id} {uri}")); + } // Our resends' stream, paired with the line's, as the web client // writes it. if let (true, Some(_), Some(rtx_ssrc)) = (sending, rtx, rtx_ssrc) { @@ -1894,6 +1946,46 @@ mod tests { ); } + #[test] + fn video_lines_carry_transport_cc_at_the_number_agreed() { + // Our offer: at str0m's 3 on every video line, as the web client. + let local = LocalMedia { + video_ssrc: Some(77), + receive_cameras: vec![more("4", 401)], + ..local() + }; + let sdp = offer(&local); + let stamped = sdp + .split("m=") + .filter(|l| l.starts_with("video") && l.contains("a=sendrecv")) + .filter(|l| l.contains(&format!("a=extmap:3 {TRANSPORT_CC}\r\n"))) + .count(); + assert_eq!(stamped, 2, "{sdp}"); + assert_eq!(read(&sdp).expect("reads").transport_cc, Some(3)); + assert_eq!(read(MEETING_LOBBY).expect("reads").transport_cc, Some(3)); + // The media server's own offer numbers it 5: the answer keeps 5. + let remote = read(MEETING_RETARGET).expect("reads"); + assert_eq!(remote.transport_cc, Some(5)); + let ours = LocalMedia { + opus_pt: 102, + receive_cameras: vec![more("5", 501)], + ..local + }; + let answered = answer(&ours, &remote); + assert!( + answered.contains(&format!("a=extmap:5 {TRANSPORT_CC}\r\n")), + "{answered}" + ); + assert!(!answered.contains("a=extmap:3"), "{answered}"); + assert_eq!( + transport_cc_of( + "7/sendrecv http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01" + ), + Some(7) + ); + assert_eq!(transport_cc_of(&format!("2 {ABS_SEND_TIME}")), None); + } + #[test] fn a_meetings_share_line_is_open_both_ways_from_the_start() { let local = LocalMedia { diff --git a/src/teams/calling/video.rs b/src/teams/calling/video.rs index e08b0b42..7c47cb55 100644 --- a/src/teams/calling/video.rs +++ b/src/teams/calling/video.rs @@ -610,9 +610,14 @@ impl CallVideo { self.pictures_in += 1; if self.pictures_in == 1 { log::info!( - "video: {}: the far end's first picture, {} bytes", + "video: {}: the far end's first picture, {} bytes; transport-cc {}", self.name(), - data.data.len() + data.data.len(), + if data.ext_vals.transport_cc.is_some() { + "stamped, so fed back" + } else { + "not stamped: the server learns nothing of our link" + } ); } self.last_picture = Some(now); diff --git a/vendor/opus-decoder/Cargo.toml b/vendor/opus-decoder/Cargo.toml deleted file mode 100644 index 1d21b2e2..00000000 --- a/vendor/opus-decoder/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "opus-decoder" -version = "0.1.1" -edition = "2024" -rust-version = "1.85" -description = "Pure-Rust Opus decoder — RFC 8251 conformant, no unsafe, no FFI" -license = "MIT OR Apache-2.0" -repository = "https://github.com/TadeuszWolfGang/Rusopus" -publish = false - -[dependencies] -thiserror = "2.0.18" diff --git a/vendor/opus-decoder/README.md b/vendor/opus-decoder/README.md deleted file mode 100644 index 70aef546..00000000 --- a/vendor/opus-decoder/README.md +++ /dev/null @@ -1,229 +0,0 @@ -# opus-decoder - -[![Crates.io](https://img.shields.io/crates/v/opus-decoder.svg)](https://crates.io/crates/opus-decoder) -[![Docs.rs](https://docs.rs/opus-decoder/badge.svg)](https://docs.rs/opus-decoder) -[![CI](https://github.com/tadeuszwojcik/rasopus/actions/workflows/ci.yml/badge.svg)](https://github.com/tadeuszwojcik/rasopus/actions) -[![RFC 8251](https://img.shields.io/badge/RFC%208251-12%2F12%20conformant-brightgreen)](https://www.rfc-editor.org/rfc/rfc8251) -[![License](https://img.shields.io/crates/l/opus-decoder.svg)](LICENSE-APACHE) - -A pure-Rust [Opus](https://opus-codec.org/) decoder with no unsafe code and no -FFI. Passes all 12 RFC 8251 conformance test vectors. - -## Features - -- **RFC 8251 conformant** — all 12 official test vectors pass `opus_compare` -- **All Opus modes** — CELT, SILK, and Hybrid (SWB/FB) -- **All output sample rates** — 8, 12, 16, 24, 48 kHz -- **Mono and stereo** — including up/downmix -- **Packet Loss Concealment (PLC)** — CELT fade-out, SILK pitch extrapolation -- **Multistream** — `OpusMultistreamDecoder` for surround (5.1, 7.1) and call recording -- **`#![forbid(unsafe_code)]`** — throughout the correctness implementation -- **No FFI, no C dependencies** — pure Rust - -## Quick Start - -Add to `Cargo.toml`: - -```toml -[dependencies] -opus-decoder = "0.1" -``` - -### Decode a single packet - -```rust -use opus_decoder::{OpusDecoder, OpusError}; - -fn main() -> Result<(), OpusError> { - let mut decoder = OpusDecoder::new(48_000, 2)?; // 48 kHz, stereo - - let packet: &[u8] = /* your Opus packet bytes */; - let mut pcm = vec![0i16; 960 * 2]; // 20 ms at 48 kHz, stereo interleaved - - let samples = decoder.decode(packet, &mut pcm, false)?; - println!("Decoded {} samples per channel", samples); - Ok(()) -} -``` - -### Packet Loss Concealment - -```rust -// Pass an empty slice to trigger PLC -let mut pcm = vec![0i16; 960 * 2]; -decoder.decode(&[], &mut pcm, false)?; // generates concealment frame -``` - -### Float output - -```rust -let mut pcm_f32 = vec![0f32; 960 * 2]; -decoder.decode_float(packet, &mut pcm_f32, false)?; -``` - -### Multistream — call recording (2 speakers → stereo) - -```rust -use opus_decoder::OpusMultistreamDecoder; - -// Two independent mono streams → stereo output -// Left channel: caller (Stream A), Right channel: callee (Stream B) -let mut decoder = OpusMultistreamDecoder::new( - 48_000, // sample rate - 2, // output channels - 2, // nb_streams - 0, // nb_coupled_streams (both mono) - &[0, 1], // mapping: stream 0 → L, stream 1 → R -)?; - -let mut pcm = vec![0i16; 960 * 2]; -decoder.decode(&multistream_packet, &mut pcm, false)?; -``` - -### Multistream — 5.1 surround - -```rust -let mut decoder = OpusMultistreamDecoder::new( - 48_000, - 6, // 5.1 output channels - 4, // 4 streams total - 2, // 2 coupled (stereo) streams - &[0, 1, 2, 3, 4, 255], // mapping (255 = silence) -)?; -``` - -## API Reference - -### `OpusDecoder` - -```rust -impl OpusDecoder { - pub fn new(sample_rate: u32, channels: usize) -> Result; - pub fn decode(&mut self, packet: &[u8], pcm: &mut [i16], fec: bool) -> Result; - pub fn decode_float(&mut self, packet: &[u8], pcm: &mut [f32], fec: bool) -> Result; - pub fn reset(&mut self); -} -``` - -`sample_rate` must be one of: `8000`, `12000`, `16000`, `24000`, `48000`. -`channels` must be `1` or `2`. -`decode` returns the number of samples decoded **per channel**. -An empty `packet` or `fec: true` triggers Packet Loss Concealment (PLC). - -### `OpusMultistreamDecoder` - -```rust -impl OpusMultistreamDecoder { - pub fn new(sample_rate: u32, nb_channels: usize, nb_streams: usize, - nb_coupled_streams: usize, mapping: &[u8]) -> Result; - pub fn decode(&mut self, packet: &[u8], pcm: &mut [i16], fec: bool) -> Result; - pub fn decode_float(&mut self, packet: &[u8], pcm: &mut [f32], fec: bool) -> Result; - pub fn reset(&mut self); -} -``` - -`mapping[i] = 255` silences output channel `i`. -The first `nb_coupled_streams` streams are stereo-coupled; the rest are mono. - -### `OpusError` - -```rust -pub enum OpusError { - InvalidPacket, - InternalError, - BufferTooSmall, - InvalidArgument(&'static str), -} -``` - -## Conformance - -All 12 RFC 8251 test vectors pass the `opus_compare` quality metric: - -| Vector | Description | Result | -|--------|-------------|--------| -| tv01 | CELT stereo FB | ✅ PASS | -| tv02 | SILK NB 8 kHz mono | ✅ PASS | -| tv03 | SILK MB 12 kHz mono | ✅ PASS | -| tv04 | SILK WB 16 kHz mono | ✅ PASS | -| tv05 | Hybrid SWB transitions | ✅ PASS | -| tv06 | Hybrid FB transitions | ✅ PASS | -| tv07 | CELT mono FB | ✅ PASS | -| tv08 | SILK stereo + CELT NB stereo | ✅ PASS | -| tv09 | SILK→CELT transitions | ✅ PASS | -| tv10 | CeltOnly + Hybrid mix | ✅ PASS | -| tv11 | CELT stereo FB | ✅ PASS | -| tv12 | SILK bandwidth transitions | ✅ PASS | - -Run conformance tests locally: - -```bash -# Download test vectors -cargo xtask fetch-vectors - -# Run all 12 vectors -for v in 01 02 03 04 05 06 07 08 09 10 11 12; do - OPUS_TESTVECTORS_DIR="testdata/opus_testvectors" \ - OPUS_VECTOR=testvector${v} \ - cargo test --release -p opus-decoder rfc_conformance -- --ignored --nocapture \ - 2>&1 | grep -E "PASS|FAIL" -done -``` - -## Minimum Supported Rust Version (MSRV) - -**Rust 1.85** — enforced in CI. - -Uses: `std`, `thiserror`. No nightly features. - -## Out of Scope - -The following are intentionally **not** implemented in this crate: - -- **Encoder** — decoder only -- **Container parsing** — Ogg/WebM demux belongs in a separate crate -- **SIMD optimizations** — correctness first; performance is a future phase - -## Implementation Notes - -This crate is a pure Rust port of libopus based on RFC 6716 and RFC 8251, -implemented function-by-function and verified against the reference libopus implementation. -The floating-point decode path is used throughout (matching libopus float semantics). - -Internal architecture: - -``` -lib.rs — public API, mode routing, mode transitions -├── celt/ — CELT decoder (MDCT, PVQ, band decode, anti-collapse, PLC) -├── silk/ — SILK decoder (NLSF→LPC, excitation, IIR/FIR resampler, PLC) -├── multistream.rs — OpusMultistreamDecoder -├── entropy.rs — Range coder (EcDec) -└── compare.rs — opus_compare quality metric port -``` - -## Known Limitations - -- Hybrid mode PLC uses SILK-only concealment (no CELT highband PLC) -- No fuzz harness yet (planned) - -## License - -Licensed under either of: - -- [Apache License, Version 2.0](LICENSE-APACHE) -- [MIT License](LICENSE-MIT) - -at your option. - -This crate ports algorithms from [libopus](https://gitlab.xiph.org/xiph/opus), -which is licensed under the [BSD 3-Clause License](https://gitlab.xiph.org/xiph/opus/-/blob/master/COPYING). - -## Contributing - -Conformance is the top priority. Any change must pass all 12 RFC 8251 test vectors -before merging. Run: - -```bash -cargo test --release -p opus-decoder -cargo clippy -p opus-decoder --all-targets -- -D warnings -``` diff --git a/vendor/opus-decoder/VENDORED.md b/vendor/opus-decoder/VENDORED.md deleted file mode 100644 index d8c7e1bb..00000000 --- a/vendor/opus-decoder/VENDORED.md +++ /dev/null @@ -1,22 +0,0 @@ -# opus-decoder 0.1.1, patched - -A copy of [`opus-decoder`](https://crates.io/crates/opus-decoder) 0.1.1 -(, MIT OR Apache-2.0, as its -manifest says), used through `[patch.crates-io]` in NoSlacking's -`Cargo.toml` for huddle audio. Only `src/`, the README and a standalone -manifest are copied; the upstream tests and benches are left out. - -Two changes, each marked `NoSlacking:` in the code: - -1. `src/lib.rs`, hybrid mode: the redundancy length read from the packet - is checked against the frame as libopus does (`opus_decoder.c`, - `if (len*8 < ec_tell(&dec))`). Without it a malformed packet sliced - out of range and panicked, which in a release build (`panic = "abort"`) - closes the app; one participant could close everyone's NoSlacking. -2. `src/celt/vq.rs`, `extract_collapse_mask`: a transient frame can have - 16 short blocks, so `1 << i` on a `u8` overflowed (a panic in debug - builds). `wrapping_shl` keeps what release builds always computed, - which the fold that follows turns into libopus's mask. - -Both are reported upstream (see TODO.md); drop this copy once a release -has them. diff --git a/vendor/opus-decoder/src/celt/bands.rs b/vendor/opus-decoder/src/celt/bands.rs deleted file mode 100644 index 0d41f10d..00000000 --- a/vendor/opus-decoder/src/celt/bands.rs +++ /dev/null @@ -1,2214 +0,0 @@ -//! CELT band decode (`bands.c`) for mono decode-only vertical slice. -#![allow( - unused_variables, - unused_assignments, - dead_code, - clippy::too_many_arguments, - clippy::needless_option_as_deref, - clippy::needless_range_loop, - clippy::get_first -)] - -use crate::celt::modes::CeltMode; -use crate::celt::quant_bands; -use crate::celt::rate; -use crate::celt::vq; -use crate::entropy::EcDec; - -const BITRES: i32 = 3; -const QTHETA_OFFSET: i32 = 4; -const QTHETA_OFFSET_TWOPHASE: i32 = 16; -const SPREAD_AGGRESSIVE: i32 = 3; -const BIT_INTERLEAVE_TABLE: [u32; 16] = [0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3]; -const BIT_DEINTERLEAVE_TABLE: [u8; 16] = [ - 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F, 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF, -]; -const EXP2_TABLE8: [i32; 8] = [16384, 17866, 19483, 21247, 23170, 25267, 27554, 30048]; -const ORDERY_TABLE: [usize; 30] = [ - 1, 0, 3, 0, 2, 1, 7, 0, 4, 3, 6, 1, 5, 2, 15, 0, 8, 7, 12, 3, 11, 4, 14, 1, 9, 6, 13, 2, 10, 5, -]; -/// CELT linear congruential generator used for folded/noise fill. -/// -/// Params: current seed. -/// Returns: next pseudo-random seed. -pub(crate) fn celt_lcg_rand(seed: u32) -> u32 { - seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223) -} - -/// Append one NDJSON debug entry for anti-collapse probes. -/// -/// Params: run metadata plus `data_json` object payload. -/// Returns: nothing. -fn append_anti_collapse_debug_log( - run_id: &str, - hypothesis_id: &str, - location: &str, - message: &str, - data_json: &str, -) { - let _ = (run_id, hypothesis_id, location, message, data_json); -} - -/// Prevent transient energy collapse by injecting low-level noise. -/// -/// Params: mode, normalized spectra (`x`/optional `y`), per-band collapse masks, -/// band/frame context (`lm`, `coded_channels`, `start`, `end`), energy history -/// (`log_e`, `prev1_log_e`, `prev2_log_e`), pulse allocation and RNG seed. -/// Returns: nothing; vectors are modified in-place. -#[allow(clippy::too_many_arguments)] -pub(crate) fn anti_collapse( - mode: &CeltMode, - x: &mut [f32], - mut y: Option<&mut [f32]>, - collapse_masks: &[u8], - lm: usize, - coded_channels: usize, - start: usize, - end: usize, - log_e: &[f32], - prev1_log_e: &[f32], - prev2_log_e: &[f32], - pulses: &[i32], - seed: u32, - trace_this_packet: bool, - packet_idx: usize, -) { - let nb = mode.nb_ebands; - if coded_channels == 0 || lm > 3 { - return; - } - let run_id = "run-anti-collapse-v1"; - let probe_packet = packet_idx <= 8 || trace_this_packet; - let mut seed_local = seed; - let blocks = 1usize << lm; - let block_mask_limit = if blocks >= 8 { - 0xFFu8 - } else { - ((1u16 << blocks) - 1) as u8 - }; - if probe_packet { - let data = format!( - "{{\"packet_idx\":{},\"lm\":{},\"coded_channels\":{},\"start\":{},\"end\":{},\"seed_in\":{},\"blocks\":{},\"collapse_masks_len\":{},\"block_mask_limit\":{}}}", - packet_idx, - lm, - coded_channels, - start, - end, - seed, - blocks, - collapse_masks.len(), - block_mask_limit - ); - // #region agent log - append_anti_collapse_debug_log( - run_id, - "H1", - "crates/opus-decoder/src/celt/bands.rs:anti_collapse", - "anti_collapse_entry", - &data, - ); - // #endregion - } - - for i in start..end { - let n0 = (mode.e_bands[i + 1] - mode.e_bands[i]) as usize; - if n0 == 0 || i >= pulses.len() { - continue; - } - let depth = (((1 + pulses[i].max(0)) as usize) / n0) >> lm; - let thresh = 0.5 * (2.0f32).powf(-0.125 * depth as f32); - let sqrt_1 = 1.0 / ((n0 << lm) as f32).sqrt(); - let band_start = (mode.e_bands[i] as usize) << lm; - let band_len = n0 << lm; - - for c in 0..coded_channels.min(2) { - let chan = if c == 0 { - &mut x[..] - } else if let Some(yv) = y.as_deref_mut() { - yv - } else { - continue; - }; - if band_start + band_len > chan.len() { - continue; - } - let mut prev1 = prev1_log_e[c * nb + i]; - let mut prev2 = prev2_log_e[c * nb + i]; - if coded_channels == 1 { - prev1 = prev1.max(prev1_log_e[nb + i]); - prev2 = prev2.max(prev2_log_e[nb + i]); - } - let ediff = (log_e[c * nb + i] - prev1.min(prev2)).max(0.0); - let mut r = 2.0 * (2.0f32).powf(-ediff); - if lm == 3 { - r *= std::f32::consts::SQRT_2; - } - r = r.min(thresh) * sqrt_1; - - let mask_idx = i * coded_channels + c; - if mask_idx >= collapse_masks.len() { - continue; - } - let collapse_mask = collapse_masks[mask_idx] & block_mask_limit; - let mut injected_blocks = 0usize; - let band = &mut chan[band_start..band_start + band_len]; - let band_pre_abs = if probe_packet { - band.iter().map(|v| v.abs()).sum::() - } else { - 0.0 - }; - for k in 0..blocks { - if (collapse_mask & (1u8 << k)) == 0 { - for j in 0..n0 { - seed_local = celt_lcg_rand(seed_local); - band[(j << lm) + k] = if (seed_local & 0x8000) != 0 { r } else { -r }; - } - injected_blocks += 1; - } - } - if injected_blocks > 0 { - vq::renormalise_vector(band, 1.0); - } - let band_post_abs = if probe_packet { - band.iter().map(|v| v.abs()).sum::() - } else { - 0.0 - }; - if probe_packet && (collapse_mask != block_mask_limit || injected_blocks > 0) { - let data = format!( - "{{\"packet_idx\":{},\"band\":{},\"channel\":{},\"depth\":{},\"ediff\":{},\"r\":{},\"collapse_mask\":{},\"block_mask_limit\":{},\"injected_blocks\":{},\"band_pre_abs\":{},\"band_post_abs\":{}}}", - packet_idx, - i, - c, - depth, - ediff, - r, - collapse_mask, - block_mask_limit, - injected_blocks, - band_pre_abs, - band_post_abs - ); - let hypothesis = if injected_blocks > 0 { "H3" } else { "H2" }; - // #region agent log - append_anti_collapse_debug_log( - run_id, - hypothesis, - "crates/opus-decoder/src/celt/bands.rs:anti_collapse", - "anti_collapse_band_probe", - &data, - ); - // #endregion - } - if trace_this_packet && (injected_blocks > 0 || i >= 18) { - // #region agent log - debug_trace!( - "R pkt{} anti_collapse band={} ch={} depth={} ediff={:.6} r={:.6} mask=0x{:x} injected_blocks={}", - packet_idx, - i, - c, - depth, - ediff, - r, - collapse_mask, - injected_blocks - ); - // #endregion - } - } - } -} - -/// Integer square-root for non-negative 32-bit values. -/// -/// Params: input integer value. -/// Returns: floor(sqrt(value)). -fn isqrt32(v: u32) -> u32 { - if v == 0 { - return 0; - } - let mut x = v; - let mut y = (x + 1) >> 1; - while y < x { - x = y; - y = (x + v / x) >> 1; - } - x -} - -/// Integer ilog helper equivalent to CELT `EC_ILOG`. -/// -/// Params: positive integer value. -/// Returns: floor(log2(v)) + 1. -fn ec_ilog(v: i32) -> i32 { - debug_assert!(v > 0); - 32 - (v as u32).leading_zeros() as i32 -} - -/// Bit-exact fractional multiply equivalent to `FRAC_MUL16`. -/// -/// Params: signed 16-bit fixed point multiplicands. -/// Returns: rounded product shifted by 15. -fn frac_mul16(a: i32, b: i32) -> i32 { - (16384 + ((a as i16 as i32) * (b as i16 as i32))) >> 15 -} - -/// Bit-exact cosine approximation from libopus `bands.c`. -/// -/// Params: angle in Q14 domain [0, 16384]. -/// Returns: cosine approximation in Q15-ish integer domain. -fn bitexact_cos(x: i32) -> i32 { - let tmp = (4096 + x * x) >> 13; - let x2 = tmp; - 1 + (32767 - x2) + frac_mul16(x2, -7651 + frac_mul16(x2, 8277 + frac_mul16(-626, x2))) -} - -/// Bit-exact log2(tan()) approximation for split bit rebalancing. -/// -/// Params: side and mid cosine terms. -/// Returns: fixed-point log2(tan()) approximation. -fn bitexact_log2tan(isin: i32, icos: i32) -> i32 { - let lc = ec_ilog(icos.max(1)); - let ls = ec_ilog(isin.max(1)); - let icos_n = icos << (15 - lc); - let isin_n = isin << (15 - ls); - (ls - lc) * (1 << 11) + frac_mul16(isin_n, frac_mul16(isin_n, -2597) + 7932) - - frac_mul16(icos_n, frac_mul16(icos_n, -2597) + 7932) -} - -/// One-stage Haar transform used by CELT TF reordering. -/// -/// Params: mutable vector, base size and stride. -/// Returns: nothing; vector is updated in-place. -fn haar1(x: &mut [f32], n0: usize, stride: usize) { - let half = n0 >> 1; - for i in 0..stride { - for j in 0..half { - let idx0 = stride * 2 * j + i; - let idx1 = stride * (2 * j + 1) + i; - let tmp1 = std::f32::consts::FRAC_1_SQRT_2 * x[idx0]; - let tmp2 = std::f32::consts::FRAC_1_SQRT_2 * x[idx1]; - x[idx0] = tmp1 + tmp2; - x[idx1] = tmp1 - tmp2; - } - } -} - -/// Convert natural to ordery Hadamard index order. -/// -/// Params: vector, segment size, stride and hadamard mode. -/// Returns: nothing; vector is updated in-place. -fn deinterleave_hadamard(x: &mut [f32], n0: usize, stride: usize, hadamard: bool) { - let n = n0 * stride; - let mut tmp = vec![0.0f32; n]; - if hadamard { - let ordery = &ORDERY_TABLE[(stride - 2)..]; - for i in 0..stride { - for j in 0..n0 { - tmp[ordery[i] * n0 + j] = x[j * stride + i]; - } - } - } else { - for i in 0..stride { - for j in 0..n0 { - tmp[i * n0 + j] = x[j * stride + i]; - } - } - } - x[..n].copy_from_slice(&tmp); -} - -/// Convert ordery Hadamard back to natural order. -/// -/// Params: vector, segment size, stride and hadamard mode. -/// Returns: nothing; vector is updated in-place. -fn interleave_hadamard(x: &mut [f32], n0: usize, stride: usize, hadamard: bool) { - let n = n0 * stride; - let mut tmp = vec![0.0f32; n]; - if hadamard { - let ordery = &ORDERY_TABLE[(stride - 2)..]; - for i in 0..stride { - for j in 0..n0 { - tmp[j * stride + i] = x[ordery[i] * n0 + j]; - } - } - } else { - for i in 0..stride { - for j in 0..n0 { - tmp[j * stride + i] = x[i * n0 + j]; - } - } - } - x[..n].copy_from_slice(&tmp); -} - -/// Apply quant_band decode-time TF/frequency post-processing. -/// -/// Params: decoded band vector and TF transform context. -/// Returns: nothing; vector is updated in-place. -fn apply_quant_band_post_tf( - x: &mut [f32], - n: usize, - b0: usize, - recombine: usize, - time_divide: usize, - long_blocks: bool, -) { - let mut n_b = n / b0.max(1); - if b0 > 1 { - interleave_hadamard(x, n_b >> recombine, b0 << recombine, long_blocks); - } - let mut b = b0; - for _ in 0..time_divide { - b >>= 1; - n_b <<= 1; - haar1(x, n_b, b); - } - for k in 0..recombine { - haar1(x, n >> k, 1 << k); - } -} - -/// Apply quant-band collapse-mask post-processing after TF transforms. -/// -/// Params: raw collapse mask from `quant_partition`, transformed block count, -/// recombine/time-divide context. -/// Returns: collapse mask mapped back to frame block layout. -fn post_tf_collapse_mask(mut cm: u32, b0: usize, recombine: usize, time_divide: usize) -> u32 { - let mut b = b0.max(1); - for _ in 0..time_divide { - b >>= 1; - if b > 0 { - cm |= cm >> b; - } - } - for _ in 0..recombine { - cm = BIT_DEINTERLEAVE_TABLE[(cm & 0x0F) as usize] as u32; - } - let final_b = b << recombine; - if final_b >= 32 { - cm - } else { - cm & ((1u32 << final_b) - 1) - } -} - -/// Transform fill mask through TF recombine/time-divide steps. -/// -/// Params: pre-TF fill mask, original block count, and TF context. -/// Returns: fill mask mapped to quant-partition block layout. -fn transform_fill_for_quant( - mut fill: u32, - mut blocks_orig: usize, - recombine: usize, - time_divide: usize, -) -> u32 { - for _ in 0..recombine { - let lo = (fill & 0x0F) as usize; - let hi = ((fill >> 4) & 0x0F) as usize; - fill = BIT_INTERLEAVE_TABLE[lo] | (BIT_INTERLEAVE_TABLE[hi] << 2); - } - for _ in 0..time_divide { - if blocks_orig == 0 { - break; - } - fill |= fill << blocks_orig; - blocks_orig <<= 1; - } - fill -} - -/// Prepare lowband reference for decode-time folding under TF transforms. -/// -/// Params: lowband slice and TF transform context. -/// Returns: transformed lowband scratch data. -fn transform_lowband_for_decode( - lowband: &[f32], - n: usize, - frame_blocks: usize, - recombine: usize, - tf_change: i32, - long_blocks: bool, -) -> Vec { - if lowband.len() != n { - return lowband.to_vec(); - } - let mut out = lowband.to_vec(); - let mut n_b = n / frame_blocks.max(1); - for k in 0..recombine { - haar1(&mut out, n >> k, 1 << k); - } - let mut b = frame_blocks >> recombine; - n_b <<= recombine; - let mut tf = tf_change; - while (n_b & 1) == 0 && tf < 0 { - haar1(&mut out, n_b, b); - b <<= 1; - n_b >>= 1; - tf += 1; - } - if b > 1 { - deinterleave_hadamard(&mut out, n_b >> recombine, b << recombine, long_blocks); - } - out -} - -/// Duplicate first-band folding history for hybrid transition, like libopus. -/// -/// Params: mode, normalized history buffers (starting at `norm_offset`), -/// CELT start band, block multiplier `m`, and dual-stereo flag. -/// Returns: nothing. -fn special_hybrid_folding_decode( - mode: &CeltMode, - norm_x: &mut [f32], - norm_y: &mut [f32], - start: usize, - m: usize, - dual_stereo_on: bool, -) { - if start + 2 >= mode.e_bands.len() { - return; - } - let n1 = m * (mode.e_bands[start + 1] as usize - mode.e_bands[start] as usize); - let n2 = m * (mode.e_bands[start + 2] as usize - mode.e_bands[start + 1] as usize); - if n2 <= n1 || n1 < (n2 - n1) { - return; - } - let len = n2 - n1; - let src_start = 2 * n1 - n2; - let src_end = src_start + len; - let dst_start = n1; - let dst_end = dst_start + len; - if src_end <= norm_x.len() && dst_end <= norm_x.len() { - norm_x.copy_within(src_start..src_end, dst_start); - } - if dual_stereo_on && src_end <= norm_y.len() && dst_end <= norm_y.len() { - norm_y.copy_within(src_start..src_end, dst_start); - } -} - -/// Merge decoded stereo mid/side vectors back to left/right. -/// -/// Matches libopus `stereo_merge()`: `x` is unscaled mid, `y` is already side-scaled. -/// Params: mutable mid/side vectors and decoded mid gain. -/// Returns: nothing; `x` and `y` become left/right channels. -fn stereo_merge_float(x: &mut [f32], y: &mut [f32], mid: f32) { - let xp = x.iter().zip(y.iter()).map(|(a, b)| a * b).sum::() * mid; - let side_energy = y.iter().map(|v| v * v).sum::(); - let el = mid * mid + side_energy - 2.0 * xp; - let er = mid * mid + side_energy + 2.0 * xp; - if er < 6.0e-4 || el < 6.0e-4 { - y.copy_from_slice(x); - return; - } - let lgain = 1.0 / el.sqrt(); - let rgain = 1.0 / er.sqrt(); - for i in 0..x.len() { - let l = mid * x[i] - y[i]; - let r = mid * x[i] + y[i]; - x[i] = lgain * l; - y[i] = rgain * r; - } -} - -/// Compute theta quantization resolution for recursive split. -/// -/// Params: partition size `n`, bit budget `b`, split `offset`, pulse cap and stereo flag. -/// Returns: theta resolution value `qn`. -fn compute_qn(n: usize, b: i32, offset: i32, pulse_cap: i32, stereo: bool) -> i32 { - let mut n2 = 2 * n as i32 - 1; - if stereo && n == 2 { - n2 -= 1; - } - let mut qb = (b + n2 * offset) / n2; - qb = qb.min(b - pulse_cap - (4 << BITRES)); - qb = qb.min(8 << BITRES); - if qb < (1 << BITRES >> 1) { - 1 - } else { - let mut qn = EXP2_TABLE8[(qb & 0x7) as usize] >> (14 - (qb >> BITRES)); - qn = ((qn + 1) >> 1) << 1; - qn.min(256) - } -} - -/// Decode split theta for mono recursive partition. -/// -/// Params: mode, band index and split context (`n`, `b`, `blocks`, `fill`). -/// Returns: `(delta, qalloc, itheta, imid, iside)` for recursive split. -#[allow(clippy::too_many_arguments)] -fn decode_theta_mono( - mode: &CeltMode, - band: usize, - dec: &mut EcDec<'_>, - n: usize, - b: &mut i32, - blocks: usize, - blocks0: usize, - lm: i32, - fill: &mut u32, -) -> (i32, i32, i32, i32, i32) { - let pulse_cap = mode.log_n[band] as i32 + lm * (1 << BITRES); - let offset = (pulse_cap >> 1) - QTHETA_OFFSET; - let qn = compute_qn(n, *b, offset, pulse_cap, false); - let tell_before = dec.tell_frac() as i32; - let mut itheta = 0i32; - if qn != 1 { - if blocks0 > 1 { - itheta = dec.dec_uint((qn + 1) as u32) as i32; - } else { - let ft = ((qn >> 1) + 1) * ((qn >> 1) + 1); - let fm = dec.decode(ft as u32) as i32; - let (fl, fs, ith) = if fm < ((qn >> 1) * ((qn >> 1) + 1) >> 1) { - let ith = ((isqrt32((8 * fm + 1) as u32) as i32) - 1) >> 1; - let fs = ith + 1; - let fl = ith * (ith + 1) >> 1; - (fl, fs, ith) - } else { - let ith = (2 * (qn + 1) - (isqrt32((8 * (ft - fm - 1) + 1) as u32) as i32)) >> 1; - let fs = qn + 1 - ith; - let fl = ft - ((qn + 1 - ith) * (qn + 2 - ith) >> 1); - (fl, fs, ith) - }; - dec.update(fl as u32, (fl + fs) as u32, ft as u32); - itheta = ith; - } - itheta = (itheta * 16384) / qn; - } - let qalloc = dec.tell_frac() as i32 - tell_before; - *b -= qalloc; - - if itheta == 0 { - *fill &= (1u32 << blocks) - 1; - return (-16384, qalloc, itheta, 32767, 0); - } - if itheta == 16384 { - *fill &= ((1u32 << blocks) - 1) << blocks; - return (16384, qalloc, itheta, 0, 32767); - } - let imid = bitexact_cos(itheta); - let iside = bitexact_cos(16384 - itheta); - let delta = frac_mul16((n as i32 - 1) << 7, bitexact_log2tan(iside, imid)); - (delta, qalloc, itheta, imid, iside) -} - -/// Decode stereo theta split and return side allocation context. -/// -/// Params: mode, band, split size/blocks, bit budget, intensity limit and decoder state. -/// Returns: `(inv, delta, qalloc, itheta, mid_gain, side_gain)` for stereo split coding. -#[allow(clippy::too_many_arguments)] -fn decode_theta_stereo( - mode: &CeltMode, - band: usize, - dec: &mut EcDec<'_>, - n: usize, - b: &mut i32, - blocks: usize, - _blocks0: usize, - lm: i32, - fill: &mut u32, - intensity: i32, - remaining_bits: i32, - disable_inv: bool, -) -> (bool, i32, i32, i32, f32, f32) { - let pulse_cap = mode.log_n[band] as i32 + lm * (1 << BITRES); - let offset = (pulse_cap >> 1) - - if n == 2 { - QTHETA_OFFSET_TWOPHASE - } else { - QTHETA_OFFSET - }; - let mut qn = compute_qn(n, *b, offset, pulse_cap, true); - if band as i32 >= intensity { - qn = 1; - } - let tell_before = dec.tell_frac() as i32; - let mut itheta = 0i32; - let mut inv = false; - if qn != 1 { - if n > 2 { - let p0 = 3i32; - let x0 = qn >> 1; - let ft = p0 * (x0 + 1) + x0; - let fs = dec.decode(ft as u32) as i32; - let x = if fs < (x0 + 1) * p0 { - fs / p0 - } else { - x0 + 1 + (fs - (x0 + 1) * p0) - }; - let (fl, fh) = if x <= x0 { - (p0 * x, p0 * (x + 1)) - } else { - ((x - 1 - x0) + (x0 + 1) * p0, (x - x0) + (x0 + 1) * p0) - }; - dec.update(fl as u32, fh as u32, ft as u32); - itheta = x; - } else { - itheta = dec.dec_uint((qn + 1) as u32) as i32; - } - itheta = (itheta * 16384) / qn; - } else { - if *b > (2 << BITRES) && remaining_bits > (2 << BITRES) { - inv = dec.dec_bit_logp(2); - } - if disable_inv { - inv = false; - } - itheta = 0; - } - let qalloc = dec.tell_frac() as i32 - tell_before; - *b -= qalloc; - if itheta == 0 { - *fill &= (1u32 << blocks) - 1; - return (inv, -16384, qalloc, itheta, 1.0, 0.0); - } - if itheta == 16384 { - *fill &= ((1u32 << blocks) - 1) << blocks; - return (inv, 16384, qalloc, itheta, 0.0, 1.0); - } - let imid = bitexact_cos(itheta); - let iside = bitexact_cos(16384 - itheta); - let delta = frac_mul16((n as i32 - 1) << 7, bitexact_log2tan(iside, imid)); - ( - inv, - delta, - qalloc, - itheta, - (imid as f32) * (1.0 / 32768.0), - (iside as f32) * (1.0 / 32768.0), - ) -} - -/// Decode one stereo band partition (decode-only path). -/// -/// Params: stereo vectors, split context, folding source and per-band allocation state. -/// Returns: collapse mask for this stereo band. -#[allow(clippy::too_many_arguments)] -fn quant_band_stereo_decode( - mode: &CeltMode, - x: &mut [f32], - y: &mut [f32], - band_idx: usize, - mut b: i32, - blocks_quant: usize, - blocks_orig: usize, - lowband_x: Option<&[f32]>, - lm: i32, - mut fill: u32, - spread: i32, - disable_inv: bool, - dec: &mut EcDec<'_>, - seed: &mut u32, - remaining_bits: &mut i32, - intensity: i32, - recombine: usize, - time_divide: usize, - long_blocks: bool, - mut mid_history_out: Option<&mut [f32]>, - trace_this_packet: bool, - packet_idx: usize, -) -> u32 { - let n = x.len(); - let orig_fill = fill; - if n == 1 { - let mut decode_sign = |v: &mut f32| { - if *remaining_bits >= (1 << BITRES) { - let sign = dec.dec_bits(1); - *remaining_bits -= 1 << BITRES; - *v = if sign != 0 { -1.0 } else { 1.0 }; - } else { - *v = 1.0; - } - }; - decode_sign(&mut x[0]); - decode_sign(&mut y[0]); - if let Some(dst) = mid_history_out.as_deref_mut() { - if !dst.is_empty() { - // Match libopus quant_band_n1(): preserve X[0] as lowband history. - dst[0] = x[0]; - } - } - return 1; - } - - let (inv, delta, qalloc, itheta, mid_gain, side_gain) = decode_theta_stereo( - mode, - band_idx, - dec, - n, - &mut b, - blocks_orig, - blocks_orig, - lm, - &mut fill, - intensity, - *remaining_bits, - disable_inv, - ); - if trace_this_packet && (band_idx == 12 || band_idx >= 18) { - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"bea564\",\"runId\":\"run-stereo-probe\",\"hypothesisId\":\"H7\",\"location\":\"crates/opus-decoder/src/celt/bands.rs:260\",\"message\":\"stereo_theta_decode\",\"data\":{{\"packet_idx\":{},\"band\":{},\"itheta\":{},\"qalloc\":{},\"delta\":{},\"inv\":{}}},\"timestamp\":{}}}\n", - packet_idx, band_idx, itheta, qalloc, delta, inv, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - if trace_this_packet && band_idx >= 9 { - // #region agent log - debug_trace!( - "R pkt{} stereo_theta band {} itheta={} qalloc={} delta={} inv={} b_after_theta={} fill=0x{:x} mid_gain={:.6} side_gain={:.6}", - packet_idx, - band_idx, - itheta, - qalloc, - delta, - inv, - b, - fill, - mid_gain, - side_gain - ); - // #endregion - } - let fill_mid = transform_fill_for_quant(fill, blocks_orig, recombine, time_divide); - let fill_side_seed = if blocks_orig >= 32 { - 0 - } else { - fill >> blocks_orig - }; - let fill_side = transform_fill_for_quant(fill_side_seed, blocks_orig, recombine, time_divide); - let orig_fill_mid = transform_fill_for_quant(orig_fill, blocks_orig, recombine, time_divide); - - if n == 2 { - let mut mbits = b; - let mut sbits = 0; - if itheta != 0 && itheta != 16384 { - sbits = 1 << BITRES; - } - mbits -= sbits; - *remaining_bits -= qalloc + sbits; - let mut sign = 0i32; - if sbits != 0 { - sign = dec.dec_bits(1) as i32; - } - let sign = 1 - 2 * sign; - if itheta > 8192 { - let cm = quant_partition_mono( - mode, - y, - band_idx, - mbits, - blocks_quant, - lowband_x, - lm, - 1.0, - orig_fill_mid, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ); - apply_quant_band_post_tf(y, n, blocks_quant, recombine, time_divide, long_blocks); - x[0] = -(sign as f32) * y[1]; - x[1] = (sign as f32) * y[0]; - if let Some(dst) = mid_history_out.as_deref_mut() { - let scale = (n as f32).sqrt(); - for (d, s) in dst.iter_mut().zip(y.iter()) { - *d = *s * scale; - } - } - let xl0 = mid_gain * x[0]; - let xl1 = mid_gain * x[1]; - let yr0 = side_gain * y[0]; - let yr1 = side_gain * y[1]; - x[0] = xl0 - yr0; - y[0] = xl0 + yr0; - x[1] = xl1 - yr1; - y[1] = xl1 + yr1; - if inv { - y[0] = -y[0]; - y[1] = -y[1]; - } - return post_tf_collapse_mask(cm, blocks_quant, recombine, time_divide); - } - let cm = quant_partition_mono( - mode, - x, - band_idx, - mbits, - blocks_quant, - lowband_x, - lm, - 1.0, - orig_fill_mid, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ); - apply_quant_band_post_tf(x, n, blocks_quant, recombine, time_divide, long_blocks); - if let Some(dst) = mid_history_out.as_deref_mut() { - let scale = (n as f32).sqrt(); - for (d, s) in dst.iter_mut().zip(x.iter()) { - *d = *s * scale; - } - } - y[0] = -(sign as f32) * x[1]; - y[1] = (sign as f32) * x[0]; - let xl0 = mid_gain * x[0]; - let xl1 = mid_gain * x[1]; - let yr0 = side_gain * y[0]; - let yr1 = side_gain * y[1]; - x[0] = xl0 - yr0; - y[0] = xl0 + yr0; - x[1] = xl1 - yr1; - y[1] = xl1 + yr1; - if inv { - y[0] = -y[0]; - y[1] = -y[1]; - } - return post_tf_collapse_mask(cm, blocks_quant, recombine, time_divide); - } - - let mut mbits = 0.max(b.min((b - delta) / 2)); - let mut sbits = b - mbits; - if trace_this_packet && band_idx >= 9 { - // #region agent log - debug_trace!( - "R pkt{} stereo_split band {} mbits={} sbits={} rem_bits={} fill=0x{:x}", - packet_idx, - band_idx, - mbits, - sbits, - *remaining_bits, - fill - ); - // #endregion - } - *remaining_bits -= qalloc; - let mut rebalance = *remaining_bits; - let cm = if mbits >= sbits { - let mut cm = quant_partition_mono( - mode, - x, - band_idx, - mbits, - blocks_quant, - lowband_x, - lm, - 1.0, - fill_mid, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ); - rebalance = mbits - (rebalance - *remaining_bits); - if rebalance > (3 << BITRES) && itheta != 0 { - sbits += rebalance - (3 << BITRES); - } - cm |= quant_partition_mono( - mode, - y, - band_idx, - sbits, - blocks_quant, - None, - lm, - side_gain, - fill_side, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ); - apply_quant_band_post_tf(x, n, blocks_quant, recombine, time_divide, long_blocks); - apply_quant_band_post_tf(y, n, blocks_quant, recombine, time_divide, long_blocks); - cm - } else { - let mut cm = quant_partition_mono( - mode, - y, - band_idx, - sbits, - blocks_quant, - None, - lm, - side_gain, - fill_side, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ); - rebalance = sbits - (rebalance - *remaining_bits); - if rebalance > (3 << BITRES) && itheta != 16384 { - mbits += rebalance - (3 << BITRES); - } - cm |= quant_partition_mono( - mode, - x, - band_idx, - mbits, - blocks_quant, - lowband_x, - lm, - 1.0, - fill_mid, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ); - apply_quant_band_post_tf(x, n, blocks_quant, recombine, time_divide, long_blocks); - apply_quant_band_post_tf(y, n, blocks_quant, recombine, time_divide, long_blocks); - cm - }; - if let Some(dst) = mid_history_out.as_deref_mut() { - let scale = (n as f32).sqrt(); - for (d, s) in dst.iter_mut().zip(x.iter()) { - *d = *s * scale; - } - } - stereo_merge_float(x, y, mid_gain); - if inv { - for v in y.iter_mut() { - *v = -*v; - } - } - post_tf_collapse_mask(cm, blocks_quant, recombine, time_divide) -} - -/// Decode one mono partition with recursive split. -/// -/// Params: mode, target partition, band index and recursive coding context. -/// Returns: collapse mask for this partition. -#[allow(clippy::too_many_arguments)] -fn quant_partition_mono( - mode: &CeltMode, - x: &mut [f32], - band_idx: usize, - mut b: i32, - mut blocks: usize, - lowband: Option<&[f32]>, - mut lm: i32, - gain: f32, - mut fill: u32, - spread: i32, - dec: &mut EcDec<'_>, - seed: &mut u32, - remaining_bits: &mut i32, - trace_this_packet: bool, - packet_idx: usize, -) -> u32 { - let n0 = x.len(); - let blocks0 = blocks; - if n0 == 1 { - if *remaining_bits >= (1 << BITRES) { - let sign = dec.dec_bits(1); - *remaining_bits -= 1 << BITRES; - x[0] = if sign != 0 { -1.0 } else { 1.0 }; - } else { - x[0] = 1.0; - } - if trace_this_packet && band_idx == 0 { - // #region agent log - debug_trace!( - "R pkt{} qpart n1: rem_bits={} tell={} x0={}", - packet_idx, - *remaining_bits, - dec.tell_frac(), - x[0] - ); - // #endregion - } - return 1; - } - if trace_this_packet && (band_idx == 12 || band_idx >= 18) { - // #region agent log - debug_trace!( - "R pkt{} qpart enter: N={} b={} B={} lm={} rem_bits={} tell={}", - packet_idx, - n0, - b, - blocks, - lm, - *remaining_bits, - dec.tell_frac() - ); - // #endregion - } - let cache_row = ((lm + 1) as usize) - .saturating_mul(mode.nb_ebands) - .saturating_add(band_idx); - let do_split = if lm != -1 && n0 > 2 && cache_row < mode.cache.index.len() { - let base = mode.cache.index[cache_row]; - if base >= 0 { - let cache = &mode.cache.bits[base as usize..]; - b > cache[cache[0] as usize] as i32 + 12 - } else { - false - } - } else { - false - }; - if packet_idx == 6 && band_idx == 8 { - let data = format!( - "{{\"packet_idx\":{},\"band\":{},\"stage\":\"entry\",\"n\":{},\"b\":{},\"blocks\":{},\"lm\":{},\"do_split\":{},\"remaining_bits\":{},\"tell\":{}}}", - packet_idx, - band_idx, - n0, - b, - blocks, - lm, - do_split, - *remaining_bits, - dec.tell_frac() - ); - // #region agent log - append_anti_collapse_debug_log( - "run-pkt6-quant-decision-v1", - "H89", - "crates/opus-decoder/src/celt/bands.rs:quant_partition_mono", - "rust_pkt6_band8_qpart_entry", - &data, - ); - // #endregion - } - if do_split { - let n = n0 >> 1; - let (x_lo, x_hi) = x.split_at_mut(n); - lm -= 1; - if blocks == 1 { - fill = (fill & 1) | (fill << 1); - } - blocks = (blocks + 1) >> 1; - let (mut delta, qalloc, itheta, imid, iside) = decode_theta_mono( - mode, band_idx, dec, n, &mut b, blocks, blocks0, lm, &mut fill, - ); - let mid = (imid as f32) * (1.0 / 32768.0); - let side = (iside as f32) * (1.0 / 32768.0); - if blocks0 > 1 && (itheta & 0x3fff) != 0 { - if itheta > 8192 { - delta -= delta >> (4 - lm); - } else { - delta = 0.min(delta + (((n as i32) << BITRES) >> (5 - lm))); - } - } - let mut mbits = 0.max(b.min((b - delta) / 2)); - let mut sbits = b - mbits; - *remaining_bits -= qalloc; - if packet_idx == 6 && band_idx == 8 { - let data = format!( - "{{\"packet_idx\":{},\"band\":{},\"stage\":\"split\",\"n\":{},\"mbits\":{},\"sbits\":{},\"delta\":{},\"qalloc\":{},\"itheta\":{},\"blocks\":{},\"lm\":{},\"remaining_bits\":{},\"tell\":{}}}", - packet_idx, - band_idx, - n0, - mbits, - sbits, - delta, - qalloc, - itheta, - blocks, - lm, - *remaining_bits, - dec.tell_frac() - ); - // #region agent log - append_anti_collapse_debug_log( - "run-pkt6-quant-decision-v1", - "H89", - "crates/opus-decoder/src/celt/bands.rs:quant_partition_mono", - "rust_pkt6_band8_qpart_split", - &data, - ); - // #endregion - } - if trace_this_packet && (band_idx == 12 || band_idx >= 18) { - // #region agent log - debug_trace!( - "R pkt{} qpart split: N={} n={} qalloc={} itheta={} delta={} mbits={} sbits={} rem_bits={} tell={}", - packet_idx, - n0, - n, - qalloc, - itheta, - delta, - mbits, - sbits, - *remaining_bits, - dec.tell_frac() - ); - // #endregion - } - let next_lowband2 = lowband.map(|lb| if lb.len() > n { &lb[n..] } else { &lb[0..0] }); - let mut rebalance = *remaining_bits; - if mbits >= sbits { - let mut cm = quant_partition_mono( - mode, - x_lo, - band_idx, - mbits, - blocks, - lowband.map(|lb| &lb[..lb.len().min(n)]), - lm, - gain * mid, - fill, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ); - rebalance = mbits - (rebalance - *remaining_bits); - if rebalance > (3 << BITRES) && itheta != 0 { - sbits += rebalance - (3 << BITRES); - } - cm |= quant_partition_mono( - mode, - x_hi, - band_idx, - sbits, - blocks, - next_lowband2, - lm, - gain * side, - fill >> blocks, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ) << (blocks0 >> 1); - return cm; - } - - let mut cm = quant_partition_mono( - mode, - x_hi, - band_idx, - sbits, - blocks, - next_lowband2, - lm, - gain * side, - fill >> blocks, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ) << (blocks0 >> 1); - rebalance = sbits - (rebalance - *remaining_bits); - if rebalance > (3 << BITRES) && itheta != 16384 { - mbits += rebalance - (3 << BITRES); - } - cm |= quant_partition_mono( - mode, - x_lo, - band_idx, - mbits, - blocks, - lowband.map(|lb| &lb[..lb.len().min(n)]), - lm, - gain * mid, - fill, - spread, - dec, - seed, - remaining_bits, - trace_this_packet, - packet_idx, - ); - return cm; - } - - let mut q = rate::bits2pulses(mode, band_idx, lm, b.max(0)); - let mut curr_bits = rate::pulses2bits(mode, band_idx, lm, q); - *remaining_bits -= curr_bits; - while *remaining_bits < 0 && q > 0 { - *remaining_bits += curr_bits; - q -= 1; - curr_bits = rate::pulses2bits(mode, band_idx, lm, q); - *remaining_bits -= curr_bits; - } - if trace_this_packet && (band_idx == 12 || band_idx >= 18) { - // #region agent log - debug_trace!( - "R pkt{} qpart nosplit: N={} b={} q={} curr_bits={} rem_bits={} tell={}", - packet_idx, - n0, - b, - q, - curr_bits, - *remaining_bits, - dec.tell_frac() - ); - // #endregion - } - if q != 0 { - let k = rate::get_pulses(q); - if packet_idx == 6 && band_idx == 8 { - let data = format!( - "{{\"packet_idx\":{},\"band\":{},\"stage\":\"nosplit_q\",\"n\":{},\"q\":{},\"k\":{},\"curr_bits\":{},\"blocks\":{},\"lm\":{},\"remaining_bits\":{},\"tell\":{}}}", - packet_idx, - band_idx, - n0, - q, - k, - curr_bits, - blocks, - lm, - *remaining_bits, - dec.tell_frac() - ); - // #region agent log - append_anti_collapse_debug_log( - "run-pkt6-quant-decision-v1", - "H89", - "crates/opus-decoder/src/celt/bands.rs:quant_partition_mono", - "rust_pkt6_band8_qpart_nosplit_q", - &data, - ); - // #endregion - } - return vq::alg_unquant(x, k, spread, blocks, band_idx, false, dec, gain).collapse_mask - as u32; - } - - let cm_mask = if blocks >= 32 { - u32::MAX - } else { - (1u32 << blocks) - 1 - }; - let fill_masked = fill & cm_mask; - if trace_this_packet && (band_idx == 12 || band_idx >= 18) { - let (has_lowband, lowband_abs, lowband_len) = if let Some(lb) = lowband { - (true, lb.iter().map(|v| v.abs()).sum::(), lb.len()) - } else { - (false, -1.0, 0usize) - }; - // #region agent log - debug_trace!( - "R pkt{} qpart q0: N={} b={} B={} fill=0x{:x} has_lowband={} lowband_len={} lowband_abs={:.6} gain={:.6}", - packet_idx, - n0, - b, - blocks, - fill_masked, - has_lowband, - lowband_len, - lowband_abs, - gain - ); - // #endregion - } - if fill_masked == 0 { - x.fill(0.0); - return 0; - } - if let Some(lb) = lowband { - if lb.is_empty() { - for v in x.iter_mut() { - *seed = celt_lcg_rand(*seed); - *v = ((*seed as i32) >> 20) as f32; - } - vq::renormalise_vector(x, gain); - return cm_mask; - } - for (j, v) in x.iter_mut().enumerate() { - *seed = celt_lcg_rand(*seed); - let noise = if (*seed & 0x8000) != 0 { - 1.0 / 256.0 - } else { - -1.0 / 256.0 - }; - *v = lb[j % lb.len()] + noise; - } - vq::renormalise_vector(x, gain); - if blocks < 32 && fill_masked != cm_mask { - let n_per_block = n0 / blocks.max(1); - for b in 0..blocks { - if (fill_masked & (1u32 << b)) == 0 { - let start = b * n_per_block; - let end = (start + n_per_block).min(n0); - x[start..end].fill(0.0); - } - } - } - return fill_masked; - } - for v in x.iter_mut() { - *seed = celt_lcg_rand(*seed); - *v = ((*seed as i32) >> 20) as f32; - } - vq::renormalise_vector(x, gain); - cm_mask -} - -/// Decode all CELT bands in mono decode-only mode. -/// -/// Params: CELT mode, active band range, output normalized spectrum `x`, per-band -/// pulse allocation, transient flag, spread mode, TF flags, total bit budget, -/// running balance, coded band limit, decoder state and random seed. -/// Returns: collapse mask per band. -#[allow(clippy::too_many_arguments)] -pub(crate) fn quant_all_bands_mono( - mode: &CeltMode, - start: usize, - end: usize, - x: &mut [f32], - pulses: &[i32], - short_blocks: bool, - spread: i32, - tf_res: &[i32], - total_bits_q: i32, - mut balance: i32, - coded_bands: usize, - lm: usize, - dec: &mut EcDec<'_>, - seed: &mut u32, - packet_idx: usize, - frame_call_idx: usize, -) -> Vec { - let trace_target = None; - let trace_this_packet = trace_target == Some(packet_idx); - let m = 1usize << lm; - let mut masks = vec![0u8; mode.nb_ebands]; - let mut lowband_offset = 0usize; - let mut update_lowband = true; - let norm_offset = m * mode.e_bands[start] as usize; - let mut norm_hist = vec![0.0f32; x.len()]; - for i in start..end { - let tell = dec.tell_frac() as i32; - if i != start { - balance -= tell; - } - let remaining_bits = total_bits_q - tell - 1; - let b = if i < coded_bands { - let curr_balance = balance / ((coded_bands - i).min(3) as i32); - (pulses[i] + curr_balance) - .clamp(0, 16_383) - .min(remaining_bits + 1) - } else { - 0 - }; - let band_start = m * mode.e_bands[i] as usize; - let band_end = m * mode.e_bands[i + 1] as usize; - let n = band_end - band_start; - if i != start && (update_lowband || lowband_offset == 0) { - lowband_offset = i; - } - let frame_blocks = if short_blocks { m } else { 1 }; - let long_blocks = frame_blocks == 1; - let mut b_blocks = frame_blocks; - let spread_eff = spread.clamp(0, 3); - let tf_change = tf_res[i]; - let mut n_b = n / b_blocks.max(1); - let n_b_initial = n_b; - let mut effective_lowband = None::; - let mut x_cm = if b_blocks >= 32 { - u32::MAX - } else { - (1u32 << b_blocks) - 1 - }; - if lowband_offset != 0 && (spread != SPREAD_AGGRESSIVE || frame_blocks > 1 || tf_change < 0) - { - let lowband_start = m * mode.e_bands[lowband_offset] as usize; - let effective = lowband_start.saturating_sub(norm_offset + n); - let fold_base = effective + norm_offset; - let fold_limit = fold_base + n; - let mut fold_start = lowband_offset; - while fold_start > 0 { - fold_start -= 1; - if (m * mode.e_bands[fold_start] as usize) <= fold_base { - break; - } - } - let mut fold_end = lowband_offset; - while fold_end < i && (m * mode.e_bands[fold_end] as usize) < fold_limit { - fold_end += 1; - } - x_cm = 0; - for fold_i in fold_start..fold_end { - x_cm |= masks[fold_i] as u32; - } - effective_lowband = Some(effective); - } - let mut fill = x_cm; - let recombine = tf_change.max(0) as usize; - for _ in 0..recombine { - let lo = (fill & 0xF) as usize; - let hi = ((fill >> 4) & 0xF) as usize; - fill = BIT_INTERLEAVE_TABLE[lo] | (BIT_INTERLEAVE_TABLE[hi] << 2); - } - b_blocks >>= recombine; - n_b <<= recombine; - let mut tf_for_blocks = tf_change; - let mut time_divide = 0usize; - while (n_b & 1) == 0 && tf_for_blocks < 0 { - if b_blocks < 32 { - fill |= fill << b_blocks; - } else { - fill = u32::MAX; - } - b_blocks <<= 1; - n_b >>= 1; - tf_for_blocks += 1; - time_divide += 1; - } - if trace_this_packet { - debug_trace!( - "R pkt{} frame_call_idx={} band {} params: b={} N={} B={} fill=0x{:x} lowband_offset={} update_lowband={} spread_eff={} tf_change={}", - packet_idx, - frame_call_idx, - i, - b, - n, - b_blocks, - fill, - lowband_offset, - update_lowband, - spread_eff, - tf_change - ); - } - let mut remaining_band_bits = remaining_bits; - let lowband_range = effective_lowband.map(|eff| { - let src_start = norm_offset + eff; - (src_start, src_start + n) - }); - if trace_this_packet { - if let Some((src_start, src_end)) = lowband_range { - debug_trace!( - "R pkt{} band {} fold: lowband_start={} lowband_end={} B={} fill_in=0x{:x}", - packet_idx, - i, - src_start, - src_end, - b_blocks, - fill - ); - } else { - debug_trace!( - "R pkt{} band {} fold: lowband_start=-1 lowband_end=-1 B={} fill_in=0x{:x}", - packet_idx, - i, - b_blocks, - fill - ); - } - } - let (_x_before, x_after) = x.split_at_mut(band_start); - let lowband = - lowband_range.and_then(|(src_start, src_end)| norm_hist.get(src_start..src_end)); - let mut lowband_scratch = None; - let lowband_for_quant = if let Some(lb) = lowband { - if recombine > 0 || ((n_b_initial & 1) == 0 && tf_change < 0) || frame_blocks > 1 { - lowband_scratch = Some(transform_lowband_for_decode( - lb, - n, - frame_blocks, - recombine, - tf_change, - long_blocks, - )); - lowband_scratch.as_deref() - } else { - Some(lb) - } - } else { - None - }; - if packet_idx == 6 && i == 8 { - let (raw_abs, raw_first8) = if let Some(lb) = lowband { - let abs: f32 = lb.iter().map(|v| v.abs()).sum(); - ( - abs, - [ - lb.get(0).copied().unwrap_or(0.0), - lb.get(1).copied().unwrap_or(0.0), - lb.get(2).copied().unwrap_or(0.0), - lb.get(3).copied().unwrap_or(0.0), - lb.get(4).copied().unwrap_or(0.0), - lb.get(5).copied().unwrap_or(0.0), - lb.get(6).copied().unwrap_or(0.0), - lb.get(7).copied().unwrap_or(0.0), - ], - ) - } else { - (0.0, [0.0; 8]) - }; - let (prep_abs, prep_first8) = if let Some(lb) = lowband_for_quant { - let abs: f32 = lb.iter().map(|v| v.abs()).sum(); - ( - abs, - [ - lb.get(0).copied().unwrap_or(0.0), - lb.get(1).copied().unwrap_or(0.0), - lb.get(2).copied().unwrap_or(0.0), - lb.get(3).copied().unwrap_or(0.0), - lb.get(4).copied().unwrap_or(0.0), - lb.get(5).copied().unwrap_or(0.0), - lb.get(6).copied().unwrap_or(0.0), - lb.get(7).copied().unwrap_or(0.0), - ], - ) - } else { - (0.0, [0.0; 8]) - }; - let data = format!( - "{{\"packet_idx\":{},\"band\":{},\"raw_lowband_abs\":{},\"raw_lowband_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"prepared_lowband_abs\":{},\"prepared_lowband_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"recombine\":{},\"tf_change\":{},\"frame_blocks\":{}}}", - packet_idx, - i, - raw_abs, - raw_first8[0], - raw_first8[1], - raw_first8[2], - raw_first8[3], - raw_first8[4], - raw_first8[5], - raw_first8[6], - raw_first8[7], - prep_abs, - prep_first8[0], - prep_first8[1], - prep_first8[2], - prep_first8[3], - prep_first8[4], - prep_first8[5], - prep_first8[6], - prep_first8[7], - recombine, - tf_change, - frame_blocks - ); - // #region agent log - append_anti_collapse_debug_log( - "run-pkt6-lowband-v1", - "H86", - "crates/opus-decoder/src/celt/bands.rs:quant_all_bands_mono", - "rust_pkt6_band8_lowband", - &data, - ); - // #endregion - } - let tell_before = dec.tell_frac(); - let mut cm = quant_partition_mono( - mode, - &mut x_after[..n], - i, - b, - b_blocks, - lowband_for_quant, - lm as i32, - 1.0, - fill, - spread_eff, - dec, - seed, - &mut remaining_band_bits, - trace_this_packet, - packet_idx, - ); - let cm_raw = cm; - if packet_idx == 6 && i == 8 { - let pre_post_tf_abs: f32 = x_after[..n].iter().map(|v| v.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"band\":{},\"stage\":\"after_quant_partition_before_post_tf\",\"abs_sum\":{},\"first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"n\":{},\"b_blocks\":{},\"recombine\":{},\"time_divide\":{}}}", - packet_idx, - i, - pre_post_tf_abs, - x_after.get(0).copied().unwrap_or(0.0), - x_after.get(1).copied().unwrap_or(0.0), - x_after.get(2).copied().unwrap_or(0.0), - x_after.get(3).copied().unwrap_or(0.0), - x_after.get(4).copied().unwrap_or(0.0), - x_after.get(5).copied().unwrap_or(0.0), - x_after.get(6).copied().unwrap_or(0.0), - x_after.get(7).copied().unwrap_or(0.0), - n, - b_blocks, - recombine, - time_divide - ); - // #region agent log - append_anti_collapse_debug_log( - "run-pkt6-quant-stage-v1", - "H88", - "crates/opus-decoder/src/celt/bands.rs:quant_all_bands_mono", - "rust_pkt6_band8_pre_post_tf", - &data, - ); - // #endregion - } - apply_quant_band_post_tf( - &mut x_after[..n], - n, - b_blocks, - recombine, - time_divide, - long_blocks, - ); - cm = post_tf_collapse_mask(cm, b_blocks, recombine, time_divide); - if packet_idx <= 8 || trace_this_packet { - let data = format!( - "{{\"packet_idx\":{},\"band\":{},\"frame_blocks\":{},\"b_blocks\":{},\"recombine\":{},\"time_divide\":{},\"cm_raw\":{},\"cm_post\":{},\"cm_used\":{}}}", - packet_idx, - i, - frame_blocks, - b_blocks, - recombine, - time_divide, - cm_raw, - post_tf_collapse_mask(cm_raw, b_blocks, recombine, time_divide), - cm - ); - // #region agent log - append_anti_collapse_debug_log( - "run-collapse-mask-ab-v1", - "H8", - "crates/opus-decoder/src/celt/bands.rs:quant_all_bands_mono", - "collapse_mask_post_tf_mapping", - &data, - ); - // #endregion - } - if packet_idx == 6 { - let band_abs_sum: f32 = x_after[..n].iter().map(|v| v.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"band\":{},\"n\":{},\"b\":{},\"frame_blocks\":{},\"b_blocks\":{},\"tf_change\":{},\"spread_eff\":{},\"recombine\":{},\"time_divide\":{},\"fill\":{},\"cm\":{},\"band_abs_sum\":{},\"band_first4\":[{:.9},{:.9},{:.9},{:.9}],\"band_last4\":[{:.9},{:.9},{:.9},{:.9}],\"lowband_offset\":{},\"lowband_present\":{}}}", - packet_idx, - i, - n, - b, - frame_blocks, - b_blocks, - tf_change, - spread_eff, - recombine, - time_divide, - fill, - cm, - band_abs_sum, - x_after.get(0).copied().unwrap_or(0.0), - x_after.get(1).copied().unwrap_or(0.0), - x_after.get(2).copied().unwrap_or(0.0), - x_after.get(3).copied().unwrap_or(0.0), - x_after.get(n.saturating_sub(4)).copied().unwrap_or(0.0), - x_after.get(n.saturating_sub(3)).copied().unwrap_or(0.0), - x_after.get(n.saturating_sub(2)).copied().unwrap_or(0.0), - x_after.get(n.saturating_sub(1)).copied().unwrap_or(0.0), - lowband_offset, - lowband_for_quant.is_some() - ); - // #region agent log - append_anti_collapse_debug_log( - "run-pkt6-quant-band-v1", - "H85", - "crates/opus-decoder/src/celt/bands.rs:quant_all_bands_mono", - "rust_pkt6_quant_band_after", - &data, - ); - // #endregion - } - if i + 1 < end { - let scale = (n as f32).sqrt(); - for j in 0..n { - norm_hist[band_start + j] = x_after[j] * scale; - } - } - let tell_after = dec.tell_frac(); - if trace_this_packet { - debug_trace!( - "pkt{} frame_call_idx={} band {} tell: {}->{} ({}bits)", - packet_idx, - frame_call_idx, - i, - tell_before, - tell_after, - tell_after as i32 - tell_before as i32 - ); - debug_trace!( - "R pkt{} frame_call_idx={} band {} cm_out=0x{:x}", - packet_idx, - frame_call_idx, - i, - cm - ); - } - masks[i] = (cm & 0xFF) as u8; - balance += pulses[i] + tell; - update_lowband = b > ((n as i32) << BITRES); - } - masks -} - -/// Decode all CELT bands in stereo decode-only mode. -/// -/// Params: mode, band range, stereo normalized spectra, allocation/TF flags and coder state. -/// Returns: collapse mask per channel-band slot (`2 * nb_ebands`). -#[allow(clippy::too_many_arguments)] -pub(crate) fn quant_all_bands_stereo( - mode: &CeltMode, - start: usize, - end: usize, - x: &mut [f32], - y: &mut [f32], - pulses: &[i32], - short_blocks: bool, - spread: i32, - tf_res: &[i32], - total_bits_q: i32, - mut balance: i32, - coded_bands: usize, - lm: usize, - dec: &mut EcDec<'_>, - seed: &mut u32, - dual_stereo: i32, - intensity: i32, - disable_inv: bool, - packet_idx: usize, - frame_call_idx: usize, -) -> Vec { - let trace_target = None; - let trace_this_packet = trace_target == Some(packet_idx); - let m = 1usize << lm; - let mut masks = vec![0u8; mode.nb_ebands * 2]; - let mut lowband_offset = 0usize; - let mut update_lowband = true; - let norm_offset = m * mode.e_bands[start] as usize; - let mut dual_stereo_on = dual_stereo != 0; - let mut norm_hist_x = vec![0.0f32; x.len()]; - let mut norm_hist_y = vec![0.0f32; y.len()]; - - for i in start..end { - let tell = dec.tell_frac() as i32; - if i != start { - balance -= tell; - } - let remaining_bits = total_bits_q - tell - 1; - let b = if i < coded_bands { - let curr_balance = balance / ((coded_bands - i).min(3) as i32); - (pulses[i] + curr_balance) - .clamp(0, 16_383) - .min(remaining_bits + 1) - } else { - 0 - }; - let band_start = m * mode.e_bands[i] as usize; - let band_end = m * mode.e_bands[i + 1] as usize; - let n = band_end - band_start; - if i != start && (update_lowband || lowband_offset == 0) { - lowband_offset = i; - } - - let frame_blocks = if short_blocks { m } else { 1 }; - let long_blocks = frame_blocks == 1; - let mut b_blocks = frame_blocks; - let spread_eff = spread.clamp(0, 3); - let tf_change = tf_res[i]; - let mut n_b = n / b_blocks.max(1); - let n_b_initial = n_b; - - if i == start + 1 { - special_hybrid_folding_decode( - mode, - &mut norm_hist_x[norm_offset..], - &mut norm_hist_y[norm_offset..], - start, - m, - dual_stereo_on, - ); - } - - let mut effective_lowband = None::; - let mut x_cm = if b_blocks >= 32 { - u32::MAX - } else { - (1u32 << b_blocks) - 1 - }; - let mut y_cm = x_cm; - if lowband_offset != 0 && (spread != SPREAD_AGGRESSIVE || frame_blocks > 1 || tf_change < 0) - { - let lowband_start = m * mode.e_bands[lowband_offset] as usize; - let effective = lowband_start.saturating_sub(norm_offset + n); - let fold_base = effective + norm_offset; - let fold_limit = fold_base + n; - let mut fold_start = lowband_offset; - while fold_start > 0 { - fold_start -= 1; - if (m * mode.e_bands[fold_start] as usize) <= fold_base { - break; - } - } - let mut fold_end = lowband_offset; - while fold_end < i && (m * mode.e_bands[fold_end] as usize) < fold_limit { - fold_end += 1; - } - x_cm = 0; - y_cm = 0; - for fold_i in fold_start..fold_end { - x_cm |= masks[fold_i * 2] as u32; - y_cm |= masks[fold_i * 2 + 1] as u32; - } - effective_lowband = Some(effective); - } - - if dual_stereo_on && i == intensity as usize { - dual_stereo_on = false; - for j in norm_offset..band_start { - norm_hist_x[j] = 0.5 * (norm_hist_x[j] + norm_hist_y[j]); - } - } - - let fill_theta = x_cm | y_cm; - let mut fill = fill_theta; - let mut fill_x = x_cm; - let mut fill_y = y_cm; - let recombine = tf_change.max(0) as usize; - for _ in 0..recombine { - let lo = (fill & 0xF) as usize; - let hi = ((fill >> 4) & 0xF) as usize; - fill = BIT_INTERLEAVE_TABLE[lo] | (BIT_INTERLEAVE_TABLE[hi] << 2); - - let lox = (fill_x & 0xF) as usize; - let hix = ((fill_x >> 4) & 0xF) as usize; - fill_x = BIT_INTERLEAVE_TABLE[lox] | (BIT_INTERLEAVE_TABLE[hix] << 2); - - let loy = (fill_y & 0xF) as usize; - let hiy = ((fill_y >> 4) & 0xF) as usize; - fill_y = BIT_INTERLEAVE_TABLE[loy] | (BIT_INTERLEAVE_TABLE[hiy] << 2); - } - - b_blocks >>= recombine; - n_b <<= recombine; - let mut tf_for_blocks = tf_change; - let mut time_divide = 0usize; - while (n_b & 1) == 0 && tf_for_blocks < 0 { - if b_blocks < 32 { - fill |= fill << b_blocks; - fill_x |= fill_x << b_blocks; - fill_y |= fill_y << b_blocks; - } else { - fill = u32::MAX; - fill_x = u32::MAX; - fill_y = u32::MAX; - } - b_blocks <<= 1; - n_b >>= 1; - tf_for_blocks += 1; - time_divide += 1; - } - - let lowband_range = effective_lowband.map(|eff| { - let src_start = norm_offset + eff; - (src_start, src_start + n) - }); - if trace_this_packet { - // #region agent log - debug_trace!( - "R pkt{} frame_call_idx={} band {} params: b={} N={} lowband_offset={} update_lowband={} spread_eff={} tf_change={} effective_lowband={} x_cm=0x{:x} y_cm=0x{:x} B={}", - packet_idx, - frame_call_idx, - i, - b, - n, - lowband_offset, - update_lowband, - spread_eff, - tf_change, - effective_lowband.map(|v| v as i32).unwrap_or(-1), - x_cm, - y_cm, - b_blocks - ); - // #endregion - } - let (_x_before, x_after) = x.split_at_mut(band_start); - let (_y_before, y_after) = y.split_at_mut(band_start); - - let lowband_x = - lowband_range.and_then(|(src_start, src_end)| norm_hist_x.get(src_start..src_end)); - let lowband_y = - lowband_range.and_then(|(src_start, src_end)| norm_hist_y.get(src_start..src_end)); - - let mut lowband_x_scratch = None; - let mut lowband_y_scratch = None; - let lowband_x_for_quant = if let Some(lb) = lowband_x { - if recombine > 0 || ((n_b_initial & 1) == 0 && tf_change < 0) || frame_blocks > 1 { - lowband_x_scratch = Some(transform_lowband_for_decode( - lb, - n, - frame_blocks, - recombine, - tf_change, - long_blocks, - )); - lowband_x_scratch.as_deref() - } else { - Some(lb) - } - } else { - None - }; - let lowband_y_for_quant = if let Some(lb) = lowband_y { - if recombine > 0 || ((n_b_initial & 1) == 0 && tf_change < 0) || frame_blocks > 1 { - lowband_y_scratch = Some(transform_lowband_for_decode( - lb, - n, - frame_blocks, - recombine, - tf_change, - long_blocks, - )); - lowband_y_scratch.as_deref() - } else { - Some(lb) - } - } else { - None - }; - - let mut remaining_band_bits = remaining_bits; - let tell_before = dec.tell_frac(); - let (cm_l, cm_r) = if dual_stereo_on { - let cmx_raw = quant_partition_mono( - mode, - &mut x_after[..n], - i, - b / 2, - b_blocks, - lowband_x_for_quant, - lm as i32, - 1.0, - fill_x, - spread_eff, - dec, - seed, - &mut remaining_band_bits, - trace_this_packet, - packet_idx, - ); - let cmy_raw = quant_partition_mono( - mode, - &mut y_after[..n], - i, - b / 2, - b_blocks, - lowband_y_for_quant, - lm as i32, - 1.0, - fill_y, - spread_eff, - dec, - seed, - &mut remaining_band_bits, - trace_this_packet, - packet_idx, - ); - apply_quant_band_post_tf( - &mut x_after[..n], - n, - b_blocks, - recombine, - time_divide, - long_blocks, - ); - apply_quant_band_post_tf( - &mut y_after[..n], - n, - b_blocks, - recombine, - time_divide, - long_blocks, - ); - let cmx = post_tf_collapse_mask(cmx_raw, b_blocks, recombine, time_divide); - let cmy = post_tf_collapse_mask(cmy_raw, b_blocks, recombine, time_divide); - if i + 1 < end { - let scale = (n as f32).sqrt(); - for j in 0..n { - norm_hist_x[band_start + j] = x_after[j] * scale; - norm_hist_y[band_start + j] = y_after[j] * scale; - } - } - (cmx, cmy) - } else { - let mut mid_hist = vec![0.0f32; n]; - if trace_this_packet && (i == 12 || i >= 18) { - // #region agent log - let log_path = std::env::current_dir() - .unwrap_or_else(|_| std::path::PathBuf::from(".")) - .join(".cursor") - .join("debug-bea564.log"); - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(log_path) - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"bea564\",\"runId\":\"run-tv01-tv10-debug1\",\"hypothesisId\":\"H2\",\"location\":\"crates/opus-decoder/src/celt/bands.rs:quant_all_bands_stereo\",\"message\":\"stereo_band_entry\",\"data\":{{\"packet_idx\":{},\"band\":{},\"n\":{},\"b\":{},\"b_blocks\":{},\"frame_blocks\":{},\"remaining_band_bits\":{},\"intensity\":{},\"dual_stereo_on\":{},\"tf_change\":{},\"recombine\":{},\"time_divide\":{},\"fill_theta\":{},\"fill_tf\":{}}},\"timestamp\":{}}}\n", - packet_idx, - i, - n, - b, - b_blocks, - frame_blocks, - remaining_band_bits, - intensity, - dual_stereo_on, - tf_change, - recombine, - time_divide, - fill_theta, - fill, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - let cm = quant_band_stereo_decode( - mode, - &mut x_after[..n], - &mut y_after[..n], - i, - b, - b_blocks, - frame_blocks, - lowband_x_for_quant.as_deref(), - lm as i32, - fill_theta, - spread_eff, - disable_inv, - dec, - seed, - &mut remaining_band_bits, - intensity, - recombine, - time_divide, - long_blocks, - Some(&mut mid_hist), - trace_this_packet, - packet_idx, - ); - if trace_this_packet && (i == 12 || i >= 18) { - // #region agent log - let log_path = std::env::current_dir() - .unwrap_or_else(|_| std::path::PathBuf::from(".")) - .join(".cursor") - .join("debug-bea564.log"); - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(log_path) - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"bea564\",\"runId\":\"run-tv01-tv10-debug1\",\"hypothesisId\":\"H3\",\"location\":\"crates/opus-decoder/src/celt/bands.rs:quant_all_bands_stereo\",\"message\":\"stereo_band_exit\",\"data\":{{\"packet_idx\":{},\"band\":{},\"cm\":{},\"remaining_band_bits\":{}}},\"timestamp\":{}}}\n", - packet_idx, i, cm, remaining_band_bits, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - if i + 1 < end { - norm_hist_x[band_start..band_start + n].copy_from_slice(&mid_hist); - } - (cm, cm) - }; - - let tell_after = dec.tell_frac(); - if trace_this_packet { - debug_trace!( - "pkt{} frame_call_idx={} stereo band {} tell: {}->{} ({}bits) dual_stereo={}", - packet_idx, - frame_call_idx, - i, - tell_before, - tell_after, - tell_after as i32 - tell_before as i32, - dual_stereo_on - ); - // #region agent log - debug_trace!( - "R pkt{} frame_call_idx={} band {} cm_out: x=0x{:x} y=0x{:x}", - packet_idx, - frame_call_idx, - i, - cm_l, - cm_r - ); - // #endregion - } - masks[i * 2] = (cm_l & 0xFF) as u8; - masks[i * 2 + 1] = (cm_r & 0xFF) as u8; - balance += pulses[i] + tell; - update_lowband = b > ((n as i32) << BITRES); - } - masks -} - -/// De-normalize normalized bands to MDCT bins using decoded energies. -/// -/// Params: mode, normalized `x`, output `freq`, decoded `band_loge`, band range, -/// and `silence` flag forcing zero spectrum output. -/// Returns: nothing. -pub(crate) fn denormalise_bands( - mode: &CeltMode, - x: &[f32], - freq: &mut [f32], - band_loge: &[f32], - start: usize, - end: usize, - lm: usize, - silence: bool, -) { - if silence { - freq.fill(0.0); - return; - } - let m = 1usize << lm; - for i in start..end { - let j0 = m * mode.e_bands[i] as usize; - let j1 = m * mode.e_bands[i + 1] as usize; - let lg = band_loge[i] + quant_bands::e_means()[i]; - let g = 2.0f32.powf(lg.max(-32.0)); - for j in j0..j1 { - freq[j] = x[j] * g; - } - } -} diff --git a/vendor/opus-decoder/src/celt/cwrs.rs b/vendor/opus-decoder/src/celt/cwrs.rs deleted file mode 100644 index 7a36366a..00000000 --- a/vendor/opus-decoder/src/celt/cwrs.rs +++ /dev/null @@ -1,120 +0,0 @@ -//! CELT combinatorial PVQ pulse decoding (`cwrs.c`) for decode path. - -use crate::entropy::EcDec; - -/// Result of decoding one CWRS PVQ codeword. -#[derive(Debug, Clone)] -pub(crate) struct PulseDecode { - pub yy: u32, - pub pulses: Vec, - pub nc: u32, - pub index: u32, -} - -/// Compute U(n, k) row up to `k+1` and return V(n, k). -/// -/// Params: `n` dimensions, `k` pulses. -/// Returns: `(u_row, v_nk)` where `u_row[i] = U(n, i)`. -fn ncwrs_urow(n: usize, k: usize) -> (Vec, u32) { - let len = k + 2; - let mut u = vec![0u32; len]; - u[0] = 0; - u[1] = 1; - for (idx, item) in u.iter_mut().enumerate().skip(2) { - *item = ((idx as u32) << 1) - 1; - } - for _ in 2..n { - unext(&mut u[1..], 1); - } - let v = u[k].wrapping_add(u[k + 1]); - (u, v) -} - -/// Advance one recurrence row for U-table generation. -/// -/// Params: mutable row slice and first base value. -/// Returns: nothing. -fn unext(u: &mut [u32], mut u0: u32) { - if u.len() < 2 { - return; - } - for j in 1..u.len() { - let u1 = u[j].wrapping_add(u[j - 1]).wrapping_add(u0); - u[j - 1] = u0; - u0 = u1; - } - let last = u.len() - 1; - u[last] = u0; -} - -/// Step recurrence backwards for one dimension removal. -/// -/// Params: mutable row slice and first base value. -/// Returns: nothing. -fn uprev(u: &mut [u32], mut u0: u32) { - if u.len() < 2 { - return; - } - for j in 1..u.len() { - let u1 = u[j].wrapping_sub(u[j - 1]).wrapping_sub(u0); - u[j - 1] = u0; - u0 = u1; - } - let last = u.len() - 1; - u[last] = u0; -} - -/// Decode CWRS index into signed pulse vector. -/// -/// Params: `n` dimensions, `k` pulses, index `idx`, and mutable U-row scratch. -/// Returns: `(sum_of_squares, decoded_vector)`. -fn cwrsi(n: usize, mut k: usize, mut idx: u32, u: &mut [u32]) -> (u32, Vec) { - let mut yy = 0u32; - let mut y = vec![0i32; n]; - for yj in y.iter_mut().take(n) { - let p = u[k + 1]; - let neg = idx >= p; - if neg { - idx = idx.wrapping_sub(p); - } - let yk = k; - let mut cur = u[k]; - while cur > idx { - k -= 1; - cur = u[k]; - } - idx = idx.wrapping_sub(cur); - let mut val = (yk - k) as i32; - if neg { - val = -val; - } - *yj = val; - yy = yy.wrapping_add((val * val) as u32); - uprev(&mut u[..=k + 1], 0); - } - (yy, y) -} - -/// Decode PVQ pulses from range coder. -/// -/// Params: range decoder, `n` dimensions and `k` pulses. -/// Returns: `(sum_of_squares, decoded_signed_pulses)`. -pub(crate) fn decode_pulses(dec: &mut EcDec<'_>, n: usize, k: usize) -> PulseDecode { - if n == 0 || k == 0 { - return PulseDecode { - yy: 0, - pulses: vec![0; n], - nc: 0, - index: 0, - }; - } - let (mut u, nc) = ncwrs_urow(n, k); - let idx = dec.dec_uint(nc.max(2)); - let (yy, pulses) = cwrsi(n, k, idx, &mut u); - PulseDecode { - yy, - pulses, - nc, - index: idx, - } -} diff --git a/vendor/opus-decoder/src/celt/kiss_fft.rs b/vendor/opus-decoder/src/celt/kiss_fft.rs deleted file mode 100644 index aba34869..00000000 --- a/vendor/opus-decoder/src/celt/kiss_fft.rs +++ /dev/null @@ -1,191 +0,0 @@ -#![allow(dead_code)] - -//! Minimal CELT FFT primitives for decoder path. -//! -//! This module intentionally implements only the inverse transform behavior -//! needed by the decoder. It follows the libopus scaling convention where the -//! inverse FFT is unscaled and the forward path applies `1/N`. - -use core::f32::consts::PI; - -/// Complex number used by CELT FFT/MDCT primitives. -#[derive(Debug, Clone, Copy, PartialEq)] -pub(crate) struct Complex32 { - /// Real component. - pub re: f32, - /// Imaginary component. - pub im: f32, -} - -impl Complex32 { - /// Create a complex value from real and imaginary parts. - /// - /// Params: `re` is the real component, `im` is the imaginary component. - /// Returns: a new `Complex32`. - pub const fn new(re: f32, im: f32) -> Self { - Self { re, im } - } -} - -/// Inverse-only FFT plan for CELT decoder usage. -#[derive(Debug, Clone)] -pub(crate) struct KissFft { - nfft: usize, - inv_twiddles: Vec, -} - -impl KissFft { - /// Build an inverse FFT plan with runtime twiddle generation. - /// - /// Params: `nfft` is the FFT size. - /// Returns: a plan containing precomputed inverse twiddles. - pub fn new(nfft: usize) -> Self { - let mut inv_twiddles = Vec::with_capacity(nfft); - for k in 0..nfft { - let phase = 2.0 * PI * (k as f32) / (nfft as f32); - inv_twiddles.push(Complex32::new(phase.cos(), phase.sin())); - } - Self { nfft, inv_twiddles } - } - - /// Return FFT size configured in this plan. - /// - /// Params: none. - /// Returns: number of points in the transform. - pub fn len(&self) -> usize { - self.nfft - } - - /// Compute unscaled inverse FFT. - /// - /// Params: `input` is frequency-domain complex spectrum, `output` is - /// destination time-domain buffer and must have length `self.len()`. - /// Returns: `Ok(())` on success, otherwise a static validation error. - pub fn ifft(&self, input: &[Complex32], output: &mut [Complex32]) -> Result<(), &'static str> { - if input.len() != self.nfft || output.len() != self.nfft { - return Err("kiss_fft length mismatch"); - } - for out in output.iter_mut() { - *out = Complex32::new(0.0, 0.0); - } - for (n, out) in output.iter_mut().enumerate() { - let mut acc_re = 0.0f32; - let mut acc_im = 0.0f32; - for (k, xk) in input.iter().enumerate() { - let tw = self.inv_twiddles[(k * n) % self.nfft]; - acc_re += xk.re * tw.re - xk.im * tw.im; - acc_im += xk.re * tw.im + xk.im * tw.re; - } - *out = Complex32::new(acc_re, acc_im); - } - Ok(()) - } - - /// Compute unscaled forward FFT. - /// - /// Params: `input` is time-domain complex vector, `output` is destination - /// frequency-domain buffer and must have length `self.len()`. - /// Returns: `Ok(())` on success, otherwise a static validation error. - pub fn fft(&self, input: &[Complex32], output: &mut [Complex32]) -> Result<(), &'static str> { - if input.len() != self.nfft || output.len() != self.nfft { - return Err("kiss_fft length mismatch"); - } - for out in output.iter_mut() { - *out = Complex32::new(0.0, 0.0); - } - for (n, out) in output.iter_mut().enumerate() { - let mut acc_re = 0.0f32; - let mut acc_im = 0.0f32; - for (k, xk) in input.iter().enumerate() { - let tw = self.inv_twiddles[(k * n) % self.nfft]; - // Conjugate inverse twiddle gives forward phase. - acc_re += xk.re * tw.re + xk.im * tw.im; - acc_im += -xk.re * tw.im + xk.im * tw.re; - } - *out = Complex32::new(acc_re, acc_im); - } - Ok(()) - } -} - -/// Compute forward DFT on flat interleaved complex buffers. -/// -/// Params: `input`/`output` are `[re0, im0, re1, im1, ...]` and `n` is the -/// number of complex samples. -/// Returns: nothing; writes the forward transform to `output`. -pub(crate) fn flat_fft_forward(input: &[f32], output: &mut [f32], n: usize) { - assert!(input.len() >= 2 * n && output.len() >= 2 * n); - for k in 0..n { - let mut sum_re = 0.0f64; - let mut sum_im = 0.0f64; - for j in 0..n { - let angle = -2.0 * PI as f64 * (k as f64) * (j as f64) / (n as f64); - let (sin_a, cos_a) = angle.sin_cos(); - let re = input[2 * j] as f64; - let im = input[2 * j + 1] as f64; - sum_re += re * cos_a - im * sin_a; - sum_im += re * sin_a + im * cos_a; - } - output[2 * k] = sum_re as f32; - output[2 * k + 1] = sum_im as f32; - } -} - -#[cfg(test)] -mod tests { - use super::{Complex32, KissFft}; - use core::f32::consts::PI; - - /// Compute libopus-style forward DFT with `1/N` scaling for tests. - /// - /// Params: `input` is time-domain complex vector. - /// Returns: scaled frequency-domain vector. - fn forward_scaled(input: &[Complex32]) -> Vec { - let n = input.len(); - let mut out = vec![Complex32::new(0.0, 0.0); n]; - for (k, yk) in out.iter_mut().enumerate() { - let mut acc_re = 0.0f32; - let mut acc_im = 0.0f32; - for (n_idx, xn) in input.iter().enumerate() { - let phase = -2.0 * PI * (k as f32) * (n_idx as f32) / (n as f32); - let c = phase.cos(); - let s = phase.sin(); - acc_re += xn.re * c - xn.im * s; - acc_im += xn.re * s + xn.im * c; - } - *yk = Complex32::new(acc_re / (n as f32), acc_im / (n as f32)); - } - out - } - - #[test] - fn ifft_roundtrip_matches_input() { - let n = 60usize; - let fft = KissFft::new(n); - let mut input = Vec::with_capacity(n); - for i in 0..n { - let t = i as f32 / n as f32; - input.push(Complex32::new( - (2.0 * PI * 3.0 * t).sin(), - (2.0 * PI * 5.0 * t).cos(), - )); - } - let freq = forward_scaled(&input); - let mut recon = vec![Complex32::new(0.0, 0.0); n]; - fft.ifft(&freq, &mut recon).expect("ifft must succeed"); - for (a, b) in input.iter().zip(recon.iter()) { - assert!( - (a.re - b.re).abs() < 2e-5, - "re mismatch: {} vs {}", - a.re, - b.re - ); - assert!( - (a.im - b.im).abs() < 2e-5, - "im mismatch: {} vs {}", - a.im, - b.im - ); - } - } -} diff --git a/vendor/opus-decoder/src/celt/laplace.rs b/vendor/opus-decoder/src/celt/laplace.rs deleted file mode 100644 index 750d72ff..00000000 --- a/vendor/opus-decoder/src/celt/laplace.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Laplace-distributed symbol decoding used by CELT energy quantization. - -use crate::entropy::EcDec; - -const LAPLACE_LOG_MINP: u32 = 0; -const LAPLACE_MINP: u32 = 1 << LAPLACE_LOG_MINP; -const LAPLACE_NMIN: u32 = 16; - -/// Compute the first decay bucket frequency for Laplace model. -/// -/// Params: `fs0` is probability mass of zero symbol, `decay` is model decay. -/// Returns: frequency for first non-zero bucket. -fn laplace_get_freq1(fs0: u32, decay: u32) -> u32 { - let ft = 32_768u32 - LAPLACE_MINP * (2 * LAPLACE_NMIN) - fs0; - ((ft as u64 * (16_384u32.saturating_sub(decay)) as u64) >> 15) as u32 -} - -/// Decode one integer symbol from CELT Laplace-like distribution. -/// -/// Params: `dec` is range decoder state, `fs` is zero-probability frequency, -/// `decay` is decay parameter. -/// Returns: decoded signed integer delta. -pub(crate) fn ec_laplace_decode(dec: &mut EcDec<'_>, fs: u32, decay: u32) -> i32 { - let mut val = 0i32; - let fm = dec.decode_bin(15); - let mut fl = 0u32; - let mut fs_cur = fs; - if fm >= fs_cur { - val += 1; - fl = fs_cur; - fs_cur = laplace_get_freq1(fs_cur, decay) + LAPLACE_MINP; - while fs_cur > LAPLACE_MINP && fm >= fl + 2 * fs_cur { - fs_cur *= 2; - fl += fs_cur; - fs_cur = (((fs_cur - 2 * LAPLACE_MINP) as u64 * decay as u64) >> 15) as u32; - fs_cur += LAPLACE_MINP; - val += 1; - } - if fs_cur <= LAPLACE_MINP { - let di = ((fm - fl) >> (LAPLACE_LOG_MINP + 1)) as i32; - val += di; - fl += (2 * di as u32) * LAPLACE_MINP; - } - if fm < fl + fs_cur { - val = -val; - } else { - fl += fs_cur; - } - } - let fh = (fl + fs_cur).min(32_768); - dec.update(fl, fh, 32_768); - val -} diff --git a/vendor/opus-decoder/src/celt/mdct.rs b/vendor/opus-decoder/src/celt/mdct.rs deleted file mode 100644 index 73fb3355..00000000 --- a/vendor/opus-decoder/src/celt/mdct.rs +++ /dev/null @@ -1,528 +0,0 @@ -#![allow(dead_code, unused_variables, unused_assignments)] - -//! CELT inverse MDCT primitives. -//! -//! Current scope is a decoder-oriented path for 48 kHz / 20 ms (`LM=3`), with -//! explicit overlap/window processing kept close to libopus flow. - -use crate::celt::kiss_fft::{KissFft, flat_fft_forward}; - -/// Backward MDCT helper for CELT decode path. -#[derive(Debug, Clone)] -pub(crate) struct MdctBackward { - n: usize, - overlap: usize, - fft: KissFft, - trig: Vec, -} - -impl MdctBackward { - /// Create backward MDCT state for fixed frame size. - /// - /// Params: `n` is MDCT size (e.g. 960), `overlap` is overlap length - /// (e.g. 120). - /// Returns: initialized state with runtime trig tables. - pub fn new(n: usize, overlap: usize) -> Self { - let n2 = n / 2; - let mut trig = Vec::with_capacity(n2); - for j in 0..n2 { - let phase = 2.0_f64 * core::f64::consts::PI * (j as f64 + 0.125_f64) / n as f64; - trig.push(phase.cos() as f32); - } - Self { - n, - overlap, - fft: KissFft::new(n / 4), - trig, - } - } - - /// Return configured MDCT size. - /// - /// Params: none. - /// Returns: MDCT size `N` for this instance. - pub fn n(&self) -> usize { - self.n - } - - /// Return internal FFT size used by MDCT. - /// - /// Params: none. - /// Returns: complex FFT size `N/4`. - pub fn fft_len(&self) -> usize { - self.fft.len() - } - - /// Run `clt_mdct_backward`-style transform. - /// - /// Params: `input` contains `N/2` MDCT bins, `window` contains `overlap` - /// samples, `out` has length at least `N/2 + overlap`. - /// Returns: `Ok(())` on success, otherwise a static validation error. - pub fn backward( - &self, - input: &[f32], - window: &[f32], - out: &mut [f32], - ) -> Result<(), &'static str> { - let n = self.n; - let n2 = n >> 1; - let n4 = n >> 2; - let overlap = self.overlap; - let required = n2 + overlap; - if input.len() != n2 || out.len() < required { - return Err("mdct backward length mismatch"); - } - if window.len() != overlap { - return Err("mdct backward window length mismatch"); - } - - // #region agent log - let lm0_trace = false; - if lm0_trace { - let old_sum: f32 = out[..overlap.min(out.len())].iter().map(|x| x.abs()).sum(); - let first8: String = (0..8.min(out.len())) - .map(|i| format!("{:.6}", out[i])) - .collect::>() - .join(","); - let line = format!( - r#"{{"sessionId":"bea564","runId":"lm0_imdct","hypothesisId":"H2","location":"mdct.rs:backward_entry","message":"lm0_old_overlap","data":{{"n":{},"n2":{},"overlap":{},"old_sum":{},"old_first8":[{}]}},"timestamp":{}}}"#, - n, - n2, - overlap, - old_sum, - first8, - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_millis() - ); - let log_path = std::env::current_dir() - .unwrap_or_else(|_| std::path::PathBuf::from(".")) - .join(".cursor") - .join("debug-bea564.log"); - let _ = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(&log_path) - .and_then(|mut f| std::io::Write::write_all(&mut f, (line + "\n").as_bytes())); - } - // #endregion - - let trace_fft = false; - if trace_fft { - debug_trace!( - "mdct input[0]={:.6} input[{}]={:.6} trig[0]={:.6} trig[{}]={:.6}", - input[0], - n2 - 1, - input[n2 - 1], - self.trig[0], - n4, - self.trig[n4] - ); - debug_trace!("backward: self.n={} n4={}", self.n, n4); - if self.n == 1920 && self.trig.len() >= 960 { - debug_trace!( - "trig[0]={:.8} trig[1]={:.8} trig[479]={:.8}", - self.trig[0], - self.trig[1], - self.trig[479] - ); - debug_trace!( - "trig[480]={:.8} trig[481]={:.8} trig[959]={:.8}", - self.trig[480], - self.trig[481], - self.trig[959] - ); - debug_trace!( - "TRIG CHECK: [1]={:.8} [239]={:.8} [240]={:.8} [479]={:.8}", - self.trig[1], - self.trig[239], - self.trig[240], - self.trig[479] - ); - } - } - // Flat interleaved buffer: [re0, im0, re1, im1, ...] - let mut f2 = vec![0.0f32; n2]; - let t = &self.trig; - let impulse_mode = - input.first().copied() == Some(1.0) && input.iter().skip(1).all(|&v| v == 0.0); - for i in 0..n4 { - let xp1 = input[2 * i]; - let xp2 = input[n2 - 1 - 2 * i]; - // Match libopus clt_mdct_backward pre-rotation for float path. - let yr = xp2 * t[i] + xp1 * t[n4 + i]; - let yi = xp1 * t[i] - xp2 * t[n4 + i]; - // Swap slots like libopus FFT-vs-IFFT trick. - f2[2 * i] = yi; - f2[2 * i + 1] = yr; - } - if impulse_mode && f2.len() >= 8 { - debug_trace!( - "IMPULSE f2[0..8]: {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6}", - f2[0], - f2[1], - f2[2], - f2[3], - f2[4], - f2[5], - f2[6], - f2[7] - ); - if self.n == 1920 { - debug_trace!( - "R PRE-FFT[0..8]: {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6}", - f2[0], - f2[1], - f2[2], - f2[3], - f2[4], - f2[5], - f2[6], - f2[7] - ); - } - } - if trace_fft && self.n == 1920 && f2.len() >= 4 { - debug_trace!( - "f2[0..4]: {:.6} {:.6} {:.6} {:.6}", - f2[0], - f2[1], - f2[2], - f2[3] - ); - } - if n == 1920 && n4 >= 2 { - let mut sum_re = 0.0f32; - let mut sum_im = 0.0f32; - for i in 0..n4 { - sum_re += f2[2 * i]; - sum_im += f2[2 * i + 1]; - } - debug_trace!( - "MDCT1920 fft_in[0..2]: ({:.6},{:.6}) ({:.6},{:.6})", - f2[0], - f2[1], - f2[2], - f2[3] - ); - debug_trace!("MDCT1920 fft_in sum: ({:.6},{:.6})", sum_re, sum_im); - } - - let mut f2_out = vec![0.0f32; n2]; - flat_fft_forward(&f2, &mut f2_out, n4); - if impulse_mode && f2_out.len() >= 8 { - debug_trace!( - "IMPULSE f2_out[0..8]: {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6}", - f2_out[0], - f2_out[1], - f2_out[2], - f2_out[3], - f2_out[4], - f2_out[5], - f2_out[6], - f2_out[7] - ); - } - if trace_fft && self.n == 1920 && f2_out.len() >= 4 { - debug_trace!( - "f2_out[0..4]: {:.6} {:.6} {:.6} {:.6}", - f2_out[0], - f2_out[1], - f2_out[2], - f2_out[3] - ); - debug_trace!("f2_out[0..2]: {:.6} {:.6}", f2_out[0], f2_out[1]); - debug_trace!("f2_out[958..960]: {:.6} {:.6}", f2_out[958], f2_out[959]); - debug_trace!( - "post i=0: re={:.6} im={:.6} t0={:.6} t480={:.6}", - f2_out[0], - f2_out[959], - self.trig[0], - self.trig[480] - ); - let sum_re: f64 = (0..n4).map(|j| f2[2 * j] as f64).sum(); - let sum_im: f64 = (0..n4).map(|j| f2[2 * j + 1] as f64).sum(); - debug_trace!( - "VERIFY FFT: input_sum=({:.6},{:.6}) output[0]=({:.6},{:.6})", - sum_re, - sum_im, - f2_out[0], - f2_out[1] - ); - debug_trace!( - "f2_out[last2]: ({:.6},{:.6})", - f2_out[n2 - 2], - f2_out[n2 - 1] - ); - } - if n == 1920 && n4 >= 2 { - debug_trace!( - "MDCT1920 fft_out[0..2]: ({:.6},{:.6}) ({:.6},{:.6})", - f2_out[0], - f2_out[1], - f2_out[2], - f2_out[3] - ); - } - - let ov2 = overlap >> 1; - out[ov2..ov2 + n2].copy_from_slice(&f2_out[..n2]); - // #region agent log - if lm0_trace { - let after_sum: f32 = out[ov2..ov2 + n2.min(out.len().saturating_sub(ov2))] - .iter() - .map(|x| x.abs()) - .sum(); - let after_first8: String = (ov2..(ov2 + 8).min(out.len())) - .map(|i| format!("{:.6}", out[i])) - .collect::>() - .join(","); - let line = format!( - r#"{{"sessionId":"bea564","runId":"lm0_imdct","hypothesisId":"H3","location":"mdct.rs:after_copy","message":"after_copy_from_slice","data":{{"ov2":{},"after_sum":{},"out_60_68":[{}]}},"timestamp":{}}}"#, - ov2, - after_sum, - after_first8, - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_millis() - ); - let log_path = std::env::current_dir() - .unwrap_or_else(|_| std::path::PathBuf::from(".")) - .join(".cursor") - .join("debug-bea564.log"); - let _ = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(&log_path) - .and_then(|mut f| std::io::Write::write_all(&mut f, (line + "\n").as_bytes())); - } - // #endregion - let mut p0 = ov2; - let mut p1 = ov2 + n2 - 2; - for i in 0..((n4 + 1) >> 1) { - if trace_fft && self.n == 1920 && i == 0 { - debug_trace!("[RUST] POST i=0: p0={} p1={}", p0, p1); - debug_trace!( - "[RUST] read: out[{}]={:.6} out[{}]={:.6}", - p0, - out[p0], - p0 + 1, - out[p0 + 1] - ); - debug_trace!( - "[RUST] read: out[{}]={:.6} out[{}]={:.6}", - p1, - out[p1], - p1 + 1, - out[p1 + 1] - ); - debug_trace!( - "[RUST] trig: t[0]={:.8} t[480]={:.8} t[479]={:.8} t[959]={:.8}", - self.trig[0], - self.trig[480], - self.trig[479], - self.trig[959] - ); - } - // We swap real/imag in reads because we use FFT instead of IFFT. - let re = out[p0 + 1]; - let im = out[p0]; - let t0 = t[i]; - let t1 = t[n4 + i]; - let yr = re * t0 + im * t1; - let yi = re * t1 - im * t0; - - // Read yp1 from the original buffer state, like libopus. - let re2 = out[p1 + 1]; - let im2 = out[p1]; - out[p0] = yr; - out[p1 + 1] = yi; - - let t2 = t[n4 - i - 1]; - let t3 = t[n2 - i - 1]; - let yr2 = re2 * t2 + im2 * t3; - let yi2 = re2 * t3 - im2 * t2; - out[p1] = yr2; - out[p0 + 1] = yi2; - if trace_fft && self.n == 1920 && i == 0 { - debug_trace!( - "[RUST] write: out[60]={:.6} out[1019]={:.6} out[1018]={:.6} out[61]={:.6}", - out[60], - out[1019], - out[1018], - out[61] - ); - } - - p0 += 2; - p1 -= 2; - } - if trace_fft && self.n == 1920 && out.len() >= 64 { - debug_trace!( - "post-rot out[60..64]: {:.6} {:.6} {:.6} {:.6}", - out[60], - out[61], - out[62], - out[63] - ); - } - // #region agent log - if lm0_trace { - let post_rot_left: f32 = out[..ov2.min(out.len())].iter().map(|x| x.abs()).sum(); - let post_rot_right: f32 = out[ov2..overlap.min(out.len())] - .iter() - .map(|x| x.abs()) - .sum(); - let right_first8: String = (ov2..(ov2 + 8).min(out.len())) - .map(|i| format!("{:.6}", out[i])) - .collect::>() - .join(","); - let line = format!( - r#"{{"sessionId":"bea564","runId":"lm0_imdct","hypothesisId":"H7","location":"mdct.rs:after_post_rot","message":"before_window","data":{{"post_rot_left_sum":{},"post_rot_right_sum":{},"right_first8":[{}]}},"timestamp":{}}}"#, - post_rot_left, - post_rot_right, - right_first8, - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_millis() - ); - let log_path = std::env::current_dir() - .unwrap_or_else(|_| std::path::PathBuf::from(".")) - .join(".cursor") - .join("debug-bea564.log"); - let _ = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(&log_path) - .and_then(|mut f| std::io::Write::write_all(&mut f, (line + "\n").as_bytes())); - } - // #endregion - - for i in 0..(overlap / 2) { - let x1 = out[overlap - 1 - i]; - let x2 = out[i]; - out[i] = x2 * window[overlap - 1 - i] - x1 * window[i]; - out[overlap - 1 - i] = x2 * window[i] + x1 * window[overlap - 1 - i]; - // #region agent log - if lm0_trace && (i == 0 || i == overlap / 2 - 1) { - let line = format!( - r#"{{"sessionId":"bea564","runId":"lm0_imdct","hypothesisId":"H4","location":"mdct.rs:window_loop","message":"window_iter","data":{{"i":{},"x1":{},"x2":{},"out_i":{},"out_119_i":{}}},"timestamp":{}}}"#, - i, - x1, - x2, - out[i], - out[overlap - 1 - i], - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_millis() - ); - let log_path = std::env::current_dir() - .unwrap_or_else(|_| std::path::PathBuf::from(".")) - .join(".cursor") - .join("debug-bea564.log"); - let _ = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(&log_path) - .and_then(|mut f| std::io::Write::write_all(&mut f, (line + "\n").as_bytes())); - } - // #endregion - } - // #region agent log - if lm0_trace { - let frame_sum: f32 = out[..overlap.min(out.len())].iter().map(|x| x.abs()).sum(); - let frame_first8: String = (0..8.min(out.len())) - .map(|i| format!("{:.6}", out[i])) - .collect::>() - .join(","); - let line = format!( - r#"{{"sessionId":"bea564","runId":"lm0_imdct","hypothesisId":"H5","location":"mdct.rs:backward_exit","message":"lm0_after_window","data":{{"frame_sum":{},"frame_first8":[{}]}},"timestamp":{}}}"#, - frame_sum, - frame_first8, - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_millis() - ); - let log_path = std::env::current_dir() - .unwrap_or_else(|_| std::path::PathBuf::from(".")) - .join(".cursor") - .join("debug-bea564.log"); - let _ = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(&log_path) - .and_then(|mut f| std::io::Write::write_all(&mut f, (line + "\n").as_bytes())); - } - // #endregion - Ok(()) - } - - /// Run direct inverse MDCT that returns raw IMDCT output. - /// - /// Params: `input` contains `N/2` MDCT bins, `out` has length `N`. - /// Returns: `Ok(())` on success, otherwise a static validation error. - pub fn backward_direct( - &self, - input: &[f32], - _window: &[f32], - out: &mut [f32], - ) -> Result<(), &'static str> { - let n = self.n; - let n2 = n / 2; - if input.len() != n2 || out.len() != n { - return Err("mdct backward length mismatch"); - } - - for (i, out_i) in out.iter_mut().enumerate() { - let mut sum = 0.0f64; - for (k, &xk) in input.iter().enumerate() { - let phase = core::f64::consts::PI / (n2 as f64) - * (i as f64 + 0.5 + n2 as f64 / 2.0) - * (k as f64 + 0.5); - sum += xk as f64 * phase.cos(); - } - *out_i = sum as f32; - } - if self.n == 1920 { - let rms = (out.iter().map(|x| x * x).sum::() / out.len() as f32).sqrt(); - debug_trace!( - "raw imdct[0..4]: {:.6} {:.6} {:.6} {:.6}", - out[0], - out[1], - out[2], - out[3] - ); - debug_trace!( - "raw imdct[958..962]: {:.6} {:.6} {:.6} {:.6}", - out[958], - out[959], - out[960], - out[961] - ); - debug_trace!("MDCT1920 raw rms: {:.6}", rms); - } - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::MdctBackward; - - #[test] - fn mdct_backward_validates_lengths() { - let mdct = MdctBackward::new(960, 120); - let input = vec![0.0f32; 480]; - let window = vec![1.0f32; 120]; - let mut out = vec![0.0f32; 960]; - assert!(mdct.backward(&input, &window, &mut out).is_ok()); - assert!(mdct.backward(&input[..479], &window, &mut out).is_err()); - assert!(mdct.backward(&input, &window[..119], &mut out).is_err()); - } -} diff --git a/vendor/opus-decoder/src/celt/mod.rs b/vendor/opus-decoder/src/celt/mod.rs deleted file mode 100644 index b4f02bb2..00000000 --- a/vendor/opus-decoder/src/celt/mod.rs +++ /dev/null @@ -1,3484 +0,0 @@ -//! CELT decoder for Opus CELT-only and hybrid packets. -//! -//! The implementation stays structurally close to libopus to preserve -//! conformance while keeping the decode path self-contained in Rust. -#![allow( - unused_variables, - unused_assignments, - dead_code, - clippy::too_many_arguments, - clippy::needless_borrow, - clippy::get_first, - clippy::needless_range_loop, - clippy::unnecessary_cast -)] - -use crate::Error; -use crate::entropy::EcDec; -use core::sync::atomic::{AtomicUsize, Ordering}; - -mod bands; -mod cwrs; -mod kiss_fft; -mod laplace; -mod mdct; -mod modes; -mod quant_bands; -mod rate; -mod vq; - -const OVERLAP_48K_20MS: usize = 120; -const NBANDS_48K_20MS: usize = 21; -const LOG_ENERGY_FLOOR_DB: f32 = -28.0; -const BITRES: i32 = 3; -const COMBFILTER_MINPERIOD: usize = 15; -const DECODE_BUFFER_SIZE: usize = 2048; -const COMB_FILTER_GAINS: [[f32; 3]; 3] = [ - [0.306_640_62, 0.217_041_02, 0.129_638_67], - [0.463_867_2, 0.268_066_4, 0.0], - [0.799_804_7, 0.100_097_66, 0.0], -]; -const TRIM_ICDF: [u8; 11] = [126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0]; -const SPREAD_ICDF: [u8; 4] = [25, 23, 2, 0]; -const TAPSET_ICDF: [u8; 3] = [2, 1, 0]; -const TF_SELECT_TABLE: [[i8; 8]; 4] = [ - [0, -1, 0, -1, 0, -1, 0, -1], - [0, -1, 0, -2, 1, 0, 1, -1], - [0, -2, 0, -3, 2, 0, 1, -1], - [0, -2, 0, -3, 3, 0, 1, -1], -]; -static TRACE_PACKET_CALL_IDX: AtomicUsize = AtomicUsize::new(0); - -/// Result of decoding one CELT frame. -#[derive(Debug, Clone, Copy)] -pub(crate) struct CeltFrameDecode { - /// Number of decoded samples per channel. - pub samples_per_channel: usize, - /// Indicates transient frame path usage in CELT. - pub is_transient: bool, -} - -#[derive(Debug, Clone)] -pub(crate) struct CeltDecoder { - fs_hz: u32, - channels: u8, - start_band: usize, - mode: &'static modes::CeltMode, - mdct: mdct::MdctBackward, - mdct_480: mdct::MdctBackward, - mdct_240: mdct::MdctBackward, - mdct_short: mdct::MdctBackward, - decode_mem: Vec, - decode_mem_right: Vec, - prev_energy: Vec, - old_log_energy: Vec, - old_log_energy2: Vec, - background_log_energy: Vec, - deemph_mem: Vec, - end_band_override: Option, - postfilter_period: i32, - postfilter_period_old: i32, - postfilter_gain: f32, - postfilter_gain_old: f32, - postfilter_tapset: usize, - postfilter_tapset_old: usize, - final_range: u32, - rng_seed: u32, - last_split_count: usize, - loss_count: u32, -} - -impl CeltDecoder { - /// Create CELT decoder state. - /// - /// Params: `fs_hz` is output sample rate and `channels` is output channels. - /// Returns: initialized CELT decoder state. - pub fn new(fs_hz: u32, channels: u8) -> Self { - let mode = modes::mode48000_960_120(); - let state_ch = 2usize; - Self { - fs_hz, - channels, - start_band: 0, - mode, - mdct: mdct::MdctBackward::new(1920, mode.overlap), - mdct_480: mdct::MdctBackward::new(960, mode.overlap), - mdct_240: mdct::MdctBackward::new(480, mode.overlap), - mdct_short: mdct::MdctBackward::new(240, mode.overlap), - decode_mem: vec![0.0; DECODE_BUFFER_SIZE + mode.overlap], - decode_mem_right: vec![0.0; DECODE_BUFFER_SIZE + mode.overlap], - prev_energy: vec![0.0; NBANDS_48K_20MS * state_ch], - old_log_energy: vec![LOG_ENERGY_FLOOR_DB; NBANDS_48K_20MS * state_ch], - old_log_energy2: vec![LOG_ENERGY_FLOOR_DB; NBANDS_48K_20MS * state_ch], - background_log_energy: vec![0.0; NBANDS_48K_20MS * state_ch], - deemph_mem: vec![0.0; state_ch], - end_band_override: None, - postfilter_period: 0, - postfilter_period_old: 0, - postfilter_gain: 0.0, - postfilter_gain_old: 0.0, - postfilter_tapset: 0, - postfilter_tapset_old: 0, - final_range: 0, - rng_seed: 0, - last_split_count: 0, - loss_count: 0, - } - } - - /// Reset decoder state between streams. - /// - /// Params: none. - /// Returns: nothing. - pub fn reset(&mut self) { - self.decode_mem.fill(0.0); - self.decode_mem_right.fill(0.0); - self.prev_energy.fill(0.0); - self.old_log_energy.fill(LOG_ENERGY_FLOOR_DB); - self.old_log_energy2.fill(LOG_ENERGY_FLOOR_DB); - self.background_log_energy.fill(0.0); - self.deemph_mem.fill(0.0); - self.end_band_override = None; - self.postfilter_period = 0; - self.postfilter_period_old = 0; - self.postfilter_gain = 0.0; - self.postfilter_gain_old = 0.0; - self.postfilter_tapset = 0; - self.postfilter_tapset_old = 0; - self.final_range = 0; - self.rng_seed = 0; - self.last_split_count = 0; - self.start_band = 0; - self.loss_count = 0; - } - - /// Clear CELT PLC loss history before a good decode. - /// - /// Params: none. - /// Returns: nothing. - pub fn reset_loss_count(&mut self) { - self.loss_count = 0; - } - - /// Return the CELT output decimation factor for the configured sample rate. - /// - /// Params: none. - /// Returns: integer 48 kHz downsample factor for the public output rate. - fn downsample_factor(&self) -> Result { - match self.fs_hz { - 48_000 => Ok(1), - 24_000 => Ok(2), - 16_000 => Ok(3), - 12_000 => Ok(4), - 8_000 => Ok(6), - _ => Err(Error::NotImplemented), - } - } - - /// Convert a 48 kHz CELT frame size to output samples per channel. - /// - /// Params: internal `frame_size_48k`. - /// Returns: per-channel sample count at the configured public output rate. - fn output_frame_size(&self, frame_size_48k: usize) -> Result { - let downsample = self.downsample_factor()?; - if frame_size_48k % downsample != 0 { - return Err(Error::NotImplemented); - } - Ok(frame_size_48k / downsample) - } - - /// Write one deemphasized CELT channel into the interleaved i16 output buffer. - /// - /// Params: mutable interleaved `out`, per-channel synthesized `ch_synth`, - /// channel index `ch`, and additive flag `accum`. - /// Returns: `Ok(())` on success or `Error::NotImplemented` for unsupported - /// output rates. - fn write_output_channel_i16( - &self, - out: &mut [i16], - ch_synth: &[f32], - ch: usize, - accum: bool, - ) -> Result<(), Error> { - let downsample = self.downsample_factor()?; - let output_samples = self.output_frame_size(ch_synth.len())?; - for i in 0..output_samples { - let sample = Self::float_to_i16(ch_synth[i * downsample]); - let out_idx = i * self.channels as usize + ch; - out[out_idx] = if accum { - out[out_idx].saturating_add(sample) - } else { - sample - }; - } - Ok(()) - } - - /// Derive the highest active CELT band from saved energy history. - /// - /// Params: active output `channels`. - /// Returns: exclusive upper band index for PLC shaping. - fn plc_end_band(&self, channels: usize) -> usize { - let active_channels = channels.clamp(1, 2); - let nb_ebands = self.mode.nb_ebands; - for band in (0..nb_ebands).rev() { - for ch in 0..active_channels { - if self.prev_energy[ch * nb_ebands + band] > LOG_ENERGY_FLOOR_DB { - return band + 1; - } - } - } - 0 - } - - /// Called when a CELT packet is lost (packet=[] or fec path). - /// - /// Params: per-channel `frame_size` and output `channels`. - /// Returns: interleaved concealed floating PCM. - pub fn decode_lost(&mut self, frame_size: usize, channels: usize) -> Vec { - let downsample = match self.downsample_factor() { - Ok(value) => value, - Err(_) => return Vec::new(), - }; - let active_channels = channels.clamp(1, self.channels as usize).min(2); - let mut pcm = vec![0.0f32; frame_size * active_channels]; - let Some(frame_size_48k) = frame_size.checked_mul(downsample) else { - return pcm; - }; - let lm = match frame_size_48k { - 120 => 0, - 240 => 1, - 480 => 2, - 960 => 3, - _ => return pcm, - }; - - let nb_ebands = self.mode.nb_ebands; - let end = self.plc_end_band(active_channels); - if end == 0 { - self.loss_count = self.loss_count.saturating_add(1); - return pcm; - } - - let mut left_energy = self.prev_energy[..nb_ebands].to_vec(); - let mut right_energy = if active_channels == 2 { - self.prev_energy[nb_ebands..2 * nb_ebands].to_vec() - } else { - left_energy.clone() - }; - if self.loss_count > 0 { - for band in 0..end { - left_energy[band] = (left_energy[band] - 6.0).max(self.background_log_energy[band]); - if active_channels == 2 { - right_energy[band] = (right_energy[band] - 6.0) - .max(self.background_log_energy[nb_ebands + band]); - } - } - } - for band in end..nb_ebands { - left_energy[band] = LOG_ENERGY_FLOOR_DB; - right_energy[band] = LOG_ENERGY_FLOOR_DB; - } - - let mut mdct_left = vec![0.0f32; frame_size_48k]; - for band in 0..end { - let start = (self.mode.e_bands[band] as usize) << lm; - let band_len = - (self.mode.e_bands[band + 1] as usize - self.mode.e_bands[band] as usize) << lm; - for value in &mut mdct_left[start..start + band_len] { - self.rng_seed = bands::celt_lcg_rand(self.rng_seed); - *value = ((self.rng_seed >> 16) as i16) as f32; - } - vq::renormalise_vector(&mut mdct_left[start..start + band_len], 1.0); - } - let mut denorm_left = vec![0.0f32; frame_size_48k]; - bands::denormalise_bands( - self.mode, - &mdct_left, - &mut denorm_left, - &left_energy, - 0, - end, - lm, - false, - ); - if Self::synthesise_channel_to_mem( - &denorm_left, - frame_size_48k, - lm, - false, - self.mode.window, - self.mode.overlap, - &self.mdct, - &self.mdct_480, - &self.mdct_240, - &self.mdct_short, - &mut self.decode_mem, - ) - .is_err() - { - return pcm; - } - - if active_channels == 2 { - let mut mdct_right = vec![0.0f32; frame_size_48k]; - for band in 0..end { - let start = (self.mode.e_bands[band] as usize) << lm; - let band_len = - (self.mode.e_bands[band + 1] as usize - self.mode.e_bands[band] as usize) << lm; - for value in &mut mdct_right[start..start + band_len] { - self.rng_seed = bands::celt_lcg_rand(self.rng_seed); - *value = ((self.rng_seed >> 16) as i16) as f32; - } - vq::renormalise_vector(&mut mdct_right[start..start + band_len], 1.0); - } - let mut denorm_right = vec![0.0f32; frame_size_48k]; - bands::denormalise_bands( - self.mode, - &mdct_right, - &mut denorm_right, - &right_energy, - 0, - end, - lm, - false, - ); - if Self::synthesise_channel_to_mem( - &denorm_right, - frame_size_48k, - lm, - false, - self.mode.window, - self.mode.overlap, - &self.mdct, - &self.mdct_480, - &self.mdct_240, - &self.mdct_short, - &mut self.decode_mem_right, - ) - .is_err() - { - return pcm; - } - } - - self.postfilter_period = 0; - self.postfilter_period_old = 0; - self.postfilter_gain = 0.0; - self.postfilter_gain_old = 0.0; - self.postfilter_tapset = 0; - self.postfilter_tapset_old = 0; - self.old_log_energy2[..nb_ebands].copy_from_slice(&self.old_log_energy[..nb_ebands]); - self.old_log_energy[..nb_ebands].copy_from_slice(&left_energy); - self.prev_energy[..nb_ebands].copy_from_slice(&left_energy); - self.old_log_energy2[nb_ebands..2 * nb_ebands] - .copy_from_slice(&self.old_log_energy[nb_ebands..2 * nb_ebands]); - if active_channels == 2 { - self.old_log_energy[nb_ebands..2 * nb_ebands].copy_from_slice(&right_energy); - self.prev_energy[nb_ebands..2 * nb_ebands].copy_from_slice(&right_energy); - } else { - self.old_log_energy[nb_ebands..2 * nb_ebands].copy_from_slice(&left_energy); - self.prev_energy[nb_ebands..2 * nb_ebands].copy_from_slice(&left_energy); - } - - let out_start = Self::out_start(frame_size_48k); - let mut left = self.decode_mem[out_start..out_start + frame_size_48k].to_vec(); - self.apply_deemph(0, &mut left); - for i in 0..frame_size { - pcm[i * active_channels] = left[i * downsample]; - } - if active_channels == 2 { - let mut right = self.decode_mem_right[out_start..out_start + frame_size_48k].to_vec(); - self.apply_deemph(1, &mut right); - for i in 0..frame_size { - pcm[i * 2 + 1] = right[i * downsample]; - } - } - - self.final_range = 0; - self.last_split_count = 0; - self.loss_count = self.loss_count.saturating_add(1); - pcm - } - - /// Return CELT range coder final state. - /// - /// Params: none. - /// Returns: range coder final value for last decoded frame. - pub fn final_range(&self) -> u32 { - self.final_range - } - - /// Return the MDCT overlap window used for transition fades. - /// - /// Params: none. - /// Returns: immutable CELT synthesis window coefficients. - pub(crate) fn window(&self) -> &[f32] { - self.mode.window - } - - /// Return deemphasis memory for the selected channel. - /// - /// Params: `ch` channel index. - /// Returns: deemphasis memory value for that channel. - pub fn deemph_mem(&self, ch: usize) -> f32 { - self.deemph_mem[ch] - } - - /// Return absolute sum of the left decode memory. - /// - /// Params: none. - /// Returns: sum of absolute sample values in the left decode buffer. - pub fn decode_mem_abs_sum(&self) -> f32 { - self.decode_mem.iter().map(|x| x.abs()).sum() - } - - /// Return absolute sum of the right decode memory. - /// - /// Params: none. - /// Returns: sum of absolute sample values in the right decode buffer. - pub fn decode_mem_right_abs_sum(&self) -> f32 { - self.decode_mem_right.iter().map(|x| x.abs()).sum() - } - - /// Return leading CELT band energies for debug comparisons. - /// - /// Params: `count` is the number of entries to copy. - /// Returns: copied prefix of `prev_energy` with at most `count` values. - pub fn prev_energy_head(&self, count: usize) -> Vec { - self.prev_energy[..self.prev_energy.len().min(count)].to_vec() - } - - /// Return split count captured for last decoded frame. - /// - /// Params: none. - /// Returns: recursive split count. - pub fn last_split_count(&self) -> usize { - self.last_split_count - } - - /// Override the lowest coded band for the next CELT decode. - /// - /// Params: `band` lower band index to decode from. - /// Returns: nothing; the index is clamped to the valid mode range. - pub fn set_start_band(&mut self, band: usize) { - self.start_band = band.min(self.mode.nb_ebands.saturating_sub(1)); - } - - /// Override the highest coded band for the next CELT decode. - /// - /// Params: `band` exclusive upper band index. - /// Returns: nothing; the index is clamped to the valid mode range. - pub fn set_end_band(&mut self, band: usize) { - self.end_band_override = Some(band.clamp(1, self.mode.nb_ebands)); - } - - /// Clear any explicit CELT end-band override. - /// - /// Params: none. - /// Returns: nothing; future decodes use config-derived band end. - pub fn clear_end_band(&mut self) { - self.end_band_override = None; - } - - /// Return the currently configured CELT start band. - /// - /// Params: none. - /// Returns: active lower coded band index. - pub(crate) fn debug_start_band(&self) -> usize { - self.start_band - } - - /// Return the CELT end band implied by a TOC config. - /// - /// Params: packet `config`. - /// Returns: upper coded band index for the current mode. - pub(crate) fn debug_end_band(&self, config: u8) -> usize { - self.end_band_override - .unwrap_or_else(|| bandwidth_end(config)) - } - - /// Check whether packet should emit targeted debug logs. - /// - /// Params: `packet_idx` absolute packet index in current test run. - /// Returns: true when packet is in onset/hotspot probe set. - fn should_trace_debug_packet(packet_idx: usize) -> bool { - let _ = packet_idx; - false - } - - /// Append one NDJSON debug entry to the session log. - /// - /// Params: `run_id` run label, `hypothesis_id` tested hypothesis, `location` - /// source marker, `message` event name, and `data_json` object string. - /// Returns: nothing. - fn append_debug_log( - run_id: &str, - hypothesis_id: &str, - location: &str, - message: &str, - data_json: &str, - ) { - let _ = (run_id, hypothesis_id, location, message, data_json); - } - - /// Append one NDJSON debug entry to the active runtime debug session. - /// - /// Params: run metadata and JSON payload string. - /// Returns: nothing. - fn append_runtime_debug_log( - run_id: &str, - hypothesis_id: &str, - location: &str, - message: &str, - data_json: &str, - ) { - let _ = (run_id, hypothesis_id, location, message, data_json); - } - - /// Apply first-order deemphasis to one channel buffer in-place. - /// - /// Params: `ch` is channel index, `samples` are per-channel samples. - /// Returns: nothing. - fn apply_deemph(&mut self, ch: usize, samples: &mut [f32]) { - let coef = self.mode.preemph[0]; - let mut mem = self.deemph_mem[ch]; - for x in samples.iter_mut() { - // libopus recurrence: - // tmp = x + m; m = coef * tmp; output = tmp - let tmp = *x + mem; - mem = coef * tmp; - *x = tmp; - } - self.deemph_mem[ch] = mem; - } - - /// Apply CELT pitch comb postfilter in-place on decoder memory. - /// - /// Params: `buf` is decode memory, `start` is frame start in `buf`, `t0`/`t1` - /// are old/new periods, `n` is frame segment length, `g0`/`g1` are old/new - /// gains, `tapset0`/`tapset1` select tap weights, `window` is overlap window, - /// `overlap` is overlap length. - /// Returns: nothing. - fn comb_filter_in_place( - buf: &mut [f32], - start: usize, - t0: usize, - t1: usize, - n: usize, - g0: f32, - g1: f32, - tapset0: usize, - tapset1: usize, - window: &[f32], - overlap: usize, - ) { - if n == 0 || (g0 == 0.0 && g1 == 0.0) { - return; - } - let period0 = t0.max(COMBFILTER_MINPERIOD); - let period1 = t1.max(COMBFILTER_MINPERIOD); - let ts0 = tapset0.min(2); - let ts1 = tapset1.min(2); - let g00 = g0 * COMB_FILTER_GAINS[ts0][0]; - let g01 = g0 * COMB_FILTER_GAINS[ts0][1]; - let g02 = g0 * COMB_FILTER_GAINS[ts0][2]; - let g10 = g1 * COMB_FILTER_GAINS[ts1][0]; - let g11 = g1 * COMB_FILTER_GAINS[ts1][1]; - let g12 = g1 * COMB_FILTER_GAINS[ts1][2]; - let mut overlap_len = overlap.min(n); - if g0 == g1 && period0 == period1 && ts0 == ts1 { - overlap_len = 0; - } - - let mut x1 = buf[start - period1 + 1]; - let mut x2 = buf[start - period1]; - let mut x3 = buf[start - period1 - 1]; - let mut x4 = buf[start - period1 - 2]; - - for i in 0..overlap_len { - let x0 = buf[start + i - period1 + 2]; - let f = window[i] * window[i]; - let one_minus_f = 1.0 - f; - let mut y = buf[start + i]; - y += (one_minus_f * g00) * buf[start + i - period0]; - y += - (one_minus_f * g01) * (buf[start + i - period0 + 1] + buf[start + i - period0 - 1]); - y += - (one_minus_f * g02) * (buf[start + i - period0 + 2] + buf[start + i - period0 - 2]); - y += (f * g10) * x2; - y += (f * g11) * (x1 + x3); - y += (f * g12) * (x0 + x4); - buf[start + i] = y; - x4 = x3; - x3 = x2; - x2 = x1; - x1 = x0; - } - - if g1 == 0.0 { - return; - } - for i in overlap_len..n { - let x0 = buf[start + i - period1 + 2]; - let y = buf[start + i] + g10 * x2 + g11 * (x1 + x3) + g12 * (x0 + x4); - buf[start + i] = y; - x4 = x3; - x3 = x2; - x2 = x1; - x1 = x0; - } - } - - /// Convert one floating sample to i16 with saturation. - /// - /// Params: `x` is floating PCM sample. - /// Returns: saturated i16 PCM. - fn float_to_i16(x: f32) -> i16 { - let v = x.round().clamp(i16::MIN as f32, i16::MAX as f32); - v as i16 - } - - /// Return frame output start offset inside `decode_mem`. - /// - /// Params: `frame_size_48k` is frame size in 48 kHz samples. - /// Returns: start index for current synthesis output in `decode_mem`. - fn out_start(frame_size_48k: usize) -> usize { - DECODE_BUFFER_SIZE - frame_size_48k - } - - /// Return overlap tail slice stored in unified decode memory. - /// - /// Params: none. - /// Returns: immutable overlap tail `[DECODE_BUFFER_SIZE..DECODE_BUFFER_SIZE+overlap)`. - fn overlap_tail(&self) -> &[f32] { - let start = DECODE_BUFFER_SIZE; - let end = start + self.mode.overlap; - &self.decode_mem[start..end] - } - - /// Run CELT synthesis for one channel into the provided decode-state buffer. - /// - /// Params: denormalized spectrum, frame shape context and mutable channel state. - /// Returns: `Ok(())` on success, `Error::NotImplemented` for unsupported sizes. - fn synthesise_channel_to_mem( - denorm: &[f32], - frame_size_48k: usize, - lm: usize, - is_transient: bool, - window: &[f32], - overlap: usize, - mdct: &mdct::MdctBackward, - mdct_480: &mdct::MdctBackward, - mdct_240: &mdct::MdctBackward, - mdct_short: &mdct::MdctBackward, - decode_mem: &mut [f32], - ) -> Result<(), Error> { - let decode_len = DECODE_BUFFER_SIZE + overlap; - decode_mem.copy_within(frame_size_48k..decode_len, 0); - let out_start = Self::out_start(frame_size_48k); - if is_transient { - let m_blocks = 1usize << lm; - let short_len = frame_size_48k / m_blocks; - let mut short_coeffs = vec![0.0f32; short_len]; - for b in 0..m_blocks { - let out_offset = out_start + b * short_len; - for j in 0..short_len { - short_coeffs[j] = denorm[j * m_blocks + b]; - } - mdct_short - .backward( - &short_coeffs, - window, - &mut decode_mem[out_offset..out_offset + short_len + overlap], - ) - .map_err(|_| Error::NotImplemented)?; - } - } else { - let mdct_impl = match frame_size_48k { - 120 => mdct_short, - 240 => mdct_240, - 480 => mdct_480, - 960 => mdct, - _ => return Err(Error::NotImplemented), - }; - mdct_impl - .backward( - denorm, - window, - &mut decode_mem[out_start..out_start + frame_size_48k + overlap], - ) - .map_err(|_| Error::NotImplemented)?; - } - Ok(()) - } - - /// Decode a single CELT frame. - /// - /// `frame_size_48k` is samples-per-channel at 48 kHz (120/240/480/960). - pub fn decode_frame( - &mut self, - frame: &[u8], - frame_size_48k: usize, - config: u8, - packet_channels: u8, - parent_packet_idx: usize, - out: &mut [i16], - ) -> Result { - let mut ec = EcDec::new(frame); - self.decode_frame_with_ec( - frame, - &mut ec, - frame_size_48k, - config, - packet_channels, - parent_packet_idx, - out, - false, - ) - } - - /// Decode a single CELT frame using a shared entropy decoder. - /// - /// Params: source `frame`, shared mutable `ec`, `frame_size_48k`, TOC `config`, - /// coded `packet_channels`, parent `packet_idx`, mutable `out`, and `accum` - /// which adds decoded PCM on top of existing samples for hybrid mode. - /// Returns: decoded CELT frame metadata. - pub(crate) fn decode_frame_with_ec( - &mut self, - frame: &[u8], - mut ec: &mut EcDec<'_>, - frame_size_48k: usize, - config: u8, - packet_channels: u8, - parent_packet_idx: usize, - out: &mut [i16], - accum: bool, - ) -> Result { - let frame_call_idx = TRACE_PACKET_CALL_IDX.fetch_add(1, Ordering::SeqCst); - let _ = parent_packet_idx; - let packet_idx = usize::MAX; - let output_samples = self.output_frame_size(frame_size_48k)?; - if !matches!(self.channels, 1 | 2) { - return Err(Error::NotImplemented); - } - if !matches!(frame_size_48k, 120 | 240 | 480 | 960) { - return Err(Error::NotImplemented); - } - let needed = output_samples * self.channels as usize; - if out.len() < needed { - return Err(Error::OutputTooSmall { - needed, - got: out.len(), - }); - } - - let trace_this_packet = false; - let debug_packet = false; - let coded_channels = packet_channels.clamp(1, 2) as usize; - if debug_packet { - let overlap_abs: f32 = self.overlap_tail().iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"frame_size_48k\":{},\"config\":{},\"coded_channels\":{},\"overlap_abs_in\":{},\"deemph_mem0_in\":{},\"pf_old_period\":{},\"pf_old_gain\":{},\"pf_old_tapset\":{},\"pf_cur_period\":{},\"pf_cur_gain\":{},\"pf_cur_tapset\":{}}}", - packet_idx, - frame_size_48k, - config, - coded_channels, - overlap_abs, - self.deemph_mem[0], - self.postfilter_period_old, - self.postfilter_gain_old, - self.postfilter_tapset_old, - self.postfilter_period, - self.postfilter_gain, - self.postfilter_tapset - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H1", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "state_in", - &data, - ); - // #endregion - } - if packet_idx == 6 { - let overlap_tail = self.overlap_tail(); - let ovl_abs_sum: f32 = overlap_tail.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"entry\",\"overlap_abs_sum\":{},\"overlap_first4\":[{:.9},{:.9},{:.9},{:.9}],\"pf_old_period\":{},\"pf_old_gain\":{},\"pf_old_tapset\":{},\"pf_cur_period\":{},\"pf_cur_gain\":{},\"pf_cur_tapset\":{}}}", - packet_idx, - ovl_abs_sum, - overlap_tail.first().copied().unwrap_or(0.0), - overlap_tail.get(1).copied().unwrap_or(0.0), - overlap_tail.get(2).copied().unwrap_or(0.0), - overlap_tail.get(3).copied().unwrap_or(0.0), - self.postfilter_period_old, - self.postfilter_gain_old, - self.postfilter_tapset_old, - self.postfilter_period, - self.postfilter_gain, - self.postfilter_tapset - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-overlap-entry-v1", - "H61", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_overlap_entry", - &data, - ); - // #endregion - } - if coded_channels == 1 { - let nb = self.mode.nb_ebands; - for i in 0..nb { - self.prev_energy[i] = self.prev_energy[i].max(self.prev_energy[nb + i]); - } - } - let start = self.start_band.min(self.mode.nb_ebands.saturating_sub(1)); - let end = self - .end_band_override - .unwrap_or_else(|| bandwidth_end(config)); - let active_len = ec.storage(); - if trace_this_packet { - debug_trace!( - "R pkt{} config={} end={} coded_channels={} output_channels={} frame_bytes={} total_bits={}", - packet_idx, - config, - end, - coded_channels, - self.channels, - active_len, - active_len as i32 * 8 - ); - debug_trace!("R pkt{} frame_call_idx={}", packet_idx, frame_call_idx); - if self.channels == 2 { - debug_trace!( - "R pkt{} deemph_mem_in: ch0={:.6} ch1={:.6}", - packet_idx, - self.deemph_mem[0], - self.deemph_mem[1] - ); - } - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-stereo-probe\",\"hypothesisId\":\"H4\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:184\",\"message\":\"target_pkt_channels_and_config\",\"data\":{{\"packet_idx\":{},\"config\":{},\"end\":{},\"coded_channels\":{},\"output_channels\":{}}},\"timestamp\":{}}}\n", - packet_idx, config, end, coded_channels, self.channels, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - let total_bits = (active_len * 8) as i32; - let trace_pkt38_stage = |_stage: &str, _ec: &EcDec<'_>| {}; - trace_pkt38_stage("entry", &ec); - let mut tell = ec.tell(); - let silence_flag = if tell >= total_bits { - true - } else if tell == 1 { - ec.dec_bit_logp(15) - } else { - false - }; - if silence_flag { - tell = total_bits; - } - if trace_this_packet { - debug_trace!( - "R pkt{} [after_silence_flag] tell={}", - packet_idx, - ec.tell_frac() - ); - } - trace_pkt38_stage("silence", &ec); - - let mut postfilter_pitch = 0i32; - let mut postfilter_qg = -1i32; - let mut postfilter_tapset = 0i32; - let mut postfilter_gain = 0.0f32; - if start == 0 && tell + 16 <= total_bits && ec.dec_bit_logp(1) { - let octave = ec.dec_uint(6) as i32; - postfilter_pitch = ((16 << octave) + ec.dec_bits((4 + octave) as u32) as i32) - 1; - postfilter_qg = ec.dec_bits(3) as i32; - if ec.tell() + 2 <= total_bits { - postfilter_tapset = ec.dec_icdf(&TAPSET_ICDF, 2) as i32; - } - postfilter_gain = 0.09375 * (postfilter_qg + 1) as f32; - } - if trace_this_packet { - debug_trace!( - "R pkt{} postfilter_active={} pitch={} qg={} tapset={}", - packet_idx, - postfilter_pitch > 0, - postfilter_pitch, - postfilter_qg, - postfilter_tapset - ); - } - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-postfilter-overlap-scan\",\"hypothesisId\":\"H10\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:227\",\"message\":\"postfilter_decoded\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"postfilter_active\":{},\"postfilter_pitch\":{},\"postfilter_qg\":{},\"postfilter_tapset\":{}}},\"timestamp\":{}}}\n", - packet_idx, - coded_channels, - postfilter_pitch > 0, - postfilter_pitch, - postfilter_qg, - postfilter_tapset, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - if postfilter_pitch > 0 { - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-postfilter-overlap-scan\",\"hypothesisId\":\"H11\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:249\",\"message\":\"postfilter_active_but_not_applied_in_rust\",\"data\":{{\"packet_idx\":{},\"postfilter_pitch\":{},\"postfilter_qg\":{},\"postfilter_tapset\":{},\"comb_filter_applied\":false}},\"timestamp\":{}}}\n", - packet_idx, postfilter_pitch, postfilter_qg, postfilter_tapset, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - tell = ec.tell(); - if trace_this_packet { - debug_trace!( - "R pkt{} [after_postfilter] tell={}", - packet_idx, - ec.tell_frac() - ); - } - trace_pkt38_stage("postfilt", &ec); - let lm = match frame_size_48k { - 120 => 0, - 240 => 1, - 480 => 2, - 960 => 3, - _ => unreachable!(), - }; - let is_transient = if lm > 0 && tell + 3 <= total_bits { - ec.dec_bit_logp(3) - } else { - false - }; - if packet_idx == 905 { - debug_trace!( - "R pkt905 frame_size_48k={} lm={} is_transient={}", - frame_size_48k, - lm, - is_transient - ); - } - if trace_this_packet { - debug_trace!( - "R pkt{} [after_transient] tell={}", - packet_idx, - ec.tell_frac() - ); - } - trace_pkt38_stage("transient", &ec); - tell = ec.tell(); - let intra_ener = tell + 3 <= total_bits && ec.dec_bit_logp(3); - if trace_this_packet { - debug_trace!( - "R pkt{} [after_intra_ener] tell={}", - packet_idx, - ec.tell_frac() - ); - } - trace_pkt38_stage("intra", &ec); - if trace_this_packet { - debug_trace!( - "R pkt{} prev_energy_all: {:?}", - packet_idx, - &self.prev_energy[..self.mode.nb_ebands] - ); - debug_trace!( - "R pkt{} old_log_energy_all: {:?}", - packet_idx, - &self.old_log_energy[..self.mode.nb_ebands] - ); - debug_trace!( - "R pkt{} old_log_energy2_all: {:?}", - packet_idx, - &self.old_log_energy2[..self.mode.nb_ebands] - ); - } - - quant_bands::unquant_coarse_energy( - self.mode, - start, - end, - &mut self.prev_energy, - intra_ener, - &mut ec, - coded_channels, - lm, - total_bits, - ); - if trace_this_packet { - debug_trace!( - "R pkt{} [after_coarse_energy] tell={}", - packet_idx, - ec.tell_frac() - ); - } - trace_pkt38_stage("coarse", &ec); - let mut tf_res = vec![0i32; self.mode.nb_ebands]; - tf_decode( - start, - end, - is_transient, - &mut tf_res, - lm, - total_bits, - &mut ec, - ); - if trace_this_packet { - debug_trace!("R pkt{} [after_tf] tell={}", packet_idx, ec.tell_frac()); - // #region agent log - let tf_csv = tf_res[start..end] - .iter() - .map(std::string::ToString::to_string) - .collect::>() - .join(","); - debug_trace!( - "R pkt{} frame_call_idx={} tf_res[{}..{}]={}", - packet_idx, - frame_call_idx, - start, - end, - tf_csv - ); - // #endregion - } - trace_pkt38_stage("tf", &ec); - let spread_decision = if ec.tell() + 4 <= total_bits { - ec.dec_icdf(&SPREAD_ICDF, 5) - } else { - 0 - }; - if trace_this_packet { - debug_trace!("R pkt{} [after_spread] tell={}", packet_idx, ec.tell_frac()); - // #region agent log - debug_trace!( - "R pkt{} frame_call_idx={} spread_decision={}", - packet_idx, - frame_call_idx, - spread_decision - ); - // #endregion - } - trace_pkt38_stage("spread", &ec); - let cap = rate::init_caps(self.mode, lm, coded_channels); - let mut offsets = vec![0i32; self.mode.nb_ebands]; - let mut dynalloc_logp = 6i32; - let mut total_bits_q = total_bits << BITRES; - tell = ec.tell_frac() as i32; - if trace_this_packet { - debug_trace!( - "R pkt{} [after_dynalloc_start] tell={}", - packet_idx, - ec.tell_frac() - ); - } - for i in start..end { - let width = (coded_channels as i32) - * (((self.mode.e_bands[i + 1] - self.mode.e_bands[i]) as i32) << lm); - let quanta = ((width << BITRES).min((6 << BITRES).max(width))).max(0); - let mut dynalloc_loop_logp = dynalloc_logp; - let mut boost = 0i32; - let tell_before_dyn_band = tell; - while tell + (dynalloc_loop_logp << BITRES) < total_bits_q && boost < cap[i] { - let flag = ec.dec_bit_logp(dynalloc_loop_logp as u32); - tell = ec.tell_frac() as i32; - if !flag { - break; - } - boost += quanta; - total_bits_q -= quanta; - dynalloc_loop_logp = 1; - } - offsets[i] = boost; - if boost > 0 { - dynalloc_logp = (dynalloc_logp - 1).max(2); - } - if trace_this_packet { - // #region agent log - debug_trace!( - "R pkt{} dynalloc band={} width={} quanta={} cap={} boost={} tell:{}->{} total_bits_q={} dynalloc_logp={}", - packet_idx, - i, - width, - quanta, - cap[i], - boost, - tell_before_dyn_band, - tell, - total_bits_q, - dynalloc_logp - ); - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-stereo-probe\",\"hypothesisId\":\"H8\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:318\",\"message\":\"dynalloc_band_state\",\"data\":{{\"packet_idx\":{},\"band\":{},\"width\":{},\"quanta\":{},\"cap\":{},\"boost\":{},\"tell_before\":{},\"tell_after\":{},\"total_bits_q\":{},\"dynalloc_logp\":{}}},\"timestamp\":{}}}\n", - packet_idx, - i, - width, - quanta, - cap[i], - boost, - tell_before_dyn_band, - tell, - total_bits_q, - dynalloc_logp, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - } - if trace_this_packet { - debug_trace!( - "R pkt{} [after_dynalloc_end] tell={}", - packet_idx, - ec.tell_frac() - ); - } - trace_pkt38_stage("dynalloc", &ec); - let alloc_trim = if tell + (6 << BITRES) <= total_bits_q { - ec.dec_icdf(&TRIM_ICDF, 7) - } else { - 5 - }; - if trace_this_packet { - debug_trace!("R pkt{} [after_trim] tell={}", packet_idx, ec.tell_frac()); - // #region agent log - debug_trace!("R pkt{} alloc_trim={}", packet_idx, alloc_trim); - // #endregion - } - trace_pkt38_stage("trim", &ec); - let mut bits = ((active_len as i32 * 8) << BITRES) - ec.tell_frac() as i32 - 1; - let anti_collapse_rsv = if is_transient && lm >= 2 && bits >= ((lm as i32 + 2) << BITRES) { - 1 << BITRES - } else { - 0 - }; - if packet_idx < 5 || trace_this_packet { - debug_trace!( - "pkt{} is_transient={} anti_collapse_rsv={}", - packet_idx, - is_transient, - anti_collapse_rsv - ); - } - if (4..=8).contains(&packet_idx) { - let overlap_abs: f32 = self.overlap_tail().iter().map(|x| x.abs()).sum(); - let deemph = self.deemph_mem[0]; - debug_trace!( - "R pkt{} STATE_IN overlap_abs={:.4} deemph={:.6} frame_size={} config={} end={} is_transient={} lm={} postfilter_pitch={} anti_collapse_rsv={}", - packet_idx, - overlap_abs, - deemph, - frame_size_48k, - config, - end, - is_transient, - lm, - postfilter_pitch, - anti_collapse_rsv - ); - } - if debug_packet || intra_ener { - let data = format!( - "{{\"packet_idx\":{},\"config\":{},\"end\":{},\"lm\":{},\"is_transient\":{},\"intra_ener\":{},\"postfilter_active\":{},\"postfilter_pitch\":{},\"postfilter_qg\":{},\"postfilter_tapset\":{},\"anti_collapse_rsv\":{}}}", - packet_idx, - config, - end, - lm, - is_transient, - intra_ener, - postfilter_pitch > 0, - postfilter_pitch, - postfilter_qg, - postfilter_tapset, - anti_collapse_rsv - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H4", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "packet_flags", - &data, - ); - // #endregion - } - bits -= anti_collapse_rsv; - let alloc = rate::clt_compute_allocation( - self.mode, - start, - end, - &offsets, - &cap, - alloc_trim, - bits, - coded_channels, - lm, - &mut ec, - packet_idx, - ); - if trace_this_packet { - debug_trace!("R pkt{} [after_alloc] tell={}", packet_idx, ec.tell_frac()); - debug_trace!( - "R pkt{} alloc_meta: bits_in={} balance_out={} coded_bands={} intensity={} dual_stereo={}", - packet_idx, - bits, - alloc.balance, - alloc.coded_bands, - alloc.intensity, - alloc.dual_stereo - ); - for i in start..end { - debug_trace!( - "R pkt{} alloc band={} pulse={} fine={} prio={}", - packet_idx, - i, - alloc.pulses[i], - alloc.fine_quant[i], - alloc.fine_priority[i] - ); - } - } - trace_pkt38_stage("alloc", &ec); - if trace_this_packet { - debug_trace!( - "pkt{}: is_transient={} anti_collapse_rsv={} coded_bands={}", - packet_idx, - is_transient, - anti_collapse_rsv, - alloc.coded_bands - ); - debug_trace!( - "pkt{}: tell_before_bands={} total_bits_q={}", - packet_idx, - ec.tell_frac(), - (active_len as i32 * 8) << 3 - ); - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-stereo-probe\",\"hypothesisId\":\"H2\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:368\",\"message\":\"tell_before_fine_with_alloc\",\"data\":{{\"packet_idx\":{},\"tell\":{},\"total_bits_q\":{},\"channels\":{},\"intensity\":{},\"dual_stereo\":{},\"coded_bands\":{}}},\"timestamp\":{}}}\n", - packet_idx, - ec.tell_frac(), - (active_len as i32 * 8) << 3, - coded_channels, - alloc.intensity, - alloc.dual_stereo, - alloc.coded_bands, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - quant_bands::unquant_fine_energy( - self.mode, - start, - end, - &mut self.prev_energy, - &alloc.fine_quant, - &mut ec, - coded_channels, - packet_idx, - ); - trace_pkt38_stage("fine", &ec); - if trace_this_packet { - debug_trace!( - "R pkt{} [after_fine_energy] tell={}", - packet_idx, - ec.tell_frac() - ); - debug_trace!( - "R pkt{} tell_before_bands_actual={}", - packet_idx, - ec.tell_frac() - ); - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-stereo-probe\",\"hypothesisId\":\"H3\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:383\",\"message\":\"tell_before_bands_actual\",\"data\":{{\"packet_idx\":{},\"tell\":{},\"channels\":{}}},\"timestamp\":{}}}\n", - packet_idx, - ec.tell_frac(), - coded_channels, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - let mut mdct_in = vec![0.0f32; frame_size_48k]; - let tell_before_bands_decode = ec.tell_frac(); - let mut mdct_side = if coded_channels == 2 { - Some(vec![0.0f32; frame_size_48k]) - } else { - None - }; - let band_decode_path = if coded_channels == 2 { - "quant_all_bands_stereo" - } else { - "quant_all_bands_mono" - }; - if trace_this_packet { - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-stereo-probe\",\"hypothesisId\":\"H5\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:434\",\"message\":\"band_decode_path_selected\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"path\":\"{}\",\"intensity\":{},\"dual_stereo\":{},\"tell_before\":{}}},\"timestamp\":{}}}\n", - packet_idx, - coded_channels, - band_decode_path, - alloc.intensity, - alloc.dual_stereo, - tell_before_bands_decode, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - let collapse_masks = if let Some(ref mut side) = mdct_side { - bands::quant_all_bands_stereo( - self.mode, - start, - end, - &mut mdct_in, - side, - &alloc.pulses, - is_transient, - spread_decision, - &tf_res, - (active_len as i32 * 8 << BITRES) - anti_collapse_rsv, - alloc.balance, - alloc.coded_bands, - lm, - &mut ec, - &mut self.rng_seed, - alloc.dual_stereo, - alloc.intensity, - coded_channels == 2 && self.channels == 1, - packet_idx, - frame_call_idx, - ) - } else { - bands::quant_all_bands_mono( - self.mode, - start, - end, - &mut mdct_in, - &alloc.pulses, - is_transient, - spread_decision, - &tf_res, - (active_len as i32 * 8 << BITRES) - anti_collapse_rsv, - alloc.balance, - alloc.coded_bands, - lm, - &mut ec, - &mut self.rng_seed, - packet_idx, - frame_call_idx, - ) - }; - let anti_probe_packet = packet_idx <= 8 || trace_this_packet; - let collapse_blocks = 1usize << lm; - let collapse_mask_limit = if collapse_blocks >= 8 { - 0xFFu8 - } else { - ((1u16 << collapse_blocks) - 1) as u8 - }; - let collapse_nonfull_count = collapse_masks - .iter() - .filter(|&&m| (m & collapse_mask_limit) != collapse_mask_limit) - .count(); - let collapse_empty_count = collapse_masks - .iter() - .filter(|&&m| (m & collapse_mask_limit) == 0) - .count(); - if anti_probe_packet { - let data = format!( - "{{\"packet_idx\":{},\"lm\":{},\"coded_channels\":{},\"collapse_masks_len\":{},\"collapse_mask_limit\":{},\"collapse_nonfull_count\":{},\"collapse_empty_count\":{},\"seed_after_quant\":{},\"ec_rng_after_quant\":{},\"anti_collapse_rsv\":{},\"is_transient\":{}}}", - packet_idx, - lm, - coded_channels, - collapse_masks.len(), - collapse_mask_limit, - collapse_nonfull_count, - collapse_empty_count, - self.rng_seed, - ec.rng(), - anti_collapse_rsv, - is_transient - ); - // #region agent log - Self::append_runtime_debug_log( - "run-anti-collapse-v1", - "H2", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "anti_collapse_post_quant_state", - &data, - ); - // #endregion - } - if packet_idx <= 10 || coded_channels == 2 { - let data = format!( - "{{\"packet_idx\":{},\"coded_channels\":{},\"output_channels\":{},\"is_transient\":{},\"lm\":{},\"anti_collapse_rsv\":{},\"collapse_nonfull_count\":{}}}", - packet_idx, - coded_channels, - self.channels, - is_transient, - lm, - anti_collapse_rsv, - collapse_nonfull_count - ); - // #region agent log - Self::append_runtime_debug_log( - "run-coded-channels-scan-v1", - "H9", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "coded_channels_state", - &data, - ); - // #endregion - } - if trace_this_packet { - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let tell_after = ec.tell_frac(); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-stereo-probe\",\"hypothesisId\":\"H6\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:450\",\"message\":\"band_decode_consumed_bits\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"tell_before\":{},\"tell_after\":{},\"consumed\":{}}},\"timestamp\":{}}}\n", - packet_idx, - coded_channels, - tell_before_bands_decode, - tell_after, - tell_after as i32 - tell_before_bands_decode as i32, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - if trace_this_packet && coded_channels == 2 { - let mid_abs = mdct_in.iter().map(|v| v.abs()).sum::(); - let side_abs = mdct_side - .as_ref() - .map(|v| v.iter().map(|x| x.abs()).sum::()) - .unwrap_or(0.0); - let output_strategy = if self.channels == 1 { - "freq_downmix_after_denorm" - } else { - "multi_channel_output" - }; - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-stereo-probe\",\"hypothesisId\":\"H9\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:486\",\"message\":\"stereo_vectors_before_denorm\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"mid_abs\":{},\"side_abs\":{},\"output_strategy\":\"{}\"}},\"timestamp\":{}}}\n", - packet_idx, coded_channels, mid_abs, side_abs, output_strategy, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - self.last_split_count = 0; - if packet_idx == 0 { - let m = 1usize << lm; - let mut band_abs = String::new(); - for bi in start..end { - let bs = m * self.mode.e_bands[bi] as usize; - let be = m * self.mode.e_bands[bi + 1] as usize; - let acc: f32 = mdct_in[bs..be].iter().map(|v| v.abs()).sum(); - if !band_abs.is_empty() { - band_abs.push(','); - } - band_abs.push_str(&format!("{:.6}", acc)); - } - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-norm-band-scan\",\"hypothesisId\":\"H21\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:676\",\"message\":\"norm_band_abs_snapshot\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"start\":{},\"end\":{},\"lm\":{},\"band_abs_csv\":\"{}\"}},\"timestamp\":{}}}\n", - packet_idx, coded_channels, start, end, lm, band_abs, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - if (packet_idx == 0 || (2128..=2129).contains(&packet_idx)) && is_transient { - let m_blocks = 1usize << lm; - let short_len = frame_size_48k / m_blocks.max(1); - // #region agent log - if m_blocks == 8 && short_len > 0 { - let mut block_abs = [0.0f32; 8]; - for b in 0..8 { - let mut acc = 0.0f32; - for j in 0..short_len { - acc += mdct_in[j * m_blocks + b].abs(); - } - block_abs[b] = acc; - } - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-norm-block-scan\",\"hypothesisId\":\"H20\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:684\",\"message\":\"norm_block_abs_snapshot\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"blocks\":{},\"short_len\":{},\"block_abs\":[{:.6},{:.6},{:.6},{:.6},{:.6},{:.6},{:.6},{:.6}]}},\"timestamp\":{}}}\n", - packet_idx, - coded_channels, - m_blocks, - short_len, - block_abs[0], - block_abs[1], - block_abs[2], - block_abs[3], - block_abs[4], - block_abs[5], - block_abs[6], - block_abs[7], - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - } - // #endregion - } - // Anti-collapse bit (consumed from range coder for transient frames). - let mut anti_collapse_on = 0u32; - if anti_collapse_rsv > 0 { - anti_collapse_on = ec.dec_bits(1); - if trace_this_packet { - debug_trace!( - "pkt{} anti_collapse_rsv={} anti_collapse_on={} is_transient={} lm={}", - packet_idx, - anti_collapse_rsv, - anti_collapse_on, - is_transient, - lm - ); - } - if debug_packet { - let data = format!( - "{{\"packet_idx\":{},\"anti_collapse_rsv\":{},\"anti_collapse_on\":{},\"is_transient\":{},\"lm\":{}}}", - packet_idx, anti_collapse_rsv, anti_collapse_on, is_transient, lm - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H5", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "anti_collapse_flag", - &data, - ); - // #endregion - } - } else if trace_this_packet { - debug_trace!( - "pkt{} anti_collapse_rsv=0 anti_collapse_on=0 is_transient={} lm={}", - packet_idx, - is_transient, - lm - ); - if debug_packet { - let data = format!( - "{{\"packet_idx\":{},\"anti_collapse_rsv\":0,\"anti_collapse_on\":0,\"is_transient\":{},\"lm\":{}}}", - packet_idx, is_transient, lm - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H5", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "anti_collapse_flag", - &data, - ); - // #endregion - } - } else if debug_packet { - let data = format!( - "{{\"packet_idx\":{},\"anti_collapse_rsv\":0,\"anti_collapse_on\":0,\"is_transient\":{},\"lm\":{}}}", - packet_idx, is_transient, lm - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H5", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "anti_collapse_flag", - &data, - ); - // #endregion - } - if anti_probe_packet { - let data = format!( - "{{\"packet_idx\":{},\"anti_collapse_rsv\":{},\"anti_collapse_on\":{},\"seed_before_apply\":{},\"ec_rng_before_apply\":{},\"is_transient\":{},\"lm\":{},\"collapse_nonfull_count\":{}}}", - packet_idx, - anti_collapse_rsv, - anti_collapse_on, - self.rng_seed, - ec.rng(), - is_transient, - lm, - collapse_nonfull_count - ); - // #region agent log - Self::append_runtime_debug_log( - "run-anti-collapse-v1", - "H1", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "anti_collapse_gate_state", - &data, - ); - // #endregion - } - let anti_pre_main_abs = if anti_probe_packet { - mdct_in.iter().map(|v| v.abs()).sum::() - } else { - 0.0 - }; - let anti_pre_side_abs = if anti_probe_packet { - mdct_side - .as_ref() - .map(|side| side.iter().map(|v| v.abs()).sum::()) - .unwrap_or(0.0) - } else { - 0.0 - }; - quant_bands::unquant_energy_finalise( - self.mode, - start, - end, - Some(&mut self.prev_energy), - &alloc.fine_quant, - &alloc.fine_priority, - active_len as i32 * 8 - ec.tell(), - &mut ec, - coded_channels, - ); - if anti_collapse_on != 0 { - if trace_this_packet { - debug_trace!( - "R pkt{} applying anti_collapse lm={} coded_channels={} seed={}", - packet_idx, - lm, - coded_channels, - self.rng_seed - ); - } - if let Some(side) = mdct_side.as_mut() { - bands::anti_collapse( - self.mode, - &mut mdct_in, - Some(side), - &collapse_masks, - lm, - coded_channels, - start, - end, - &self.prev_energy, - &self.old_log_energy, - &self.old_log_energy2, - &alloc.pulses, - self.rng_seed, - trace_this_packet, - packet_idx, - ); - } else { - bands::anti_collapse( - self.mode, - &mut mdct_in, - None, - &collapse_masks, - lm, - coded_channels, - start, - end, - &self.prev_energy, - &self.old_log_energy, - &self.old_log_energy2, - &alloc.pulses, - self.rng_seed, - trace_this_packet, - packet_idx, - ); - } - } - if anti_probe_packet { - let anti_post_main_abs = mdct_in.iter().map(|v| v.abs()).sum::(); - let anti_post_side_abs = mdct_side - .as_ref() - .map(|side| side.iter().map(|v| v.abs()).sum::()) - .unwrap_or(0.0); - let data = format!( - "{{\"packet_idx\":{},\"anti_collapse_on\":{},\"main_abs_before\":{},\"main_abs_after\":{},\"side_abs_before\":{},\"side_abs_after\":{},\"main_abs_delta\":{},\"side_abs_delta\":{},\"seed_after_apply\":{},\"ec_rng_after_apply\":{}}}", - packet_idx, - anti_collapse_on, - anti_pre_main_abs, - anti_post_main_abs, - anti_pre_side_abs, - anti_post_side_abs, - anti_post_main_abs - anti_pre_main_abs, - anti_post_side_abs - anti_pre_side_abs, - self.rng_seed, - ec.rng() - ); - // #region agent log - Self::append_runtime_debug_log( - "run-anti-collapse-v1", - "H5", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "anti_collapse_apply_effect", - &data, - ); - // #endregion - } - if packet_idx == 6 { - let m = 1usize << lm; - let bound = m * self.mode.e_bands[end] as usize; - let x_abs_sum_active: f32 = mdct_in[..bound].iter().map(|x| x.abs()).sum(); - let x_abs_sum_tail: f32 = mdct_in[bound..].iter().map(|x| x.abs()).sum(); - let len = mdct_in.len(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"before_denormalise\",\"x_abs_sum_active\":{},\"x_abs_sum_tail\":{},\"bound\":{},\"x_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"x_active_last8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"x_tail_first4\":[{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - x_abs_sum_active, - x_abs_sum_tail, - bound, - mdct_in.get(0).copied().unwrap_or(0.0), - mdct_in.get(1).copied().unwrap_or(0.0), - mdct_in.get(2).copied().unwrap_or(0.0), - mdct_in.get(3).copied().unwrap_or(0.0), - mdct_in.get(4).copied().unwrap_or(0.0), - mdct_in.get(5).copied().unwrap_or(0.0), - mdct_in.get(6).copied().unwrap_or(0.0), - mdct_in.get(7).copied().unwrap_or(0.0), - mdct_in.get(bound.saturating_sub(8)).copied().unwrap_or(0.0), - mdct_in.get(bound.saturating_sub(7)).copied().unwrap_or(0.0), - mdct_in.get(bound.saturating_sub(6)).copied().unwrap_or(0.0), - mdct_in.get(bound.saturating_sub(5)).copied().unwrap_or(0.0), - mdct_in.get(bound.saturating_sub(4)).copied().unwrap_or(0.0), - mdct_in.get(bound.saturating_sub(3)).copied().unwrap_or(0.0), - mdct_in.get(bound.saturating_sub(2)).copied().unwrap_or(0.0), - mdct_in.get(bound.saturating_sub(1)).copied().unwrap_or(0.0), - mdct_in.get(bound).copied().unwrap_or(0.0), - mdct_in - .get((bound + 1).min(len.saturating_sub(1))) - .copied() - .unwrap_or(0.0), - mdct_in - .get((bound + 2).min(len.saturating_sub(1))) - .copied() - .unwrap_or(0.0), - mdct_in - .get((bound + 3).min(len.saturating_sub(1))) - .copied() - .unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-mdctin-v1", - "H84", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_mdct_in_before_denorm", - &data, - ); - // #endregion - let e = &self.prev_energy[..self.mode.nb_ebands]; - let e_abs_sum: f32 = e.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"before_denormalise\",\"energy_abs_sum\":{},\"energy_first12\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - e_abs_sum, - e.get(0).copied().unwrap_or(0.0), - e.get(1).copied().unwrap_or(0.0), - e.get(2).copied().unwrap_or(0.0), - e.get(3).copied().unwrap_or(0.0), - e.get(4).copied().unwrap_or(0.0), - e.get(5).copied().unwrap_or(0.0), - e.get(6).copied().unwrap_or(0.0), - e.get(7).copied().unwrap_or(0.0), - e.get(8).copied().unwrap_or(0.0), - e.get(9).copied().unwrap_or(0.0), - e.get(10).copied().unwrap_or(0.0), - e.get(11).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-energy-v1", - "H83", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_oldbande_before_denorm", - &data, - ); - // #endregion - } - if silence_flag { - let state_channels = coded_channels.min(2); - for c in 0..state_channels { - let base = c * self.mode.nb_ebands; - for i in 0..self.mode.nb_ebands { - self.prev_energy[base + i] = LOG_ENERGY_FLOOR_DB; - } - } - } - let mut denorm = vec![0.0f32; frame_size_48k]; - bands::denormalise_bands( - self.mode, - &mdct_in, - &mut denorm, - &self.prev_energy[..self.mode.nb_ebands], - start, - end, - lm, - silence_flag, - ); - if packet_idx == 905 { - let denorm_abs_sum: f32 = denorm.iter().map(|x| x.abs()).sum(); - let first_n = denorm.len().min(16); - debug_trace!( - "R pkt905 denorm abs_sum={:.6} first{}={:?}", - denorm_abs_sum, - first_n, - &denorm[..first_n] - ); - let m = 1usize << lm; - for i in 0..end { - let bs = m * self.mode.e_bands[i] as usize; - let be = m * self.mode.e_bands[i + 1] as usize; - let band_abs: f32 = denorm[bs..be].iter().map(|x| x.abs()).sum(); - debug_trace!("R pkt905 denorm band {} abs_sum={:.6}", i, band_abs); - } - let active_end = m * self.mode.e_bands[end] as usize; - let beyond_end_abs: f32 = denorm[active_end..].iter().map(|x| x.abs()).sum(); - debug_trace!( - "R pkt905 denorm beyond_end abs_sum={:.6} active_end={}", - beyond_end_abs, - active_end - ); - } - let mut denorm_right_for_stereo: Option> = None; - if coded_channels == 2 { - if let Some(side) = mdct_side.as_ref() { - let mut denorm_side = vec![0.0f32; frame_size_48k]; - bands::denormalise_bands( - self.mode, - side, - &mut denorm_side, - &self.prev_energy[self.mode.nb_ebands..self.mode.nb_ebands * 2], - start, - end, - lm, - silence_flag, - ); - if self.channels == 1 { - let sum_l_before: f32 = denorm.iter().map(|x| x.abs()).sum(); - let sum_r_before: f32 = denorm_side.iter().map(|x| x.abs()).sum(); - if trace_this_packet { - // #region agent log - debug_trace!( - "R pkt{} denorm abs_sum: L={:.6} R={:.6}", - packet_idx, - sum_l_before, - sum_r_before - ); - // #endregion - } - if packet_idx == 2135 { - debug_trace!( - "R pkt2135 denorm abs_sum: L={:.6} R={:.6}", - sum_l_before, - sum_r_before - ); - } - if packet_idx == 2128 { - let m = 1usize << lm; - for i in 0..end { - let s = self.mode.e_bands[i] as usize * m; - let e = self.mode.e_bands[i + 1] as usize * m; - let l_sum: f32 = denorm[s..e].iter().sum(); - let r_sum: f32 = denorm_side[s..e].iter().sum(); - let mono_sum: f32 = (l_sum + r_sum) * 0.5; - debug_trace!( - "R pkt2128 band{} L={:.6} R={:.6} mono={:.6}", - i, - l_sum, - r_sum, - mono_sum - ); - } - } - if debug_packet { - let data = format!( - "{{\"packet_idx\":{},\"sum_l_before\":{},\"sum_r_before\":{},\"frame_size_48k\":{},\"lm\":{}}}", - packet_idx, sum_l_before, sum_r_before, frame_size_48k, lm - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H2", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "stereo_denorm_before_downmix", - &data, - ); - // #endregion - } - for i in 0..frame_size_48k { - denorm[i] = 0.5 * (denorm[i] + denorm_side[i]); - } - let sum_mono_after: f32 = denorm.iter().map(|x| x.abs()).sum(); - if trace_this_packet { - // #region agent log - debug_trace!( - "R pkt{} mono abs_sum after downmix: {:.6}", - packet_idx, - sum_mono_after - ); - // #endregion - } - if packet_idx == 2135 { - debug_trace!( - "R pkt2135 mono abs_sum after downmix: {:.6}", - sum_mono_after - ); - } - if debug_packet { - let data = format!( - "{{\"packet_idx\":{},\"sum_mono_after\":{},\"frame_size_48k\":{},\"lm\":{}}}", - packet_idx, sum_mono_after, frame_size_48k, lm - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H2", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "stereo_denorm_after_downmix", - &data, - ); - // #endregion - } - } else if self.channels == 2 { - denorm_right_for_stereo = Some(denorm_side); - } - } - } else if self.channels == 2 { - // Mono-coded packet routed to stereo output: identical spectrum on both channels. - denorm_right_for_stereo = Some(denorm.clone()); - } - if packet_idx == 0 && is_transient { - let m_blocks = 1usize << lm; - let mut b0_csv = String::new(); - for bi in start..end { - let bs = m_blocks * self.mode.e_bands[bi] as usize; - let be = m_blocks * self.mode.e_bands[bi + 1] as usize; - let mut acc = 0.0f32; - let mut j = bs; - while j < be { - acc += denorm[j].abs(); - j += m_blocks; - } - if !b0_csv.is_empty() { - b0_csv.push(','); - } - b0_csv.push_str(&format!("{:.6}", acc)); - } - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-band-block0-scan\",\"hypothesisId\":\"H25\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:734\",\"message\":\"denorm_band_block0_abs\",\"data\":{{\"packet_idx\":{},\"start\":{},\"end\":{},\"lm\":{},\"band_block0_abs_csv\":\"{}\"}},\"timestamp\":{}}}\n", - packet_idx, start, end, lm, b0_csv, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - - if packet_idx == 0 || (2127..=2129).contains(&packet_idx) { - let overlap_tail = self.overlap_tail(); - let ovl_sum: f32 = overlap_tail.iter().map(|x| x.abs()).sum(); - let ovl_n = overlap_tail.len().min(8); - debug_trace!( - "R pkt{} overlap_in len={} abs_sum={:.6} first8: {:?}", - packet_idx, - overlap_tail.len(), - ovl_sum, - &overlap_tail[..ovl_n] - ); - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-postfilter-overlap-scan\",\"hypothesisId\":\"H12\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:734\",\"message\":\"overlap_in_snapshot\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"overlap_len\":{},\"overlap_abs_sum\":{},\"overlap_first8\":[{:.6},{:.6},{:.6},{:.6},{:.6},{:.6},{:.6},{:.6}]}} ,\"timestamp\":{}}}\n", - packet_idx, - coded_channels, - overlap_tail.len(), - ovl_sum, - overlap_tail.first().copied().unwrap_or(0.0), - overlap_tail.get(1).copied().unwrap_or(0.0), - overlap_tail.get(2).copied().unwrap_or(0.0), - overlap_tail.get(3).copied().unwrap_or(0.0), - overlap_tail.get(4).copied().unwrap_or(0.0), - overlap_tail.get(5).copied().unwrap_or(0.0), - overlap_tail.get(6).copied().unwrap_or(0.0), - overlap_tail.get(7).copied().unwrap_or(0.0), - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - - // Match libopus buffer flow: shift history before new synthesis. - let decode_len = DECODE_BUFFER_SIZE + self.mode.overlap; - self.decode_mem.copy_within(frame_size_48k..decode_len, 0); - let out_start = Self::out_start(frame_size_48k); - if packet_idx == 6 { - let overlap_src_end = out_start + self.mode.overlap; - let ovl = &self.decode_mem[out_start..overlap_src_end]; - let ovl_abs_sum: f32 = ovl.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"post_shift_overlap_source\",\"overlap_abs_sum\":{},\"overlap_first4\":[{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - ovl_abs_sum, - ovl.first().copied().unwrap_or(0.0), - ovl.get(1).copied().unwrap_or(0.0), - ovl.get(2).copied().unwrap_or(0.0), - ovl.get(3).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-overlap-entry-v1", - "H62", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_post_shift_overlap_source", - &data, - ); - // #endregion - } - if packet_idx == 5 { - let overlap_tail = self.overlap_tail(); - let ovl_abs_sum: f32 = overlap_tail.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"post_shift\",\"overlap_tail_abs_sum\":{},\"overlap_tail_first4\":[{:.9},{:.9},{:.9},{:.9}],\"shift_applied\":true}}", - packet_idx, - ovl_abs_sum, - overlap_tail.first().copied().unwrap_or(0.0), - overlap_tail.get(1).copied().unwrap_or(0.0), - overlap_tail.get(2).copied().unwrap_or(0.0), - overlap_tail.get(3).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-post-imdct-state-pkt5-v1", - "H34", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_post_shift_overlap_tail", - &data, - ); - // #endregion - } - - let mut base_synth = vec![0.0f32; frame_size_48k]; - let trace_transient_pkt5 = - packet_idx == 5 && is_transient && lm == 3 && frame_size_48k == 960; - let trace_long_pkt6 = packet_idx == 6 && !is_transient && lm == 3 && frame_size_48k == 960; - if packet_idx == 0 || (2127..=2129).contains(&packet_idx) { - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let denorm_abs: f32 = denorm.iter().map(|x| x.abs()).sum(); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-synthesis-path-scan\",\"hypothesisId\":\"H15\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:769\",\"message\":\"synthesis_path_decision\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"is_transient\":{},\"lm\":{},\"frame_size_48k\":{},\"postfilter_active\":{},\"postfilter_pitch\":{},\"denorm_abs_sum\":{}}},\"timestamp\":{}}}\n", - packet_idx, - coded_channels, - is_transient, - lm, - frame_size_48k, - postfilter_pitch > 0, - postfilter_pitch, - denorm_abs, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - if trace_transient_pkt5 { - let denorm_abs: f32 = denorm.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"after_denormalise_bands\",\"lm\":{},\"is_transient\":{},\"frame_size\":{},\"denorm_abs_sum\":{},\"denorm_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - lm, - is_transient, - frame_size_48k, - denorm_abs, - denorm.get(0).copied().unwrap_or(0.0), - denorm.get(1).copied().unwrap_or(0.0), - denorm.get(2).copied().unwrap_or(0.0), - denorm.get(3).copied().unwrap_or(0.0), - denorm.get(4).copied().unwrap_or(0.0), - denorm.get(5).copied().unwrap_or(0.0), - denorm.get(6).copied().unwrap_or(0.0), - denorm.get(7).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-transient-imdct-pkt5-v1", - "H30", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_denorm_after_denormalise", - &data, - ); - // #endregion - } - if trace_long_pkt6 { - let denorm_abs: f32 = denorm.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"after_denormalise_bands\",\"lm\":{},\"is_transient\":{},\"frame_size\":{},\"denorm_abs_sum\":{},\"denorm_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - lm, - is_transient, - frame_size_48k, - denorm_abs, - denorm.get(0).copied().unwrap_or(0.0), - denorm.get(1).copied().unwrap_or(0.0), - denorm.get(2).copied().unwrap_or(0.0), - denorm.get(3).copied().unwrap_or(0.0), - denorm.get(4).copied().unwrap_or(0.0), - denorm.get(5).copied().unwrap_or(0.0), - denorm.get(6).copied().unwrap_or(0.0), - denorm.get(7).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-long-imdct-v1", - "H81", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_denorm_after_denormalise", - &data, - ); - // #endregion - } - if is_transient { - // Transient CELT frames are composed of 120-sample short blocks. - let m_blocks = 1usize << lm; - let short_len = frame_size_48k / m_blocks; - let mut short_coeffs = vec![0.0f32; short_len]; - - for b in 0..m_blocks { - let out_offset = out_start + b * short_len; - let overlap_in_abs: f32 = self.decode_mem - [out_offset..out_offset + self.mode.overlap] - .iter() - .map(|x| x.abs()) - .sum(); - for j in 0..short_len { - short_coeffs[j] = denorm[j * m_blocks + b]; - } - if trace_transient_pkt5 { - let coeff_abs_sum: f32 = short_coeffs.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"after_deinterleave_before_imdct\",\"block\":{},\"blocks_total\":{},\"short_len\":{},\"coeff_abs_sum\":{},\"coeff_first5\":[{:.9},{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - b, - m_blocks, - short_len, - coeff_abs_sum, - short_coeffs.get(0).copied().unwrap_or(0.0), - short_coeffs.get(1).copied().unwrap_or(0.0), - short_coeffs.get(2).copied().unwrap_or(0.0), - short_coeffs.get(3).copied().unwrap_or(0.0), - short_coeffs.get(4).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-transient-imdct-pkt5-v1", - "H31", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_short_block_deinterleaved", - &data, - ); - // #endregion - } - let short_coeff_abs: f32 = short_coeffs.iter().map(|x| x.abs()).sum(); - let short_coeff_abs_contig: f32 = (0..short_len) - .map(|j| denorm[b * short_len + j].abs()) - .sum(); - self.mdct_short - .backward( - &short_coeffs, - self.mode.window, - &mut self.decode_mem - [out_offset..out_offset + short_len + self.mode.overlap], - ) - .map_err(|_| Error::NotImplemented)?; - if trace_transient_pkt5 { - let block_out = - &self.decode_mem[out_offset..out_offset + short_len + self.mode.overlap]; - let block_abs_sum: f32 = block_out.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"after_short_imdct_before_overlap_chain\",\"block\":{},\"blocks_total\":{},\"block_out_len\":{},\"block_out_abs_sum\":{},\"block_out_first5\":[{:.9},{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - b, - m_blocks, - block_out.len(), - block_abs_sum, - block_out.get(0).copied().unwrap_or(0.0), - block_out.get(1).copied().unwrap_or(0.0), - block_out.get(2).copied().unwrap_or(0.0), - block_out.get(3).copied().unwrap_or(0.0), - block_out.get(4).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-transient-imdct-pkt5-v1", - "H32", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_short_block_imdct_out", - &data, - ); - // #endregion - } - if (packet_idx == 0 || (2127..=2129).contains(&packet_idx)) - && (packet_idx == 0 || b == 0 || b + 1 == m_blocks) - { - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let head_abs: f32 = self.decode_mem - [out_offset..out_offset + self.mode.overlap] - .iter() - .map(|x| x.abs()) - .sum(); - let tail_abs: f32 = self.decode_mem - [out_offset + short_len..out_offset + short_len + self.mode.overlap] - .iter() - .map(|x| x.abs()) - .sum(); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-synthesis-path-scan\",\"hypothesisId\":\"H16\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:804\",\"message\":\"short_mdct_block_snapshot\",\"data\":{{\"packet_idx\":{},\"block\":{},\"blocks_total\":{},\"short_len\":{},\"overlap\":{},\"overlap_in_abs_sum\":{},\"short_coeff_abs_sum\":{},\"short_coeff_abs_contig\":{},\"short_tmp_head_abs_sum\":{},\"short_tmp_tail_abs_sum\":{}}},\"timestamp\":{}}}\n", - packet_idx, - b, - m_blocks, - short_len, - self.mode.overlap, - overlap_in_abs, - short_coeff_abs, - short_coeff_abs_contig, - head_abs, - tail_abs, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - } - base_synth.copy_from_slice(&self.decode_mem[out_start..out_start + frame_size_48k]); - if trace_transient_pkt5 { - let overlap_tail = self.overlap_tail(); - let base_abs_sum: f32 = base_synth.iter().map(|x| x.abs()).sum(); - let overlap_abs_sum: f32 = overlap_tail.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"after_overlap_add_all_short_blocks\",\"result_abs_sum\":{},\"result_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"new_overlap_abs_sum\":{},\"new_overlap_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - base_abs_sum, - base_synth.get(0).copied().unwrap_or(0.0), - base_synth.get(1).copied().unwrap_or(0.0), - base_synth.get(2).copied().unwrap_or(0.0), - base_synth.get(3).copied().unwrap_or(0.0), - base_synth.get(4).copied().unwrap_or(0.0), - base_synth.get(5).copied().unwrap_or(0.0), - base_synth.get(6).copied().unwrap_or(0.0), - base_synth.get(7).copied().unwrap_or(0.0), - overlap_abs_sum, - overlap_tail.first().copied().unwrap_or(0.0), - overlap_tail.get(1).copied().unwrap_or(0.0), - overlap_tail.get(2).copied().unwrap_or(0.0), - overlap_tail.get(3).copied().unwrap_or(0.0), - overlap_tail.get(4).copied().unwrap_or(0.0), - overlap_tail.get(5).copied().unwrap_or(0.0), - overlap_tail.get(6).copied().unwrap_or(0.0), - overlap_tail.get(7).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-transient-imdct-pkt5-v1", - "H33", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_transient_overlap_result", - &data, - ); - // #endregion - } - } else { - // Long block path for all supported frame sizes. - let mdct_impl = match frame_size_48k { - 120 => &self.mdct_short, - 240 => &self.mdct_240, - 480 => &self.mdct_480, - 960 => &self.mdct, - _ => return Err(Error::NotImplemented), - }; - mdct_impl - .backward( - &denorm, - self.mode.window, - &mut self.decode_mem[out_start..out_start + frame_size_48k + self.mode.overlap], - ) - .map_err(|_| Error::NotImplemented)?; - if trace_long_pkt6 { - let out_abs_sum: f32 = self.decode_mem[out_start..out_start + frame_size_48k] - .iter() - .map(|x| x.abs()) - .sum(); - let ovl_abs_sum: f32 = self.decode_mem - [out_start + frame_size_48k..out_start + frame_size_48k + self.mode.overlap] - .iter() - .map(|x| x.abs()) - .sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"after_long_imdct\",\"out_abs_sum\":{},\"out_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"new_overlap_abs_sum\":{},\"new_overlap_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - out_abs_sum, - self.decode_mem.get(out_start).copied().unwrap_or(0.0), - self.decode_mem.get(out_start + 1).copied().unwrap_or(0.0), - self.decode_mem.get(out_start + 2).copied().unwrap_or(0.0), - self.decode_mem.get(out_start + 3).copied().unwrap_or(0.0), - self.decode_mem.get(out_start + 4).copied().unwrap_or(0.0), - self.decode_mem.get(out_start + 5).copied().unwrap_or(0.0), - self.decode_mem.get(out_start + 6).copied().unwrap_or(0.0), - self.decode_mem.get(out_start + 7).copied().unwrap_or(0.0), - ovl_abs_sum, - self.decode_mem - .get(out_start + frame_size_48k) - .copied() - .unwrap_or(0.0), - self.decode_mem - .get(out_start + frame_size_48k + 1) - .copied() - .unwrap_or(0.0), - self.decode_mem - .get(out_start + frame_size_48k + 2) - .copied() - .unwrap_or(0.0), - self.decode_mem - .get(out_start + frame_size_48k + 3) - .copied() - .unwrap_or(0.0), - self.decode_mem - .get(out_start + frame_size_48k + 4) - .copied() - .unwrap_or(0.0), - self.decode_mem - .get(out_start + frame_size_48k + 5) - .copied() - .unwrap_or(0.0), - self.decode_mem - .get(out_start + frame_size_48k + 6) - .copied() - .unwrap_or(0.0), - self.decode_mem - .get(out_start + frame_size_48k + 7) - .copied() - .unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-long-imdct-v1", - "H82", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_long_imdct_out", - &data, - ); - // #endregion - } - if packet_idx == 0 || (2127..=2129).contains(&packet_idx) { - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let head_abs: f32 = self.decode_mem[out_start..out_start + self.mode.overlap] - .iter() - .map(|x| x.abs()) - .sum(); - let tail_abs: f32 = self.decode_mem[out_start + frame_size_48k - ..out_start + frame_size_48k + self.mode.overlap] - .iter() - .map(|x| x.abs()) - .sum(); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-synthesis-path-scan\",\"hypothesisId\":\"H17\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:854\",\"message\":\"long_mdct_snapshot\",\"data\":{{\"packet_idx\":{},\"frame_size_48k\":{},\"overlap\":{},\"mdct_head_abs_sum\":{},\"mdct_tail_abs_sum\":{}}},\"timestamp\":{}}}\n", - packet_idx, frame_size_48k, self.mode.overlap, head_abs, tail_abs, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - base_synth.copy_from_slice(&self.decode_mem[out_start..out_start + frame_size_48k]); - } - if packet_idx == 0 || (2127..=2129).contains(&packet_idx) { - let overlap_tail = self.overlap_tail(); - let ovl_sum: f32 = overlap_tail.iter().map(|x| x.abs()).sum(); - let ovl_n = overlap_tail.len().min(8); - debug_trace!( - "R pkt{} overlap_out len={} abs_sum={:.6} first8: {:?}", - packet_idx, - overlap_tail.len(), - ovl_sum, - &overlap_tail[..ovl_n] - ); - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-postfilter-overlap-scan\",\"hypothesisId\":\"H13\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:793\",\"message\":\"overlap_out_snapshot\",\"data\":{{\"packet_idx\":{},\"coded_channels\":{},\"overlap_len\":{},\"overlap_abs_sum\":{},\"overlap_first8\":[{:.6},{:.6},{:.6},{:.6},{:.6},{:.6},{:.6},{:.6}]}} ,\"timestamp\":{}}}\n", - packet_idx, - coded_channels, - overlap_tail.len(), - ovl_sum, - overlap_tail.first().copied().unwrap_or(0.0), - overlap_tail.get(1).copied().unwrap_or(0.0), - overlap_tail.get(2).copied().unwrap_or(0.0), - overlap_tail.get(3).copied().unwrap_or(0.0), - overlap_tail.get(4).copied().unwrap_or(0.0), - overlap_tail.get(5).copied().unwrap_or(0.0), - overlap_tail.get(6).copied().unwrap_or(0.0), - overlap_tail.get(7).copied().unwrap_or(0.0), - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - if packet_idx == 905 { - let post_imdct_abs: f32 = base_synth.iter().map(|x| x.abs()).sum(); - let first_n = base_synth.len().min(8); - debug_trace!( - "R pkt905 post_imdct abs_sum={:.6} first{}={:?}", - post_imdct_abs, - first_n, - &base_synth[..first_n] - ); - } - if debug_packet { - let post_imdct_abs: f32 = base_synth.iter().map(|x| x.abs()).sum(); - let overlap_abs_out: f32 = self.overlap_tail().iter().map(|x| x.abs()).sum(); - let mut first8_csv = String::new(); - for x in base_synth.iter().take(8) { - if !first8_csv.is_empty() { - first8_csv.push(';'); - } - first8_csv.push_str(&format!("{:.6}", x)); - } - let data = format!( - "{{\"packet_idx\":{},\"is_transient\":{},\"lm\":{},\"post_imdct_abs\":{},\"overlap_abs_out\":{},\"base_first8_csv\":\"{}\"}}", - packet_idx, is_transient, lm, post_imdct_abs, overlap_abs_out, first8_csv - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H2", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "post_imdct_overlap_out", - &data, - ); - // #endregion - } - if trace_this_packet { - let post_imdct_abs: f32 = base_synth.iter().map(|x| x.abs()).sum(); - // #region agent log - debug_trace!( - "R pkt{} comb_pre c=0 abs_sum={:.6}", - packet_idx, - post_imdct_abs - ); - debug_trace!( - "R pkt{} comb_pre_first8 c=0: {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6}", - packet_idx, - base_synth.first().copied().unwrap_or(0.0), - base_synth.get(1).copied().unwrap_or(0.0), - base_synth.get(2).copied().unwrap_or(0.0), - base_synth.get(3).copied().unwrap_or(0.0), - base_synth.get(4).copied().unwrap_or(0.0), - base_synth.get(5).copied().unwrap_or(0.0), - base_synth.get(6).copied().unwrap_or(0.0), - base_synth.get(7).copied().unwrap_or(0.0), - ); - // #endregion - } - if trace_this_packet && self.channels == 2 { - let right_pre = &self.decode_mem_right[out_start..out_start + frame_size_48k]; - let right_pre_abs: f32 = right_pre.iter().map(|x| x.abs()).sum(); - // #region agent log - debug_trace!( - "R pkt{} comb_pre c=1 abs_sum={:.6}", - packet_idx, - right_pre_abs - ); - debug_trace!( - "R pkt{} comb_pre_first8 c=1: {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6}", - packet_idx, - right_pre.first().copied().unwrap_or(0.0), - right_pre.get(1).copied().unwrap_or(0.0), - right_pre.get(2).copied().unwrap_or(0.0), - right_pre.get(3).copied().unwrap_or(0.0), - right_pre.get(4).copied().unwrap_or(0.0), - right_pre.get(5).copied().unwrap_or(0.0), - right_pre.get(6).copied().unwrap_or(0.0), - right_pre.get(7).copied().unwrap_or(0.0), - ); - // #endregion - } - if self.channels == 2 { - let window = self.mode.window; - let overlap = self.mode.overlap; - if let Some(denorm_right) = denorm_right_for_stereo.as_deref() { - Self::synthesise_channel_to_mem( - denorm_right, - frame_size_48k, - lm, - is_transient, - window, - overlap, - &self.mdct, - &self.mdct_480, - &self.mdct_240, - &self.mdct_short, - &mut self.decode_mem_right, - )?; - } else { - Self::synthesise_channel_to_mem( - &denorm, - frame_size_48k, - lm, - is_transient, - window, - overlap, - &self.mdct, - &self.mdct_480, - &self.mdct_240, - &self.mdct_short, - &mut self.decode_mem_right, - )?; - } - } - - if packet_idx == 5 { - let overlap_tail = self.overlap_tail(); - let ovl_abs_sum: f32 = overlap_tail.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"post_imdct\",\"overlap_tail_abs_sum\":{},\"overlap_tail_first4\":[{:.9},{:.9},{:.9},{:.9}]}}", - packet_idx, - ovl_abs_sum, - overlap_tail.first().copied().unwrap_or(0.0), - overlap_tail.get(1).copied().unwrap_or(0.0), - overlap_tail.get(2).copied().unwrap_or(0.0), - overlap_tail.get(3).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-post-imdct-state-pkt5-v1", - "H34", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_post_imdct_overlap_tail", - &data, - ); - // #endregion - } - - let postfilter_before_abs: f32 = self.decode_mem[out_start..out_start + frame_size_48k] - .iter() - .map(|x| x.abs()) - .sum(); - let pre_s163 = if frame_size_48k > 163 { - self.decode_mem[out_start + 163] - } else { - 0.0 - }; - if packet_idx == 0 && frame_size_48k >= self.mode.short_mdct_size { - let chunk = self.mode.short_mdct_size; - let chunks = frame_size_48k / chunk; - if chunks == 8 { - let mut chunk_abs = [0.0f32; 8]; - for b in 0..8 { - let s = b * chunk; - let e = s + chunk; - chunk_abs[b] = self.decode_mem[out_start + s..out_start + e] - .iter() - .map(|x| x.abs()) - .sum(); - } - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-precomb-chunk-scan\",\"hypothesisId\":\"H23\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:1018\",\"message\":\"precomb_chunk_abs\",\"data\":{{\"packet_idx\":{},\"chunk_size\":{},\"chunk_abs\":[{:.6},{:.6},{:.6},{:.6},{:.6},{:.6},{:.6},{:.6}],\"total_abs\":{}}},\"timestamp\":{}}}\n", - packet_idx, - chunk, - chunk_abs[0], - chunk_abs[1], - chunk_abs[2], - chunk_abs[3], - chunk_abs[4], - chunk_abs[5], - chunk_abs[6], - chunk_abs[7], - postfilter_before_abs, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - } - let pf_period = self.postfilter_period.max(COMBFILTER_MINPERIOD as i32) as usize; - let pf_period_old = self.postfilter_period_old.max(COMBFILTER_MINPERIOD as i32) as usize; - let decoded_tapset = postfilter_tapset.clamp(0, 2) as usize; - let short_n = self.mode.short_mdct_size.min(frame_size_48k); - let disable_comb_filter = false; - if !disable_comb_filter { - Self::comb_filter_in_place( - &mut self.decode_mem, - out_start, - pf_period_old, - pf_period, - short_n, - self.postfilter_gain_old, - self.postfilter_gain, - self.postfilter_tapset_old, - self.postfilter_tapset, - self.mode.window, - self.mode.overlap, - ); - if lm != 0 && frame_size_48k > short_n { - Self::comb_filter_in_place( - &mut self.decode_mem, - out_start + short_n, - pf_period, - postfilter_pitch.max(0) as usize, - frame_size_48k - short_n, - self.postfilter_gain, - postfilter_gain, - self.postfilter_tapset, - decoded_tapset, - self.mode.window, - self.mode.overlap, - ); - } - if self.channels == 2 { - Self::comb_filter_in_place( - &mut self.decode_mem_right, - out_start, - pf_period_old, - pf_period, - short_n, - self.postfilter_gain_old, - self.postfilter_gain, - self.postfilter_tapset_old, - self.postfilter_tapset, - self.mode.window, - self.mode.overlap, - ); - if lm != 0 && frame_size_48k > short_n { - Self::comb_filter_in_place( - &mut self.decode_mem_right, - out_start + short_n, - pf_period, - postfilter_pitch.max(0) as usize, - frame_size_48k - short_n, - self.postfilter_gain, - postfilter_gain, - self.postfilter_tapset, - decoded_tapset, - self.mode.window, - self.mode.overlap, - ); - } - } - } - if packet_idx == 5 { - let overlap_start = DECODE_BUFFER_SIZE; - let overlap_end = overlap_start + self.mode.overlap; - let ovl = &self.decode_mem[overlap_start..overlap_end]; - let ovl_abs_sum: f32 = ovl.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"post_comb\",\"overlap_tail_abs_sum\":{},\"overlap_tail_first4\":[{:.9},{:.9},{:.9},{:.9}],\"comb_filter_disabled\":{}}}", - packet_idx, - ovl_abs_sum, - ovl.get(0).copied().unwrap_or(0.0), - ovl.get(1).copied().unwrap_or(0.0), - ovl.get(2).copied().unwrap_or(0.0), - ovl.get(3).copied().unwrap_or(0.0), - disable_comb_filter - ); - // #region agent log - Self::append_runtime_debug_log( - "run-post-imdct-state-pkt5-v1", - "H35", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_post_comb_overlap_tail", - &data, - ); - // #endregion - } - self.postfilter_period_old = self.postfilter_period; - self.postfilter_gain_old = self.postfilter_gain; - self.postfilter_tapset_old = self.postfilter_tapset; - self.postfilter_period = postfilter_pitch; - self.postfilter_gain = postfilter_gain; - self.postfilter_tapset = decoded_tapset; - if lm != 0 { - self.postfilter_period_old = self.postfilter_period; - self.postfilter_gain_old = self.postfilter_gain; - self.postfilter_tapset_old = self.postfilter_tapset; - } - if packet_idx <= 2 || (2127..=2129).contains(&packet_idx) { - let postfilter_after_abs: f32 = self.decode_mem[out_start..out_start + frame_size_48k] - .iter() - .map(|x| x.abs()) - .sum(); - let post_s163 = if frame_size_48k > 163 { - self.decode_mem[out_start + 163] - } else { - 0.0 - }; - let mut post_win_csv = String::new(); - if frame_size_48k > 170 { - for i in 160..=170 { - if !post_win_csv.is_empty() { - post_win_csv.push(';'); - } - post_win_csv.push_str(&format!("{i}:{:.6}", self.decode_mem[out_start + i])); - } - } - let mut post_win2_csv = String::new(); - if frame_size_48k > 220 { - for i in 200..=220 { - if !post_win2_csv.is_empty() { - post_win2_csv.push(';'); - } - post_win2_csv.push_str(&format!("{i}:{:.6}", self.decode_mem[out_start + i])); - } - } - let mut post_prefix_csv = String::new(); - if packet_idx == 0 && frame_size_48k > 170 { - for i in 0..=170 { - if !post_prefix_csv.is_empty() { - post_prefix_csv.push(';'); - } - post_prefix_csv.push_str(&format!("{i}:{:.6}", self.decode_mem[out_start + i])); - } - } - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-postfilter-apply\",\"hypothesisId\":\"H19\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:980\",\"message\":\"comb_filter_applied\",\"data\":{{\"packet_idx\":{},\"lm\":{},\"postfilter_pitch\":{},\"postfilter_gain\":{},\"postfilter_tapset\":{},\"state_pitch\":{},\"state_gain\":{},\"state_tapset\":{},\"pre_abs_sum\":{},\"post_abs_sum\":{},\"pre_s163\":{},\"post_s163\":{},\"post_win_csv\":\"{}\",\"post_win2_csv\":\"{}\",\"post_prefix_csv\":\"{}\"}},\"timestamp\":{}}}\n", - packet_idx, - lm, - postfilter_pitch, - postfilter_gain, - decoded_tapset, - pf_period, - self.postfilter_gain, - self.postfilter_tapset, - postfilter_before_abs, - postfilter_after_abs, - pre_s163, - post_s163, - post_win_csv, - post_win2_csv, - post_prefix_csv, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - if packet_idx == 905 { - let post_comb = &self.decode_mem[out_start..out_start + frame_size_48k]; - let post_comb_abs: f32 = post_comb.iter().map(|x| x.abs()).sum(); - let first_n = post_comb.len().min(8); - debug_trace!( - "R pkt905 post_comb abs_sum={:.6} first{}={:?}", - post_comb_abs, - first_n, - &post_comb[..first_n] - ); - } - if debug_packet { - let post_comb = &self.decode_mem[out_start..out_start + frame_size_48k]; - let post_comb_abs: f32 = post_comb.iter().map(|x| x.abs()).sum(); - let mut first8_csv = String::new(); - for x in post_comb.iter().take(8) { - if !first8_csv.is_empty() { - first8_csv.push(';'); - } - first8_csv.push_str(&format!("{:.6}", x)); - } - let post_s163 = if frame_size_48k > 163 { - post_comb[163] - } else { - 0.0 - }; - let mut post_win_160_170_csv = String::new(); - if frame_size_48k > 170 { - for i in 160..=170 { - if !post_win_160_170_csv.is_empty() { - post_win_160_170_csv.push(';'); - } - post_win_160_170_csv.push_str(&format!("{}:{:.6}", i, post_comb[i])); - } - } - let data = format!( - "{{\"packet_idx\":{},\"disable_comb_filter\":{},\"post_comb_abs\":{},\"post_comb_first8_csv\":\"{}\",\"post_comb_s163\":{},\"post_comb_160_170_csv\":\"{}\",\"pf_period_old_in\":{},\"pf_period_in\":{},\"pf_period_old_out\":{},\"pf_period_out\":{},\"pf_gain_old_out\":{},\"pf_gain_out\":{},\"pf_tapset_old_out\":{},\"pf_tapset_out\":{}}}", - packet_idx, - disable_comb_filter, - post_comb_abs, - first8_csv, - post_s163, - post_win_160_170_csv, - pf_period_old, - pf_period, - self.postfilter_period_old, - self.postfilter_period, - self.postfilter_gain_old, - self.postfilter_gain, - self.postfilter_tapset_old, - self.postfilter_tapset - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v2", - "H2", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "post_comb_and_pf_state", - &data, - ); - // #endregion - } - if trace_this_packet { - let post_comb = &self.decode_mem[out_start..out_start + frame_size_48k]; - let post_comb_abs: f32 = post_comb.iter().map(|x| x.abs()).sum(); - // #region agent log - debug_trace!( - "R pkt{} comb_post c=0 abs_sum={:.6} first16: {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6}", - packet_idx, - post_comb_abs, - post_comb.first().copied().unwrap_or(0.0), - post_comb.get(1).copied().unwrap_or(0.0), - post_comb.get(2).copied().unwrap_or(0.0), - post_comb.get(3).copied().unwrap_or(0.0), - post_comb.get(4).copied().unwrap_or(0.0), - post_comb.get(5).copied().unwrap_or(0.0), - post_comb.get(6).copied().unwrap_or(0.0), - post_comb.get(7).copied().unwrap_or(0.0), - post_comb.get(8).copied().unwrap_or(0.0), - post_comb.get(9).copied().unwrap_or(0.0), - post_comb.get(10).copied().unwrap_or(0.0), - post_comb.get(11).copied().unwrap_or(0.0), - post_comb.get(12).copied().unwrap_or(0.0), - post_comb.get(13).copied().unwrap_or(0.0), - post_comb.get(14).copied().unwrap_or(0.0), - post_comb.get(15).copied().unwrap_or(0.0), - ); - // #endregion - } - if trace_this_packet && self.channels == 2 { - let post_comb_r = &self.decode_mem_right[out_start..out_start + frame_size_48k]; - let post_comb_r_abs: f32 = post_comb_r.iter().map(|x| x.abs()).sum(); - // #region agent log - debug_trace!( - "R pkt{} comb_post c=1 abs_sum={:.6} first16: {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6}", - packet_idx, - post_comb_r_abs, - post_comb_r.first().copied().unwrap_or(0.0), - post_comb_r.get(1).copied().unwrap_or(0.0), - post_comb_r.get(2).copied().unwrap_or(0.0), - post_comb_r.get(3).copied().unwrap_or(0.0), - post_comb_r.get(4).copied().unwrap_or(0.0), - post_comb_r.get(5).copied().unwrap_or(0.0), - post_comb_r.get(6).copied().unwrap_or(0.0), - post_comb_r.get(7).copied().unwrap_or(0.0), - post_comb_r.get(8).copied().unwrap_or(0.0), - post_comb_r.get(9).copied().unwrap_or(0.0), - post_comb_r.get(10).copied().unwrap_or(0.0), - post_comb_r.get(11).copied().unwrap_or(0.0), - post_comb_r.get(12).copied().unwrap_or(0.0), - post_comb_r.get(13).copied().unwrap_or(0.0), - post_comb_r.get(14).copied().unwrap_or(0.0), - post_comb_r.get(15).copied().unwrap_or(0.0), - ); - if frame_size_48k > 750 { - debug_trace!( - "R pkt{} comb_post_740_750 c=1: {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6} {:.6}", - packet_idx, - post_comb_r[740], - post_comb_r[741], - post_comb_r[742], - post_comb_r[743], - post_comb_r[744], - post_comb_r[745], - post_comb_r[746], - post_comb_r[747], - post_comb_r[748], - post_comb_r[749], - post_comb_r[750], - ); - } - // #endregion - } - if packet_idx == 5 { - let overlap_tail = self.overlap_tail(); - let ovl_abs_sum: f32 = overlap_tail.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"final_state\",\"overlap_abs_sum\":{},\"overlap_first4\":[{:.9},{:.9},{:.9},{:.9}],\"separate_overlap_buffer\":false}}", - packet_idx, - ovl_abs_sum, - overlap_tail.first().copied().unwrap_or(0.0), - overlap_tail.get(1).copied().unwrap_or(0.0), - overlap_tail.get(2).copied().unwrap_or(0.0), - overlap_tail.get(3).copied().unwrap_or(0.0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-post-imdct-state-pkt5-v1", - "H36", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_final_state_overlap", - &data, - ); - // #endregion - } - - let nb_ebands = self.mode.nb_ebands; - let state_len = 2 * nb_ebands; - if coded_channels == 1 { - let (left, right) = self.prev_energy.split_at_mut(nb_ebands); - right.copy_from_slice(left); - } - if !is_transient { - self.old_log_energy2[..state_len].copy_from_slice(&self.old_log_energy[..state_len]); - self.old_log_energy[..state_len].copy_from_slice(&self.prev_energy[..state_len]); - } else { - for i in 0..state_len { - self.old_log_energy[i] = self.old_log_energy[i].min(self.prev_energy[i]); - } - } - let m = 1usize << lm; - let max_background_increase = (m.min(160) as f32) * 0.001; - for i in 0..state_len { - self.background_log_energy[i] = - (self.background_log_energy[i] + max_background_increase).min(self.prev_energy[i]); - } - for c in 0..2usize { - let base = c * nb_ebands; - for i in 0..start.min(nb_ebands) { - self.prev_energy[base + i] = 0.0; - self.old_log_energy[base + i] = LOG_ENERGY_FLOOR_DB; - self.old_log_energy2[base + i] = LOG_ENERGY_FLOOR_DB; - } - for i in end.min(nb_ebands)..nb_ebands { - self.prev_energy[base + i] = 0.0; - self.old_log_energy[base + i] = LOG_ENERGY_FLOOR_DB; - self.old_log_energy2[base + i] = LOG_ENERGY_FLOOR_DB; - } - } - if trace_this_packet { - debug_trace!( - "R pkt{} energy_state_post prev={:?} old1={:?} old2={:?}", - packet_idx, - &self.prev_energy[..nb_ebands], - &self.old_log_energy[..nb_ebands], - &self.old_log_energy2[..nb_ebands] - ); - } - - if packet_idx == 6 { - let pre = &self.decode_mem[out_start..out_start + frame_size_48k]; - let pre_abs_sum: f32 = pre.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"pre_deemph\",\"pre_abs_sum\":{},\"pre_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"deemph_mem0_in\":{}}}", - packet_idx, - pre_abs_sum, - pre.get(0).copied().unwrap_or(0.0), - pre.get(1).copied().unwrap_or(0.0), - pre.get(2).copied().unwrap_or(0.0), - pre.get(3).copied().unwrap_or(0.0), - pre.get(4).copied().unwrap_or(0.0), - pre.get(5).copied().unwrap_or(0.0), - pre.get(6).copied().unwrap_or(0.0), - pre.get(7).copied().unwrap_or(0.0), - self.deemph_mem[0] - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-deemph-v1", - "H71", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_pre_deemph", - &data, - ); - // #endregion - } - for ch in 0..self.channels as usize { - let decode_mem_ch = if ch == 0 { - &self.decode_mem - } else { - &self.decode_mem_right - }; - let mut ch_synth = decode_mem_ch[out_start..out_start + frame_size_48k].to_vec(); - self.apply_deemph(ch, &mut ch_synth); - if packet_idx == 6 && ch == 0 { - let post_abs_sum: f32 = ch_synth.iter().map(|x| x.abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"post_deemph\",\"post_abs_sum\":{},\"post_first8\":[{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9},{:.9}],\"deemph_mem0_out\":{}}}", - packet_idx, - post_abs_sum, - ch_synth.get(0).copied().unwrap_or(0.0), - ch_synth.get(1).copied().unwrap_or(0.0), - ch_synth.get(2).copied().unwrap_or(0.0), - ch_synth.get(3).copied().unwrap_or(0.0), - ch_synth.get(4).copied().unwrap_or(0.0), - ch_synth.get(5).copied().unwrap_or(0.0), - ch_synth.get(6).copied().unwrap_or(0.0), - ch_synth.get(7).copied().unwrap_or(0.0), - self.deemph_mem[ch] - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-deemph-v1", - "H72", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_post_deemph", - &data, - ); - // #endregion - } - if packet_idx == 905 && ch == 0 { - let post_deemph_abs: f32 = ch_synth.iter().map(|x| x.abs()).sum(); - let first_n = ch_synth.len().min(8); - debug_trace!( - "R pkt905 post_deemph abs_sum={:.6} first{}={:?}", - post_deemph_abs, - first_n, - &ch_synth[..first_n] - ); - } - if debug_packet && ch == 0 { - let post_deemph_abs: f32 = ch_synth.iter().map(|x| x.abs()).sum(); - let mut first8_csv = String::new(); - for x in ch_synth.iter().take(8) { - if !first8_csv.is_empty() { - first8_csv.push(';'); - } - first8_csv.push_str(&format!("{:.6}", x)); - } - let data = format!( - "{{\"packet_idx\":{},\"post_deemph_abs\":{},\"deemph_mem0_out\":{},\"post_deemph_first8_csv\":\"{}\"}}", - packet_idx, post_deemph_abs, self.deemph_mem[ch], first8_csv - ); - // #region agent log - Self::append_debug_log( - "run-onset-map-v1", - "H1", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "post_deemph", - &data, - ); - // #endregion - } - if trace_this_packet && ch == 0 { - let post_deemph_abs: f32 = ch_synth.iter().map(|x| x.abs()).sum(); - // #region agent log - debug_trace!( - "R pkt{} post_deemph_abs_sum={:.6}", - packet_idx, - post_deemph_abs - ); - debug_trace!( - "R pkt{} pcm[0..8]: {} {} {} {} {} {} {} {}", - packet_idx, - Self::float_to_i16(ch_synth.first().copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(1).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(2).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(3).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(4).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(5).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(6).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(7).copied().unwrap_or(0.0)) - ); - // #endregion - } - if trace_this_packet && ch == 1 { - let post_deemph_abs: f32 = ch_synth.iter().map(|x| x.abs()).sum(); - // #region agent log - debug_trace!( - "R pkt{} post_deemph_abs_sum_ch1={:.6}", - packet_idx, - post_deemph_abs - ); - debug_trace!( - "R pkt{} pcm_ch1[0..8]: {} {} {} {} {} {} {} {}", - packet_idx, - Self::float_to_i16(ch_synth.first().copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(1).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(2).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(3).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(4).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(5).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(6).copied().unwrap_or(0.0)), - Self::float_to_i16(ch_synth.get(7).copied().unwrap_or(0.0)) - ); - if frame_size_48k > 750 { - debug_trace!( - "R pkt{} pcm_ch1[740..750]: {} {} {} {} {} {} {} {} {} {} {}", - packet_idx, - Self::float_to_i16(ch_synth[740]), - Self::float_to_i16(ch_synth[741]), - Self::float_to_i16(ch_synth[742]), - Self::float_to_i16(ch_synth[743]), - Self::float_to_i16(ch_synth[744]), - Self::float_to_i16(ch_synth[745]), - Self::float_to_i16(ch_synth[746]), - Self::float_to_i16(ch_synth[747]), - Self::float_to_i16(ch_synth[748]), - Self::float_to_i16(ch_synth[749]), - Self::float_to_i16(ch_synth[750]), - ); - } - debug_trace!( - "R pkt{} deemph_mem_ch1_out={:.6}", - packet_idx, - self.deemph_mem[1] - ); - // #endregion - } - if packet_idx == 0 && ch == 0 && frame_size_48k > 170 { - let mut win_csv = String::new(); - for i in 160..=170 { - if !win_csv.is_empty() { - win_csv.push(';'); - } - let si = Self::float_to_i16(ch_synth[i]); - win_csv.push_str(&format!("{i}:{:.6}:{si}", ch_synth[i])); - } - let mut win2_csv = String::new(); - if frame_size_48k > 220 { - for i in 200..=220 { - if !win2_csv.is_empty() { - win2_csv.push(';'); - } - let si = Self::float_to_i16(ch_synth[i]); - win2_csv.push_str(&format!("{i}:{:.6}:{si}", ch_synth[i])); - } - } - // #region agent log - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-bea564.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"4200c5\",\"runId\":\"run-deemph-scan\",\"hypothesisId\":\"H26\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:1110\",\"message\":\"deemph_window_packet0\",\"data\":{{\"packet_idx\":{},\"channel\":{},\"window_csv\":\"{}\",\"window2_csv\":\"{}\"}},\"timestamp\":{}}}\n", - packet_idx, ch, win_csv, win2_csv, ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - // #endregion - } - self.write_output_channel_i16(out, &ch_synth, ch, accum)?; - if packet_idx == 6 && ch == 0 { - let out_len = output_samples * self.channels as usize; - let out_slice = &out[..out_len]; - let out_abs_sum: i64 = out_slice.iter().map(|&x| (x as i64).abs()).sum(); - let data = format!( - "{{\"packet_idx\":{},\"stage\":\"post_quant_i16\",\"out_abs_sum\":{},\"out_first8\":[{},{},{},{},{},{},{},{}]}}", - packet_idx, - out_abs_sum, - out_slice.get(0).copied().unwrap_or(0), - out_slice.get(1).copied().unwrap_or(0), - out_slice.get(2).copied().unwrap_or(0), - out_slice.get(3).copied().unwrap_or(0), - out_slice.get(4).copied().unwrap_or(0), - out_slice.get(5).copied().unwrap_or(0), - out_slice.get(6).copied().unwrap_or(0), - out_slice.get(7).copied().unwrap_or(0) - ); - // #region agent log - Self::append_runtime_debug_log( - "run-pkt6-deemph-v1", - "H73", - "crates/opus-decoder/src/celt/mod.rs:decode_frame", - "rust_pkt6_post_quant_i16", - &data, - ); - // #endregion - } - } - - if packet_idx == 38 { - // #region agent log H103 - if let Ok(mut f) = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open("/Users/tadeusz/Opus/Rasopus/.cursor/debug-9a4e1c.log") - { - let ts = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0); - let line = format!( - "{{\"sessionId\":\"9a4e1c\",\"runId\":\"tv09-pkt38-celt-stage\",\"hypothesisId\":\"H103\",\"location\":\"crates/opus-decoder/src/celt/mod.rs:3300\",\"message\":\"pkt38_before_final_range\",\"data\":{{\"tell\":{},\"tell_frac\":{},\"rng\":{},\"seed\":{},\"is_transient\":{},\"anti_collapse_on\":{},\"lm\":{}}},\"timestamp\":{}}}\n", - ec.tell(), - ec.tell_frac(), - ec.rng(), - self.rng_seed, - is_transient, - anti_collapse_on, - lm, - ts - ); - let _ = std::io::Write::write_all(&mut f, line.as_bytes()); - } - } - self.final_range = ec.final_range(); - if packet_idx < 5 { - debug_trace!( - "pkt{} final_range: got={:08x}", - packet_idx, - self.final_range - ); - } - self.rng_seed = ec.rng(); - let _ = ( - OVERLAP_48K_20MS, - self.mode.preemph[0], - self.mode.overlap, - self.prev_energy.len(), - alloc.intensity, - alloc.dual_stereo, - quant_bands::e_means()[0], - postfilter_pitch, - rate::pulses2bits(self.mode, 0, lm as i32, 0), - ); - Ok(CeltFrameDecode { - samples_per_channel: output_samples, - is_transient, - }) - } -} - -/// Map CELT or Hybrid Opus config to active end band. -/// -/// Params: TOC config value `(toc >> 3) & 0x1f`. -/// Returns: exclusive end band index. -fn bandwidth_end(config: u8) -> usize { - match config { - 12 | 13 => 19, - 14 | 15 => 21, - 16..=19 => 13, - 20..=23 => 17, - 24..=27 => 19, - 28..=31 => 21, - _ => 21, - } -} - -/// Decode CELT TF flags for active bands. -/// -/// Params: band range, transient flag, mutable tf array, LM, frame bit budget and range decoder. -/// Returns: nothing; `tf_res` updated in-place. -fn tf_decode( - start: usize, - end: usize, - is_transient: bool, - tf_res: &mut [i32], - lm: usize, - total_bits: i32, - dec: &mut EcDec<'_>, -) { - let mut budget = total_bits.max(0); - let mut tell = dec.tell(); - let mut logp = if is_transient { 2 } else { 4 }; - let tf_select_rsv = lm > 0 && tell + logp + 1 <= budget; - if tf_select_rsv { - budget = budget.saturating_sub(1); - } - let mut tf_changed = 0i32; - let mut curr = 0i32; - for i in start..end { - if tell + logp <= budget { - curr ^= i32::from(dec.dec_bit_logp(logp as u32)); - tell = dec.tell(); - tf_changed |= curr; - } - tf_res[i] = curr; - logp = if is_transient { 4 } else { 5 }; - } - let mut tf_select = 0i32; - let idx0 = 4 * usize::from(is_transient) + tf_changed as usize; - let idx1 = 4 * usize::from(is_transient) + 2 + tf_changed as usize; - if tf_select_rsv && TF_SELECT_TABLE[lm][idx0] != TF_SELECT_TABLE[lm][idx1] { - tf_select = i32::from(dec.dec_bit_logp(1)); - } - for t in tf_res.iter_mut().take(end).skip(start) { - let idx = 4 * usize::from(is_transient) + 2 * tf_select as usize + *t as usize; - *t = TF_SELECT_TABLE[lm][idx] as i32; - } -} diff --git a/vendor/opus-decoder/src/celt/modes.rs b/vendor/opus-decoder/src/celt/modes.rs deleted file mode 100644 index a7cc2edb..00000000 --- a/vendor/opus-decoder/src/celt/modes.rs +++ /dev/null @@ -1,238 +0,0 @@ -#![allow(clippy::excessive_precision)] - -//! CELT static mode data (48 kHz / 960 / 120 overlap). -//! -//! The values are ported from libopus `modes.c` + `static_modes_float.h`. - -/// CELT pulse cache tables used by allocation/rate logic. -#[derive(Debug, Clone, Copy)] -pub(crate) struct PulseCache { - pub index: &'static [i16], - pub bits: &'static [u8], - pub caps: &'static [u8], -} - -/// Flattened CELT mode definition for decoder use. -#[derive(Debug, Clone, Copy)] -pub(crate) struct CeltMode { - pub overlap: usize, - pub nb_ebands: usize, - pub preemph: [f32; 4], - pub e_bands: &'static [i16], - pub short_mdct_size: usize, - pub nb_alloc_vectors: usize, - pub alloc_vectors: &'static [u8], - pub log_n: &'static [i16], - pub window: &'static [f32], - pub cache: PulseCache, -} - -/// Return the static CELT mode for 48 kHz, 20 ms frames, 2.5 ms overlap. -pub(crate) fn mode48000_960_120() -> &'static CeltMode { - &MODE_48000_960_120 -} - -const EBAND_5MS: [i16; 22] = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100, -]; - -const LOG_N_400: [i16; 21] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 16, 16, 16, 21, 21, 24, 29, 34, 36, -]; - -const WINDOW_120: [f32; 120] = [ - 6.7286966e-05f32, - 0.00060551348, - 0.0016815970, - 0.0032947962, - 0.0054439943, - 0.0081276923, - 0.011344001, - 0.015090633, - 0.019364886, - 0.024163635, - 0.029483315, - 0.035319905, - 0.041668911, - 0.048525347, - 0.055883718, - 0.063737999, - 0.072081616, - 0.080907428, - 0.090207705, - 0.099974111, - 0.11019769, - 0.12086883, - 0.13197729, - 0.14351214, - 0.15546177, - 0.16781389, - 0.18055550, - 0.19367290, - 0.20715171, - 0.22097682, - 0.23513243, - 0.24960208, - 0.26436860, - 0.27941419, - 0.29472040, - 0.31026818, - 0.32603788, - 0.34200931, - 0.35816177, - 0.37447407, - 0.39092462, - 0.40749142, - 0.42415215, - 0.44088423, - 0.45766484, - 0.47447104, - 0.49127978, - 0.50806798, - 0.52481261, - 0.54149077, - 0.55807973, - 0.57455701, - 0.59090049, - 0.60708841, - 0.62309951, - 0.63891306, - 0.65450896, - 0.66986776, - 0.68497077, - 0.69980010, - 0.71433873, - 0.72857055, - 0.74248043, - 0.75605425, - 0.76927895, - 0.78214257, - 0.79463430, - 0.80674445, - 0.81846456, - 0.82978733, - 0.84070669, - 0.85121779, - 0.86131698, - 0.87100183, - 0.88027111, - 0.88912479, - 0.89756398, - 0.90559094, - 0.91320904, - 0.92042270, - 0.92723738, - 0.93365955, - 0.93969656, - 0.94535671, - 0.95064907, - 0.95558353, - 0.96017067, - 0.96442171, - 0.96834849, - 0.97196334, - 0.97527906, - 0.97830883, - 0.98106616, - 0.98356480, - 0.98581869, - 0.98784191, - 0.98964856, - 0.99125274, - 0.99266849, - 0.99390969, - 0.99499004, - 0.99592297, - 0.99672162, - 0.99739874, - 0.99796667, - 0.99843728, - 0.99882195, - 0.99913147, - 0.99937606, - 0.99956527, - 0.99970802, - 0.99981248, - 0.99988613, - 0.99993565, - 0.99996697, - 0.99998518, - 0.99999457, - 0.99999859, - 0.99999982, - 1.0000000, -]; - -const BAND_ALLOCATION: [u8; 231] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 80, 75, 69, 63, 56, 49, 40, - 34, 29, 20, 18, 10, 0, 0, 0, 0, 0, 0, 0, 0, 110, 100, 90, 84, 78, 71, 65, 58, 51, 45, 39, 32, - 26, 20, 12, 0, 0, 0, 0, 0, 0, 118, 110, 103, 93, 86, 80, 75, 70, 65, 59, 53, 47, 40, 31, 23, - 15, 4, 0, 0, 0, 0, 126, 119, 112, 104, 95, 89, 83, 78, 72, 66, 60, 54, 47, 39, 32, 25, 17, 12, - 1, 0, 0, 134, 127, 120, 114, 103, 97, 91, 85, 78, 72, 66, 60, 54, 47, 41, 35, 29, 23, 16, 10, - 1, 144, 137, 130, 124, 113, 107, 101, 95, 88, 82, 76, 70, 64, 57, 51, 45, 39, 33, 26, 15, 1, - 152, 145, 138, 132, 123, 117, 111, 105, 98, 92, 86, 80, 74, 67, 61, 55, 49, 43, 36, 20, 1, 162, - 155, 148, 142, 133, 127, 121, 115, 108, 102, 96, 90, 84, 77, 71, 65, 59, 53, 46, 30, 1, 172, - 165, 158, 152, 143, 137, 131, 125, 118, 112, 106, 100, 94, 87, 81, 75, 69, 63, 56, 45, 20, 200, - 200, 200, 200, 200, 200, 200, 200, 198, 193, 188, 183, 178, 173, 168, 163, 158, 153, 148, 129, - 104, -]; - -const CACHE_INDEX_50: [i16; 105] = [ - -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 41, 41, 41, 82, 82, 123, 164, 200, 222, 0, 0, 0, 0, - 0, 0, 0, 0, 41, 41, 41, 41, 123, 123, 123, 164, 164, 240, 266, 283, 295, 41, 41, 41, 41, 41, - 41, 41, 41, 123, 123, 123, 123, 240, 240, 240, 266, 266, 305, 318, 328, 336, 123, 123, 123, - 123, 123, 123, 123, 123, 240, 240, 240, 240, 305, 305, 305, 318, 318, 343, 351, 358, 364, 240, - 240, 240, 240, 240, 240, 240, 240, 305, 305, 305, 305, 343, 343, 343, 351, 351, 370, 376, 382, - 387, -]; - -const CACHE_BITS_50: [u8; 392] = [ - 40, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 40, 15, 23, 28, 31, 34, 36, 38, 39, 41, 42, 43, 44, 45, 46, 47, - 47, 49, 50, 51, 52, 53, 54, 55, 55, 57, 58, 59, 60, 61, 62, 63, 63, 65, 66, 67, 68, 69, 70, 71, - 71, 40, 20, 33, 41, 48, 53, 57, 61, 64, 66, 69, 71, 73, 75, 76, 78, 80, 82, 85, 87, 89, 91, 92, - 94, 96, 98, 101, 103, 105, 107, 108, 110, 112, 114, 117, 119, 121, 123, 124, 126, 128, 40, 23, - 39, 51, 60, 67, 73, 79, 83, 87, 91, 94, 97, 100, 102, 105, 107, 111, 115, 118, 121, 124, 126, - 129, 131, 135, 139, 142, 145, 148, 150, 153, 155, 159, 163, 166, 169, 172, 174, 177, 179, 35, - 28, 49, 65, 78, 89, 99, 107, 114, 120, 126, 132, 136, 141, 145, 149, 153, 159, 165, 171, 176, - 180, 185, 189, 192, 199, 205, 211, 216, 220, 225, 229, 232, 239, 245, 251, 21, 33, 58, 79, 97, - 112, 125, 137, 148, 157, 166, 174, 182, 189, 195, 201, 207, 217, 227, 235, 243, 251, 17, 35, - 63, 86, 106, 123, 139, 152, 165, 177, 187, 197, 206, 214, 222, 230, 237, 250, 25, 31, 55, 75, - 91, 105, 117, 128, 138, 146, 154, 161, 168, 174, 180, 185, 190, 200, 208, 215, 222, 229, 235, - 240, 245, 255, 16, 36, 65, 89, 110, 128, 144, 159, 173, 185, 196, 207, 217, 226, 234, 242, 250, - 11, 41, 74, 103, 128, 151, 172, 191, 209, 225, 241, 255, 9, 43, 79, 110, 138, 163, 186, 207, - 227, 246, 12, 39, 71, 99, 123, 144, 164, 182, 198, 214, 228, 241, 253, 9, 44, 81, 113, 142, - 168, 192, 214, 235, 255, 7, 49, 90, 127, 160, 191, 220, 247, 6, 51, 95, 134, 170, 203, 234, 7, - 47, 87, 123, 155, 184, 212, 237, 6, 52, 97, 137, 174, 208, 240, 5, 57, 106, 151, 192, 231, 5, - 59, 111, 158, 202, 243, 5, 55, 103, 147, 187, 224, 5, 60, 113, 161, 206, 248, 4, 65, 122, 175, - 224, 4, 67, 127, 182, 234, -]; - -const CACHE_CAPS_50: [u8; 168] = [ - 224, 224, 224, 224, 224, 224, 224, 224, 160, 160, 160, 160, 185, 185, 185, 178, 178, 168, 134, - 61, 37, 224, 224, 224, 224, 224, 224, 224, 224, 240, 240, 240, 240, 207, 207, 207, 198, 198, - 183, 144, 66, 40, 160, 160, 160, 160, 160, 160, 160, 160, 185, 185, 185, 185, 193, 193, 193, - 183, 183, 172, 138, 64, 38, 240, 240, 240, 240, 240, 240, 240, 240, 207, 207, 207, 207, 204, - 204, 204, 193, 193, 180, 143, 66, 40, 185, 185, 185, 185, 185, 185, 185, 185, 193, 193, 193, - 193, 193, 193, 193, 183, 183, 172, 138, 65, 39, 207, 207, 207, 207, 207, 207, 207, 207, 204, - 204, 204, 204, 201, 201, 201, 188, 188, 176, 141, 66, 40, 193, 193, 193, 193, 193, 193, 193, - 193, 193, 193, 193, 193, 194, 194, 194, 184, 184, 173, 139, 65, 39, 204, 204, 204, 204, 204, - 204, 204, 204, 201, 201, 201, 201, 198, 198, 198, 187, 187, 175, 140, 66, 40, -]; - -const MODE_48000_960_120: CeltMode = CeltMode { - overlap: 120, - nb_ebands: 21, - preemph: [0.85000610, 0.0, 1.0, 1.0], - e_bands: &EBAND_5MS, - short_mdct_size: 120, - nb_alloc_vectors: 11, - alloc_vectors: &BAND_ALLOCATION, - log_n: &LOG_N_400, - window: &WINDOW_120, - cache: PulseCache { - index: &CACHE_INDEX_50, - bits: &CACHE_BITS_50, - caps: &CACHE_CAPS_50, - }, -}; diff --git a/vendor/opus-decoder/src/celt/quant_bands.rs b/vendor/opus-decoder/src/celt/quant_bands.rs deleted file mode 100644 index a5c9f41f..00000000 --- a/vendor/opus-decoder/src/celt/quant_bands.rs +++ /dev/null @@ -1,286 +0,0 @@ -//! CELT energy dequantization (`quant_bands.c`) for decoder path. -#![allow( - unused_variables, - unused_assignments, - dead_code, - clippy::too_many_arguments, - clippy::needless_range_loop, - clippy::unnecessary_cast -)] - -use crate::celt::laplace::ec_laplace_decode; -use crate::celt::modes::CeltMode; -use crate::entropy::EcDec; -use std::sync::atomic::{AtomicUsize, Ordering}; - -const MAX_FINE_BITS: i32 = 8; -const E_MEANS: [f32; 25] = [ - 6.4375, 6.25, 5.75, 5.3125, 5.0625, 4.8125, 4.5, 4.375, 4.875, 4.6875, 4.5625, 4.4375, 4.875, - 4.625, 4.3125, 4.5, 4.375, 4.625, 4.75, 4.4375, 3.75, 3.75, 3.75, 3.75, 3.75, -]; -const PRED_COEF: [f32; 4] = [ - 29440.0 / 32768.0, - 26112.0 / 32768.0, - 21248.0 / 32768.0, - 16384.0 / 32768.0, -]; -const BETA_COEF: [f32; 4] = [ - 30147.0 / 32768.0, - 22282.0 / 32768.0, - 12124.0 / 32768.0, - 6554.0 / 32768.0, -]; -const BETA_INTRA: f32 = 4915.0 / 32768.0; -const SMALL_ENERGY_ICDF: [u8; 3] = [2, 1, 0]; -static TRACE_COARSE_CALL_IDX: AtomicUsize = AtomicUsize::new(0); -static TRACE_FINE_CALL_IDX: AtomicUsize = AtomicUsize::new(0); -static TRACE_FINALISE_CALL_IDX: AtomicUsize = AtomicUsize::new(0); - -const E_PROB_MODEL: [[[u8; 42]; 2]; 4] = [ - [ - [ - 72, 127, 65, 129, 66, 128, 65, 128, 64, 128, 62, 128, 64, 128, 64, 128, 92, 78, 92, 79, - 92, 78, 90, 79, 116, 41, 115, 40, 114, 40, 132, 26, 132, 26, 145, 17, 161, 12, 176, 10, - 177, 11, - ], - [ - 24, 179, 48, 138, 54, 135, 54, 132, 53, 134, 56, 133, 55, 132, 55, 132, 61, 114, 70, - 96, 74, 88, 75, 88, 87, 74, 89, 66, 91, 67, 100, 59, 108, 50, 120, 40, 122, 37, 97, 43, - 78, 50, - ], - ], - [ - [ - 83, 78, 84, 81, 88, 75, 86, 74, 87, 71, 90, 73, 93, 74, 93, 74, 109, 40, 114, 36, 117, - 34, 117, 34, 143, 17, 145, 18, 146, 19, 162, 12, 165, 10, 178, 7, 189, 6, 190, 8, 177, - 9, - ], - [ - 23, 178, 54, 115, 63, 102, 66, 98, 69, 99, 74, 89, 71, 91, 73, 91, 78, 89, 86, 80, 92, - 66, 93, 64, 102, 59, 103, 60, 104, 60, 117, 52, 123, 44, 138, 35, 133, 31, 97, 38, 77, - 45, - ], - ], - [ - [ - 61, 90, 93, 60, 105, 42, 107, 41, 110, 45, 116, 38, 113, 38, 112, 38, 124, 26, 132, 27, - 136, 19, 140, 20, 155, 14, 159, 16, 158, 18, 170, 13, 177, 10, 187, 8, 192, 6, 175, 9, - 159, 10, - ], - [ - 21, 178, 59, 110, 71, 86, 75, 85, 84, 83, 91, 66, 88, 73, 87, 72, 92, 75, 98, 72, 105, - 58, 107, 54, 115, 52, 114, 55, 112, 56, 129, 51, 132, 40, 150, 33, 140, 29, 98, 35, 77, - 42, - ], - ], - [ - [ - 42, 121, 96, 66, 108, 43, 111, 40, 117, 44, 123, 32, 120, 36, 119, 33, 127, 33, 134, - 34, 139, 21, 147, 23, 152, 20, 158, 25, 154, 26, 166, 21, 173, 16, 184, 13, 184, 10, - 150, 13, 139, 15, - ], - [ - 22, 178, 63, 114, 74, 82, 84, 83, 92, 82, 103, 62, 96, 72, 96, 67, 101, 73, 107, 72, - 113, 55, 118, 52, 125, 52, 118, 52, 117, 55, 135, 49, 137, 39, 157, 32, 145, 29, 97, - 33, 77, 40, - ], - ], -]; - -/// Decode coarse per-band energies. -/// -/// Params: mode/band range, mutable `old_ebands`, `intra` flag, decoder state, -/// channel count, LM and total frame bits. -/// Returns: nothing; `old_ebands` updated in-place. -pub(crate) fn unquant_coarse_energy( - mode: &CeltMode, - start: usize, - end: usize, - old_ebands: &mut [f32], - intra: bool, - dec: &mut EcDec<'_>, - channels: usize, - lm: usize, - total_bits: i32, -) { - let target_idx = None; - let coarse_call_idx = TRACE_COARSE_CALL_IDX.fetch_add(1, Ordering::SeqCst); - let trace_this_call = target_idx == Some(coarse_call_idx); - if trace_this_call { - debug_trace!( - "R coarse_call_idx={} start tell_frac={}", - coarse_call_idx, - dec.tell_frac() - ); - } - let prob_model = &E_PROB_MODEL[lm][usize::from(intra)]; - let mut prev = [0.0f32; 2]; - let (coef, beta) = if intra { - (0.0, BETA_INTRA) - } else { - (PRED_COEF[lm], BETA_COEF[lm]) - }; - let budget = (dec.tell() + (mode.window.len() as i32 * 0)).max(0); // keep branchless placeholder - let _ = budget; - - for i in start..end { - for c in 0..channels { - let tell = dec.tell(); - let tell_frac_before = dec.tell_frac(); - let qi = if total_bits - tell >= 15 { - let pi = 2 * i.min(20); - ec_laplace_decode( - dec, - (prob_model[pi] as u32) << 7, - (prob_model[pi + 1] as u32) << 6, - ) - } else if total_bits - tell >= 2 { - let q = dec.dec_icdf(&SMALL_ENERGY_ICDF, 2); - (q >> 1) ^ -((q & 1) as i32) - } else if total_bits - tell >= 1 { - -i32::from(dec.dec_bit_logp(1)) - } else { - -1 - }; - if trace_this_call { - debug_trace!( - "R pkt{} coarse band={} c={} qi={} tell_frac={}", - coarse_call_idx, - i, - c, - qi, - dec.tell_frac() - ); - } - let q = qi as f32; - let idx = i + c * mode.nb_ebands; - old_ebands[idx] = old_ebands[idx].max(-9.0); - let tmp = coef * old_ebands[idx] + prev[c] + q; - if target_idx == Some(0) && coarse_call_idx == 0 && i == 0 && c == 0 { - debug_trace!( - "[RUST] pkt0 band0: qi={} q={:.6} coef={:.6} old_before={:.6} prev={:.6} tmp={:.6}", - qi, - q, - coef, - old_ebands[idx], - prev[c], - tmp - ); - } - old_ebands[idx] = tmp.clamp(-28.0, 28.0); - prev[c] = prev[c] + q - beta * q; - } - } -} - -/// Decode fine per-band energies. -/// -/// Params: mode/band range, mutable `old_ebands`, per-band extra quant bits, -/// decoder and channel count. -/// Returns: nothing; `old_ebands` updated in-place. -pub(crate) fn unquant_fine_energy( - mode: &CeltMode, - start: usize, - end: usize, - old_ebands: &mut [f32], - extra_quant: &[i32], - dec: &mut EcDec<'_>, - channels: usize, - packet_idx: usize, -) { - let target_idx = None; - let fine_call_idx = TRACE_FINE_CALL_IDX.fetch_add(1, Ordering::SeqCst); - for i in start..end { - let extra = extra_quant[i]; - if extra <= 0 { - continue; - } - let tell_before_band = dec.tell_frac(); - let guard_ok = dec.tell() + (channels as i32) * extra <= (dec.storage() as i32) * 8; - for c in 0..channels { - let nbits_before = dec.nbits_total(); - let tell_before = dec.tell(); - let q2 = dec.dec_bits(extra as u32) as f32; - let offset = - (q2 + 0.5) * ((1u32 << (14 - extra as u32)) as f32) * (1.0 / 16384.0) - 0.5; - old_ebands[i + c * mode.nb_ebands] += offset; - } - if target_idx == Some(fine_call_idx) { - debug_trace!( - "R pkt{} fine band={} extra={} tell:{}->{}", - fine_call_idx, - i, - extra, - tell_before_band, - dec.tell_frac() - ); - } - } -} - -/// Spend remaining bits on final 1-bit fine energy refinement. -/// -/// Params: mode/band range, optional mutable energies, fine quant/priority -/// arrays, remaining whole bits, decoder and channel count. -/// Returns: nothing. -pub(crate) fn unquant_energy_finalise( - mode: &CeltMode, - start: usize, - end: usize, - old_ebands: Option<&mut [f32]>, - fine_quant: &[i32], - fine_priority: &[i32], - bits_left: i32, - dec: &mut EcDec<'_>, - channels: usize, -) { - let target_idx = None; - let finalise_call_idx = TRACE_FINALISE_CALL_IDX.fetch_add(1, Ordering::SeqCst); - let mut bits_left_mut = bits_left; - let mut old_opt = old_ebands; - for prio in 0..2 { - for i in start..end { - if bits_left_mut < channels as i32 { - return; - } - if fine_quant[i] >= MAX_FINE_BITS || fine_priority[i] != prio { - continue; - } - for c in 0..channels { - if target_idx == Some(0) && finalise_call_idx == 0 && i == 0 && c == 0 { - debug_trace!( - "[RUST] finalise band0 pre-read: tell={} end_offs={} nb_end_bits={}", - dec.tell(), - dec.end_offs(), - dec.nend_bits() - ); - } - let q2 = dec.dec_bits(1) as f32; - let offset = (q2 - 0.5) - * ((1u32 << (14 - fine_quant[i] as u32 - 1)) as f32) - * (1.0 / 16384.0); - if target_idx == Some(0) && finalise_call_idx == 0 && i == 0 && c == 0 { - debug_trace!( - "[RUST] finalise band0: fine_quant={} fine_priority={} q2={} offset={:.6}", - fine_quant[0], - fine_priority[0], - q2 as i32, - offset - ); - } - if let Some(ref mut old) = old_opt { - old[i + c * mode.nb_ebands] += offset; - } - bits_left_mut -= 1; - } - } - } -} - -/// Return CELT eMeans table used by energy logic. -/// -/// Params: none. -/// Returns: static per-band mean energy table. -pub(crate) fn e_means() -> &'static [f32; 25] { - &E_MEANS -} diff --git a/vendor/opus-decoder/src/celt/rate.rs b/vendor/opus-decoder/src/celt/rate.rs deleted file mode 100644 index cb1af668..00000000 --- a/vendor/opus-decoder/src/celt/rate.rs +++ /dev/null @@ -1,459 +0,0 @@ -//! CELT bit allocation logic (`rate.c`) for decoder path. -#![allow(clippy::too_many_arguments)] - -use crate::celt::modes::CeltMode; -use crate::entropy::EcDec; - -const BITRES: i32 = 3; -const FINE_OFFSET: i32 = 21; -const MAX_FINE_BITS: i32 = 8; -const ALLOC_STEPS: i32 = 6; -const LOG2_FRAC_TABLE: [u8; 24] = [ - 0, 8, 13, 16, 19, 21, 23, 24, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 34, 35, 36, 36, 37, 37, -]; -const LOG_MAX_PSEUDO: usize = 6; - -/// Convert pseudo-pulse index to real pulse count. -/// -/// Params: `i` is pseudo index from CELT pulse cache. -/// Returns: actual pulse count for PVQ decode. -pub(crate) fn get_pulses(i: i32) -> i32 { - if i < 8 { - i - } else { - (8 + (i & 7)) << ((i >> 3) - 1) - } -} - -/// Convert allocated bits to pseudo pulse index. -/// -/// Params: `mode` CELT mode, `band` index, `lm` frame size log2, `bits` in BITRES units. -/// Returns: pseudo pulse index used by `get_pulses`. -pub(crate) fn bits2pulses(mode: &CeltMode, band: usize, lm: i32, bits: i32) -> i32 { - let lm1 = (lm + 1).max(0) as usize; - let cache_base = mode.cache.index[lm1 * mode.nb_ebands + band] as usize; - let cache = &mode.cache.bits[cache_base..]; - let mut lo = 0i32; - let mut hi = cache[0] as i32; - let target = bits - 1; - for _ in 0..LOG_MAX_PSEUDO { - let mid = (lo + hi + 1) >> 1; - if cache[mid as usize] as i32 >= target { - hi = mid; - } else { - lo = mid; - } - } - let lo_bits = if lo == 0 { - -1 - } else { - cache[lo as usize] as i32 - }; - if target - lo_bits <= cache[hi as usize] as i32 - target { - lo - } else { - hi - } -} - -/// Convert pseudo pulse index back to spent bits. -/// -/// Params: `mode` CELT mode, `band` index, `lm` frame size log2, `pulses` pseudo pulse index. -/// Returns: number of bits in BITRES units spent by this pulse count. -pub(crate) fn pulses2bits(mode: &CeltMode, band: usize, lm: i32, pulses: i32) -> i32 { - if pulses == 0 { - return 0; - } - let lm1 = (lm + 1).max(0) as usize; - let cache_base = mode.cache.index[lm1 * mode.nb_ebands + band] as usize; - let cache = &mode.cache.bits[cache_base..]; - cache[pulses as usize] as i32 + 1 -} - -/// Allocation result arrays produced by CELT `clt_compute_allocation`. -#[derive(Debug, Clone)] -pub(crate) struct AllocationResult { - pub coded_bands: usize, - pub intensity: i32, - pub dual_stereo: i32, - pub balance: i32, - pub pulses: Vec, - pub fine_quant: Vec, - pub fine_priority: Vec, -} - -/// Initialize CELT per-band bit caps for given `LM` and channel count. -/// -/// Params: `mode` is CELT mode table, `lm` is frame size log ratio, `channels` -/// is decode channel count. -/// Returns: per-band cap values in BITRES units. -pub(crate) fn init_caps(mode: &CeltMode, lm: usize, channels: usize) -> Vec { - let mut cap = vec![0i32; mode.nb_ebands]; - for (i, cap_i) in cap.iter_mut().enumerate() { - let n = ((mode.e_bands[i + 1] - mode.e_bands[i]) as usize) << lm; - let idx = mode.nb_ebands * (2 * lm + channels - 1) + i; - *cap_i = (((mode.cache.caps[idx] as i32) + 64) * channels as i32 * n as i32) >> 2; - } - cap -} - -/// Compute CELT allocation for one frame (decode path). -/// -/// Params: `mode` CELT mode, `start..end` active bands, `offsets` dynamic -/// allocation boosts, `cap` max bits per band, `alloc_trim` trim symbol, -/// `total` available bits in BITRES units, `channels`, `lm`, and `dec`. -/// Returns: full allocation result (coded bands, pulses, fine bits, balance). -pub(crate) fn clt_compute_allocation( - mode: &CeltMode, - start: usize, - end: usize, - offsets: &[i32], - cap: &[i32], - alloc_trim: i32, - total: i32, - channels: usize, - lm: usize, - dec: &mut EcDec<'_>, - _packet_idx: usize, -) -> AllocationResult { - let mut total_bits = total.max(0); - let skip_rsv = if total_bits >= (1 << BITRES) { - 1 << BITRES - } else { - 0 - }; - total_bits -= skip_rsv; - - let mut intensity_rsv = 0i32; - let mut dual_stereo_rsv = 0i32; - if channels == 2 { - intensity_rsv = LOG2_FRAC_TABLE[end - start] as i32; - if intensity_rsv > total_bits { - intensity_rsv = 0; - } else { - total_bits -= intensity_rsv; - dual_stereo_rsv = if total_bits >= (1 << BITRES) { - 1 << BITRES - } else { - 0 - }; - total_bits -= dual_stereo_rsv; - } - } - - let len = mode.nb_ebands; - let mut bits1 = vec![0i32; len]; - let mut bits2 = vec![0i32; len]; - let mut thresh = vec![0i32; len]; - let mut trim_offset = vec![0i32; len]; - let c = channels as i32; - for j in start..end { - let band_n = (mode.e_bands[j + 1] - mode.e_bands[j]) as i32; - thresh[j] = (c << BITRES).max((3 * band_n << lm << BITRES) >> 4); - trim_offset[j] = c - * band_n - * (alloc_trim - 5 - lm as i32) - * (end - j - 1) as i32 - * (1 << (lm as i32 + BITRES)) - >> 6; - if (band_n << lm) == 1 { - trim_offset[j] -= c << BITRES; - } - } - let mut lo = 1i32; - let mut hi = mode.nb_alloc_vectors as i32 - 1; - while lo <= hi { - let mut done = false; - let mut psum = 0i32; - let mid = (lo + hi) >> 1; - for j in (start..end).rev() { - let n = (mode.e_bands[j + 1] - mode.e_bands[j]) as i32; - let mut bits_j = - c * n * ((mode.alloc_vectors[mid as usize * len + j] as i32) << lm) >> 2; - if bits_j > 0 { - bits_j = (bits_j + trim_offset[j]).max(0); - } - bits_j += offsets[j]; - if bits_j >= thresh[j] || done { - done = true; - psum += bits_j.min(cap[j]); - } else if bits_j >= c << BITRES { - psum += c << BITRES; - } - } - if psum > total_bits { - hi = mid - 1; - } else { - lo = mid + 1; - } - } - hi = lo; - lo -= 1; - - let mut skip_start = start; - for j in start..end { - let n = (mode.e_bands[j + 1] - mode.e_bands[j]) as i32; - let mut bits1j = c * n * ((mode.alloc_vectors[lo as usize * len + j] as i32) << lm) >> 2; - let mut bits2j = if hi as usize >= mode.nb_alloc_vectors { - cap[j] - } else { - c * n * ((mode.alloc_vectors[hi as usize * len + j] as i32) << lm) >> 2 - }; - if bits1j > 0 { - bits1j = (bits1j + trim_offset[j]).max(0); - } - if bits2j > 0 { - bits2j = (bits2j + trim_offset[j]).max(0); - } - if lo > 0 { - bits1j += offsets[j]; - } - bits2j += offsets[j]; - if offsets[j] > 0 { - skip_start = j; - } - bits2j = (bits2j - bits1j).max(0); - bits1[j] = bits1j; - bits2[j] = bits2j; - } - - let mut pulses = vec![0i32; len]; - let mut fine_quant = vec![0i32; len]; - let mut fine_priority = vec![0i32; len]; - let mut intensity = 0i32; - let mut dual_stereo = 0i32; - let (coded_bands, balance) = interp_bits2pulses( - mode, - start, - end, - skip_start, - &bits1, - &bits2, - &thresh, - cap, - total_bits, - skip_rsv, - &mut intensity, - intensity_rsv, - &mut dual_stereo, - dual_stereo_rsv, - &mut pulses, - &mut fine_quant, - &mut fine_priority, - channels, - lm, - dec, - _packet_idx, - ); - - AllocationResult { - coded_bands, - intensity, - dual_stereo, - balance, - pulses, - fine_quant, - fine_priority, - } -} - -/// Interpolate allocation vectors and derive pulse/fine bit splits. -/// -/// Params: direct port of libopus `interp_bits2pulses` decoder flow. -/// Returns: number of coded bands. -#[allow(clippy::too_many_arguments)] -fn interp_bits2pulses( - mode: &CeltMode, - start: usize, - end: usize, - skip_start: usize, - bits1: &[i32], - bits2: &[i32], - thresh: &[i32], - cap: &[i32], - total: i32, - skip_rsv: i32, - intensity: &mut i32, - mut intensity_rsv: i32, - dual_stereo: &mut i32, - mut dual_stereo_rsv: i32, - bits: &mut [i32], - ebits: &mut [i32], - fine_priority: &mut [i32], - channels: usize, - lm: usize, - dec: &mut EcDec<'_>, - _packet_idx: usize, -) -> (usize, i32) { - let trace_packet0 = false; - let c = channels as i32; - let stereo = channels > 1; - let alloc_floor = c << BITRES; - let log_m = (lm as i32) << BITRES; - - let mut lo = 0i32; - let mut hi = 1 << ALLOC_STEPS; - for _ in 0..ALLOC_STEPS { - let mid = (lo + hi) >> 1; - let mut psum = 0i32; - let mut done = false; - for j in (start..end).rev() { - let tmp = bits1[j] + ((mid * bits2[j]) >> ALLOC_STEPS); - if tmp >= thresh[j] || done { - done = true; - psum += tmp.min(cap[j]); - } else if tmp >= alloc_floor { - psum += alloc_floor; - } - } - if psum > total { - hi = mid; - } else { - lo = mid; - } - } - - let mut psum = 0i32; - let mut done = false; - for j in (start..end).rev() { - let mut tmp = bits1[j] + ((lo * bits2[j]) >> ALLOC_STEPS); - if tmp < thresh[j] && !done { - tmp = if tmp >= alloc_floor { alloc_floor } else { 0 }; - } else { - done = true; - } - tmp = tmp.min(cap[j]); - bits[j] = tmp; - psum += tmp; - } - - let mut coded_bands = end; - let mut total_adj = total; - loop { - let j = coded_bands - 1; - if j <= skip_start { - // Match libopus: give back reserved skip bit only when skipping loop ends naturally. - total_adj += skip_rsv; - break; - } - let left = total - psum; - let denom = (mode.e_bands[coded_bands] - mode.e_bands[start]) as i32; - let percoeff = if denom > 0 { left / denom } else { 0 }; - let left_rem = left - denom * percoeff; - let rem = (left_rem - (mode.e_bands[j] - mode.e_bands[start]) as i32).max(0); - let band_width = (mode.e_bands[coded_bands] - mode.e_bands[j]) as i32; - let mut band_bits = bits[j] + percoeff * band_width + rem; - if band_bits >= thresh[j].max(alloc_floor + (1 << BITRES)) { - if dec.dec_bit_logp(1) { - break; - } - psum += 1 << BITRES; - band_bits -= 1 << BITRES; - } - psum -= bits[j] + intensity_rsv; - if intensity_rsv > 0 { - intensity_rsv = LOG2_FRAC_TABLE[j - start] as i32; - } - psum += intensity_rsv; - bits[j] = if band_bits >= alloc_floor { - alloc_floor - } else { - 0 - }; - psum += bits[j]; - coded_bands -= 1; - } - coded_bands = coded_bands.max(start + 1); - - if intensity_rsv > 0 { - *intensity = start as i32 + dec.dec_uint((coded_bands + 1 - start) as u32) as i32; - } else { - *intensity = 0; - } - if *intensity <= start as i32 { - total_adj += dual_stereo_rsv; - dual_stereo_rsv = 0; - } - if dual_stereo_rsv > 0 { - *dual_stereo = i32::from(dec.dec_bit_logp(1)); - } else { - *dual_stereo = 0; - } - if trace_packet0 { - debug_trace!( - "[RUST] alloc: coded_bands={} psum={} total_adj={}", - coded_bands, - psum, - total_adj - ); - } - - let left = total_adj - psum; - let denom = (mode.e_bands[coded_bands] - mode.e_bands[start]) as i32; - let percoeff = if denom > 0 { left / denom } else { 0 }; - let mut left_rem = left - denom * percoeff; - for (j, bits_j) in bits.iter_mut().enumerate().take(coded_bands).skip(start) { - *bits_j += percoeff * (mode.e_bands[j + 1] - mode.e_bands[j]) as i32; - let tmp = left_rem.min((mode.e_bands[j + 1] - mode.e_bands[j]) as i32); - *bits_j += tmp; - left_rem -= tmp; - } - - let mut balance = 0i32; - let stereo_shift = if stereo { 1 } else { 0 }; - for j in start..coded_bands { - let n0 = (mode.e_bands[j + 1] - mode.e_bands[j]) as i32; - let n = n0 << lm; - let bit = bits[j] + balance; - let mut excess; - if n > 1 { - excess = (bit - cap[j]).max(0); - bits[j] = bit - excess; - let den = c * n - + if channels == 2 && n > 2 && *dual_stereo == 0 && j < *intensity as usize { - 1 - } else { - 0 - }; - let nclogn = den * (mode.log_n[j] as i32 + log_m); - let mut offset = (nclogn >> 1) - den * FINE_OFFSET; - if n == 2 { - offset += den << BITRES >> 2; - } - if bits[j] + offset < den * 2 << BITRES { - offset += nclogn >> 2; - } else if bits[j] + offset < den * 3 << BITRES { - offset += nclogn >> 3; - } - let mut e = (bits[j] + offset + (den << (BITRES - 1))).max(0); - e = (e / den) >> BITRES; - if c * e > (bits[j] >> BITRES) { - e = (bits[j] >> stereo_shift) >> BITRES; - } - e = e.min(MAX_FINE_BITS); - ebits[j] = e; - fine_priority[j] = i32::from(ebits[j] * (den << BITRES) >= bits[j] + offset); - bits[j] -= c * ebits[j] << BITRES; - } else { - excess = (bit - (c << BITRES)).max(0); - bits[j] = bit - excess; - ebits[j] = 0; - fine_priority[j] = 1; - } - if excess > 0 { - let extra_fine = - ((excess >> (stereo_shift + BITRES)).max(0)).min(MAX_FINE_BITS - ebits[j]); - ebits[j] += extra_fine; - let extra_bits = (extra_fine * c) << BITRES; - fine_priority[j] = i32::from(extra_bits >= excess - balance); - excess -= extra_bits; - } - balance = excess; - } - for j in coded_bands..end { - ebits[j] = bits[j] >> if stereo { 1 } else { 0 } >> BITRES; - bits[j] = 0; - fine_priority[j] = i32::from(ebits[j] < 1); - } - (coded_bands, balance) -} diff --git a/vendor/opus-decoder/src/celt/vq.rs b/vendor/opus-decoder/src/celt/vq.rs deleted file mode 100644 index f91f78ae..00000000 --- a/vendor/opus-decoder/src/celt/vq.rs +++ /dev/null @@ -1,179 +0,0 @@ -//! CELT PVQ vector decode helpers (`vq.c`) for decoder path. -#![allow(clippy::too_many_arguments)] - -use crate::celt::cwrs; -use crate::entropy::EcDec; - -/// CELT spreading mode: none. -pub(crate) const SPREAD_NONE: i32 = 0; - -/// PVQ band decode diagnostic information. -#[derive(Debug, Clone, Copy)] -pub(crate) struct PvqDecodeInfo { - pub collapse_mask: u8, - pub nc: u32, - pub index: u32, -} - -/// Run one exponential rotation stage. -/// -/// Params: mutable band vector, stage stride and sin/cos factors. -/// Returns: nothing. -fn exp_rotation1(x: &mut [f32], stride: usize, c: f32, s: f32) { - let len = x.len(); - if len <= stride { - return; - } - for i in 0..(len - stride) { - let x1 = x[i]; - let x2 = x[i + stride]; - x[i + stride] = c * x2 + s * x1; - x[i] = c * x1 - s * x2; - } - if len <= 2 * stride { - return; - } - for i in (0..=(len - 2 * stride - 1)).rev() { - let x1 = x[i]; - let x2 = x[i + stride]; - x[i + stride] = c * x2 + s * x1; - x[i] = c * x1 - s * x2; - } -} - -/// Apply CELT spreading rotation. -/// -/// Params: mutable band vector, direction, stride, pulses and spread mode. -/// Returns: nothing. -fn exp_rotation(x: &mut [f32], dir: i32, stride: usize, k: i32, spread: i32) { - const SPREAD_FACTOR: [i32; 3] = [15, 10, 5]; - if 2 * k >= x.len() as i32 || spread == SPREAD_NONE { - return; - } - let factor = SPREAD_FACTOR[(spread - 1) as usize]; - let gain = x.len() as f32 / (x.len() as f32 + factor as f32 * k as f32); - let theta = 0.5 * gain * gain; - let angle = core::f32::consts::FRAC_PI_2 * theta; - let c = angle.cos(); - let s = angle.sin(); - let mut stride2 = 0usize; - if x.len() >= 8 * stride { - stride2 = 1; - while (stride2 * stride2 + stride2) * stride + (stride >> 2) < x.len() { - stride2 += 1; - } - } - let len = x.len() / stride; - for i in 0..stride { - let xs = &mut x[i * len..(i + 1) * len]; - if dir < 0 { - if stride2 > 0 { - exp_rotation1(xs, stride2, s, c); - } - exp_rotation1(xs, 1, c, s); - } else { - exp_rotation1(xs, 1, c, -s); - if stride2 > 0 { - exp_rotation1(xs, stride2, s, -c); - } - } - } -} - -/// Normalize decoded integer pulses into unit-energy vector. -/// -/// Params: signed pulses, output vector and synthesis gain. -/// Returns: sum of squared pulses. -fn normalise_residual(iy: &[i32], x: &mut [f32], gain: f32) -> u32 { - let ryy = iy - .iter() - .fold(0u32, |acc, &v| acc.wrapping_add((v * v) as u32)); - let scale = if ryy == 0 { - 0.0 - } else { - gain / (ryy as f32).sqrt() - }; - for (dst, &src) in x.iter_mut().zip(iy.iter()) { - *dst = src as f32 * scale; - } - ryy -} - -/// Build collapse mask for transient anti-collapse handling. -/// -/// Params: decoded pulse vector, band length and block count. -/// Returns: bit mask with one bit per short block. -fn extract_collapse_mask(iy: &[i32], n: usize, b: usize) -> u8 { - if b <= 1 { - return 1; - } - let n0 = n / b; - let mut mask = 0u8; - for i in 0..b { - let mut nonzero = 0i32; - for j in 0..n0 { - nonzero |= iy[i * n0 + j]; - } - if nonzero != 0 { - // NoSlacking: up to 16 short blocks; libopus keeps an unsigned - // here. Wrapping onto bit `i % 8` is what release builds always - // did, and the fold after gives libopus's mask. - mask |= 1u8.wrapping_shl(i as u32); - } - } - mask -} - -/// Decode one PVQ normalized band. -/// -/// Params: output vector, pulses `k`, spread mode, blocks `b`, decoder and gain. -/// Returns: collapse mask and CWRS decode metadata for this band. -pub(crate) fn alg_unquant( - x: &mut [f32], - k: i32, - spread: i32, - b: usize, - band: usize, - trace_packet: bool, - dec: &mut EcDec<'_>, - gain: f32, -) -> PvqDecodeInfo { - let decoded = cwrs::decode_pulses(dec, x.len(), k as usize); - normalise_residual(&decoded.pulses, x, gain); - if trace_packet && band == 6 && x.len() >= 4 { - debug_trace!( - "band6 pre_rot: {:.6} {:.6} {:.6} {:.6}", - x[0], - x[1], - x[2], - x[3] - ); - } - exp_rotation(x, -1, b.max(1), k, spread); - if trace_packet && band == 6 && x.len() >= 4 { - debug_trace!( - "band6 post_rot: {:.6} {:.6} {:.6} {:.6}", - x[0], - x[1], - x[2], - x[3] - ); - } - PvqDecodeInfo { - collapse_mask: extract_collapse_mask(&decoded.pulses, x.len(), b.max(1)), - nc: decoded.nc, - index: decoded.index, - } -} - -/// Renormalize vector to desired gain. -/// -/// Params: mutable vector and target gain. -/// Returns: nothing. -pub(crate) fn renormalise_vector(x: &mut [f32], gain: f32) { - let e = x.iter().map(|v| v * v).sum::().max(1e-12); - let g = gain / e.sqrt(); - for v in x.iter_mut() { - *v *= g; - } -} diff --git a/vendor/opus-decoder/src/compare.rs b/vendor/opus-decoder/src/compare.rs deleted file mode 100644 index e1fbb68a..00000000 --- a/vendor/opus-decoder/src/compare.rs +++ /dev/null @@ -1,353 +0,0 @@ -//! Conformance comparison helper. -//! -//! This ports libopus' `src/opus_compare.c` quality metric, used by the -//! reference `tests/run_vectors.sh` script when validating RFC test vectors. -//! -//! NOTE: This is intentionally *not* a bit-exact PCM comparator. Minor output -//! differences can happen between builds/platforms (especially in CELT), while -//! still being considered acceptable by the reference test harness. - -#![allow( - dead_code, - clippy::too_many_arguments, - clippy::needless_range_loop, - clippy::identity_op, - clippy::erasing_op -)] - -use core::f32; - -const NBANDS: usize = 21; -const NFREQS: usize = 240; - -// Bark-derived CELT bands used by opus_compare.c. -const BANDS: [usize; NBANDS + 1] = [ - 0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120, 156, 200, -]; - -const TEST_WIN_SIZE: usize = 480; -const TEST_WIN_STEP: usize = 120; - -#[derive(Debug, Clone, Copy)] -pub struct QualityResult { - pub quality_percent: f32, - pub internal_weighted_error: f64, -} - -impl QualityResult { - pub fn passes_vectors(&self) -> bool { - self.quality_percent >= 0.0 - } -} - -pub fn compare_quality( - reference_pcm: &[i16], - candidate_pcm: &[i16], - reference_channels: usize, - candidate_channels: usize, - rate_hz: u32, -) -> Result { - if reference_channels != 1 && reference_channels != 2 { - return Err(format!( - "invalid reference channel count: {reference_channels}" - )); - } - if candidate_channels != 1 && candidate_channels != 2 { - return Err(format!( - "invalid candidate channel count: {candidate_channels}" - )); - } - if !matches!(rate_hz, 8000 | 12000 | 16000 | 24000 | 48000) { - return Err(format!("invalid rate_hz: {rate_hz}")); - } - - if reference_pcm.len() % reference_channels != 0 { - return Err("reference pcm is not properly interleaved".to_string()); - } - if candidate_pcm.len() % candidate_channels != 0 { - return Err("candidate pcm is not properly interleaved".to_string()); - } - - let downsample = (48_000u32 / rate_hz) as usize; - if downsample == 0 || 48_000u32 % rate_hz != 0 { - return Err("rate_hz must divide 48000".to_string()); - } - - let xlength = reference_pcm.len() / reference_channels; - let ylength = candidate_pcm.len() / candidate_channels; - if xlength != ylength.saturating_mul(downsample) { - return Err(format!( - "sample counts do not match (ref frames {xlength} != cand frames {ylength} * downsample {downsample})" - )); - } - if xlength < TEST_WIN_SIZE { - return Err(format!( - "insufficient sample data (ref frames {xlength} < {TEST_WIN_SIZE})" - )); - } - - // Convert reference to f32 (mono or stereo per reference_channels). - let mut x: Vec = Vec::with_capacity(xlength * candidate_channels); - if reference_channels == 1 { - for i in 0..xlength { - x.push(reference_pcm[i] as f32); - } - } else if candidate_channels == 1 { - for i in 0..xlength { - let l = reference_pcm[2 * i] as f32; - let r = reference_pcm[2 * i + 1] as f32; - x.push(0.5 * (l + r)); - } - } else { - for &s in reference_pcm { - x.push(s as f32); - } - } - - let y: Vec = candidate_pcm.iter().map(|&s| s as f32).collect(); - - // Equivalent of: nframes=(xlength-TEST_WIN_SIZE+TEST_WIN_STEP)/TEST_WIN_STEP; - let nframes = (xlength - TEST_WIN_SIZE + TEST_WIN_STEP) / TEST_WIN_STEP; - if nframes == 0 { - return Err("nframes == 0".to_string()); - } - - let (ybands, yfreqs) = match rate_hz { - 48000 => (NBANDS, NFREQS), - 24000 => (19usize, NFREQS / downsample), - 16000 => (17usize, NFREQS / downsample), - 12000 => (15usize, NFREQS / downsample), - 8000 => (13usize, NFREQS / downsample), - _ => return Err("unreachable rate_hz".to_string()), - }; - - let nch = candidate_channels; - - // xb: per-band masking energy - let mut xb = vec![0f32; nframes * NBANDS * nch]; - let mut x_ps = vec![0f32; nframes * NFREQS * nch]; - let mut y_ps = vec![0f32; nframes * yfreqs * nch]; - - band_energy( - Some(&mut xb), - &mut x_ps, - &x, - nch, - nframes, - TEST_WIN_SIZE, - TEST_WIN_STEP, - 1, - NBANDS, - NFREQS, - ); - - band_energy( - None, - &mut y_ps, - &y, - nch, - nframes, - TEST_WIN_SIZE / downsample, - TEST_WIN_STEP / downsample, - downsample, - ybands, - yfreqs, - ); - - // Apply masking and smoothing like opus_compare.c. - for xi in 0..nframes { - // Frequency masking (low -> high): 10 dB/Bark slope. - for bi in 1..NBANDS { - for ci in 0..nch { - let cur = (xi * NBANDS + bi) * nch + ci; - let prev = (xi * NBANDS + (bi - 1)) * nch + ci; - xb[cur] += 0.1 * xb[prev]; - } - } - - // Frequency masking (high -> low): 15 dB/Bark slope. - for bi in (0..NBANDS - 1).rev() { - for ci in 0..nch { - let cur = (xi * NBANDS + bi) * nch + ci; - let next = (xi * NBANDS + (bi + 1)) * nch + ci; - xb[cur] += 0.03 * xb[next]; - } - } - - // Temporal masking: -3 dB/2.5 ms slope. - if xi > 0 { - for bi in 0..NBANDS { - for ci in 0..nch { - let cur = (xi * NBANDS + bi) * nch + ci; - let prev = ((xi - 1) * NBANDS + bi) * nch + ci; - xb[cur] += 0.5 * xb[prev]; - } - } - } - - // Allowing some cross-talk. - if nch == 2 { - for bi in 0..NBANDS { - let l = xb[(xi * NBANDS + bi) * nch + 0]; - let r = xb[(xi * NBANDS + bi) * nch + 1]; - xb[(xi * NBANDS + bi) * nch + 0] += 0.01 * r; - xb[(xi * NBANDS + bi) * nch + 1] += 0.01 * l; - } - } - - // Apply masking to spectral energies. - for bi in 0..ybands { - for xj in BANDS[bi]..BANDS[bi + 1] { - for ci in 0..nch { - x_ps[(xi * NFREQS + xj) * nch + ci] += 0.1 * xb[(xi * NBANDS + bi) * nch + ci]; - y_ps[(xi * yfreqs + xj) * nch + ci] += 0.1 * xb[(xi * NBANDS + bi) * nch + ci]; - } - } - } - } - - // Average consecutive frames (adds previous frame). - for bi in 0..ybands { - for xj in BANDS[bi]..BANDS[bi + 1] { - for ci in 0..nch { - let mut xtmp = x_ps[(0 * NFREQS + xj) * nch + ci]; - let mut ytmp = y_ps[(0 * yfreqs + xj) * nch + ci]; - for xi in 1..nframes { - let xtmp2 = x_ps[(xi * NFREQS + xj) * nch + ci]; - let ytmp2 = y_ps[(xi * yfreqs + xj) * nch + ci]; - x_ps[(xi * NFREQS + xj) * nch + ci] += xtmp; - y_ps[(xi * yfreqs + xj) * nch + ci] += ytmp; - xtmp = xtmp2; - ytmp = ytmp2; - } - } - } - } - - // Max frequency bin to compare (see opus_compare.c). - let max_compare = if rate_hz == 48_000 { - BANDS[NBANDS] - } else if rate_hz == 12_000 { - BANDS[ybands] - } else { - BANDS[ybands].saturating_sub(3) - }; - - // Error integration. - let mut err: f64 = 0.0; - for xi in 0..nframes { - let mut ef: f64 = 0.0; - for bi in 0..ybands { - let mut eb: f64 = 0.0; - for xj in BANDS[bi]..BANDS[bi + 1] { - if xj >= max_compare { - break; - } - for ci in 0..nch { - let re = - y_ps[(xi * yfreqs + xj) * nch + ci] / x_ps[(xi * NFREQS + xj) * nch + ci]; - let mut im = re - re.ln() - 1.0; - if (79..=81).contains(&xj) { - im *= 0.1; - } - if xj == 80 { - im *= 0.1; - } - eb += im as f64; - } - } - eb /= ((BANDS[bi + 1] - BANDS[bi]) * nch) as f64; - ef += eb * eb; - } - ef /= NBANDS as f64; - ef *= ef; - err += ef * ef; - } - - err = (err / nframes as f64).powf(1.0 / 16.0); - let q = 100.0 * (1.0 - 0.5 * (1.0 + err).ln() / (1.13f64).ln()); - - Ok(QualityResult { - quality_percent: q as f32, - internal_weighted_error: err, - }) -} - -fn band_energy( - mut out_bands: Option<&mut [f32]>, - ps: &mut [f32], - input: &[f32], - nchannels: usize, - nframes: usize, - window_sz: usize, - step: usize, - downsample: usize, - nbands: usize, - ps_freqs: usize, -) { - let mut window = vec![0f32; window_sz]; - let mut c = vec![0f32; window_sz]; - let mut s = vec![0f32; window_sz]; - let mut x = vec![0f32; nchannels * window_sz]; - - for xj in 0..window_sz { - window[xj] = - 0.5 - 0.5 * (2.0 * f32::consts::PI / (window_sz as f32 - 1.0) * xj as f32).cos(); - } - for xj in 0..window_sz { - c[xj] = (2.0 * f32::consts::PI / window_sz as f32 * xj as f32).cos(); - s[xj] = (2.0 * f32::consts::PI / window_sz as f32 * xj as f32).sin(); - } - - let ps_sz = window_sz / 2; - debug_assert_eq!(ps_freqs, ps_sz); - - for xi in 0..nframes { - for ci in 0..nchannels { - for xk in 0..window_sz { - x[ci * window_sz + xk] = window[xk] * input[(xi * step + xk) * nchannels + ci]; - } - } - - let mut xj = 0usize; - for bi in 0..nbands { - let mut p0 = 0f32; - let mut p1 = 0f32; - while xj < BANDS[bi + 1] { - for ci in 0..nchannels { - let mut ti = 0usize; - let mut re = 0f32; - let mut im = 0f32; - for xk in 0..window_sz { - re += c[ti] * x[ci * window_sz + xk]; - im -= s[ti] * x[ci * window_sz + xk]; - ti += xj; - if ti >= window_sz { - ti -= window_sz; - } - } - let ds = downsample as f32; - re *= ds; - im *= ds; - - let idx = (xi * ps_sz + xj) * nchannels + ci; - ps[idx] = re * re + im * im + 100_000.0; - if ci == 0 { - p0 += ps[idx]; - } else { - p1 += ps[idx]; - } - } - xj += 1; - } - - if let Some(out_bands) = out_bands.as_deref_mut() { - let denom = (BANDS[bi + 1] - BANDS[bi]) as f32; - out_bands[(xi * nbands + bi) * nchannels + 0] = p0 / denom; - if nchannels == 2 { - out_bands[(xi * nbands + bi) * nchannels + 1] = p1 / denom; - } - } - } - } -} diff --git a/vendor/opus-decoder/src/entropy.rs b/vendor/opus-decoder/src/entropy.rs deleted file mode 100644 index b7ad1b1f..00000000 --- a/vendor/opus-decoder/src/entropy.rs +++ /dev/null @@ -1,411 +0,0 @@ -//! Entropy (range) decoder used by Opus (CELT + SILK). -//! -//! Port of libopus CELT range decoder (`celt/entdec.c`, `celt/entcode.c`). -//! Decoder-only; we keep the structure/semantics close to the reference to -//! minimize bit-exactness risk. - -#![allow(dead_code)] - -use core::cmp; - -// Constants from `celt/mfrngcod.h`. -const EC_SYM_BITS: i32 = 8; -const EC_SYM_MAX: u32 = (1u32 << EC_SYM_BITS) - 1; -const EC_CODE_BITS: i32 = 32; -const EC_CODE_TOP: u32 = 1u32 << (EC_CODE_BITS - 1); -const EC_CODE_BOT: u32 = EC_CODE_TOP >> EC_SYM_BITS; -const EC_CODE_EXTRA: i32 = ((EC_CODE_BITS - 2) % EC_SYM_BITS) + 1; - -// Constants from `celt/entcode.h`. -const EC_UINT_BITS: i32 = 8; -const BITRES: i32 = 3; - -// `ec_window` in libopus is `opus_uint32` by default. -type EcWindow = u32; -const EC_WINDOW_SIZE: i32 = (core::mem::size_of::() as i32) * 8; - -#[inline] -fn ec_ilog(v: u32) -> i32 { - // Matches `EC_ILOG()` semantics: returns 0 when v==0, else floor(log2(v))+1. - if v == 0 { - 0 - } else { - 32 - v.leading_zeros() as i32 - } -} - -#[inline] -fn imul32(a: u32, b: u32) -> u32 { - a.wrapping_mul(b) -} - -#[inline] -fn celt_udiv(n: u32, d: u32) -> u32 { - // libopus has an optional div table optimization; `n/d` is bit-exact. - n / d -} - -#[derive(Debug, Clone)] -pub(crate) struct EcDec<'a> { - buf: &'a [u8], - storage: usize, - - // Raw bits live at the end of the buffer, read LSB-first. - end_offs: usize, - end_window: EcWindow, - nend_bits: i32, - - // Total whole bits "used" so far (range coder + raw bits). - nbits_total: i32, - - // Range-coded bytes are read from the start. - offs: usize, - - // Range decoder state. - rng: u32, - // Difference between top of current range and input value, minus 1. - val: u32, - // Saved normalization factor from `decode()`. - ext: u32, - // Buffered symbol awaiting normalization. - rem: i32, - - error: bool, -} - -impl<'a> EcDec<'a> { - pub fn new(buf: &'a [u8]) -> Self { - let storage = buf.len(); - - // Mirrors `ec_dec_init()`. - let mut st = Self { - buf, - storage, - end_offs: 0, - end_window: 0, - nend_bits: 0, - nbits_total: EC_CODE_BITS + 1 - - ((EC_CODE_BITS - EC_CODE_EXTRA) / EC_SYM_BITS) * EC_SYM_BITS, - offs: 0, - rng: 1u32 << EC_CODE_EXTRA, - val: 0, - ext: 0, - rem: 0, - error: false, - }; - - st.rem = st.read_byte() as i32; - st.val = st.rng - 1 - ((st.rem as u32) >> (EC_SYM_BITS - EC_CODE_EXTRA)); - st.normalize(); - - st - } - - #[inline] - pub fn tell(&self) -> i32 { - // Matches `ec_tell()`: slightly over-estimates. - self.nbits_total - ec_ilog(self.rng) - } - - #[inline] - pub fn tell_frac(&self) -> u32 { - // Port of fast `ec_tell_frac()` from `celt/entcode.c`. - const CORRECTION: [u32; 8] = [35733, 38967, 42495, 46340, 50535, 55109, 60097, 65535]; - - let nbits = (self.nbits_total as u32) << BITRES; - let mut l = ec_ilog(self.rng); - let r = self.rng >> (l - 16); - let mut b = ((r >> 12) - 8) as usize; - b += (r > CORRECTION[b]) as usize; - l = (l << 3) + b as i32; - nbits - (l as u32) - } - - #[inline] - pub fn final_range(&self) -> u32 { - // libopus exposes this via OPUS_GET_FINAL_RANGE. - self.rng - } - - /// Return active entropy storage in bytes. - /// - /// Params: none. - /// Returns: currently visible storage after any shrinking for raw bits. - #[inline] - pub fn storage(&self) -> usize { - self.storage - } - - /// Return the whole-bit counter tracked by the entropy decoder. - /// - /// Params: none. - /// Returns: `nbits_total`, excluding partial range-coder bits. - #[inline] - pub fn nbits_total(&self) -> i32 { - self.nbits_total - } - - /// Return current entropy decoder range state. - /// - /// Params: none. - /// Returns: internal `rng` value used for CELT noise/folding seed chaining. - pub fn rng(&self) -> u32 { - self.rng - } - - /// Return current entropy decoder `val` state. - /// - /// Params: none. - /// Returns: internal `val` register for debug comparisons. - pub fn debug_val(&self) -> u32 { - self.val - } - - #[inline] - pub fn is_error(&self) -> bool { - self.error - } - - /// Shrink visible decoder storage to hide trailing side data. - /// - /// Params: `bytes` number of bytes reserved at packet end. - /// Returns: nothing; the active storage window is clamped in place. - pub fn shrink_storage(&mut self, bytes: usize) { - self.storage = self.storage.saturating_sub(bytes); - self.end_offs = self.end_offs.min(self.storage); - if self.offs > self.storage { - self.offs = self.storage; - self.error = true; - } - } - - /// Debug accessor: number of raw-bit bytes consumed from packet end. - /// - /// Params: none. - /// Returns: `end_offs` value from entropy decoder state. - pub(crate) fn end_offs(&self) -> usize { - self.end_offs - } - - /// Debug accessor: number of currently buffered raw bits. - /// - /// Params: none. - /// Returns: `nend_bits` value from entropy decoder state. - pub(crate) fn nend_bits(&self) -> i32 { - self.nend_bits - } - - #[inline] - fn read_byte(&mut self) -> u8 { - if self.offs < self.storage { - let b = self.buf[self.offs]; - self.offs += 1; - b - } else { - 0 - } - } - - #[inline] - fn read_byte_from_end(&mut self) -> u8 { - if self.end_offs < self.storage { - self.end_offs += 1; - self.buf[self.storage - self.end_offs] - } else { - 0 - } - } - - #[inline] - fn normalize(&mut self) { - while self.rng <= EC_CODE_BOT { - self.nbits_total += EC_SYM_BITS; - self.rng <<= EC_SYM_BITS; - - // Use up remaining bits from last symbol. - let mut sym = self.rem as u32; - - // Read next symbol. - self.rem = self.read_byte() as i32; - - // Take the rest of the bits we need from this new symbol. - sym = (sym << EC_SYM_BITS | (self.rem as u32)) >> (EC_SYM_BITS - EC_CODE_EXTRA); - - // Subtract from val, capped to < EC_CODE_TOP. - self.val = ((self.val << EC_SYM_BITS) + (EC_SYM_MAX & !sym)) & (EC_CODE_TOP - 1); - } - } - - /// Equivalent of `ec_decode()`. - pub fn decode(&mut self, ft: u32) -> u32 { - self.ext = celt_udiv(self.rng, ft); - let s = self.val / self.ext; - ft - cmp::min(s + 1, ft) - } - - /// Equivalent of `ec_decode_bin()` with `ft == 1< u32 { - self.ext = self.rng >> bits; - let s = self.val / self.ext; - (1u32 << bits) - cmp::min(s + 1, 1u32 << bits) - } - - /// Equivalent of `ec_dec_update()`. - pub fn update(&mut self, fl: u32, fh: u32, ft: u32) { - let s = imul32(self.ext, ft - fh); - self.val = self.val.wrapping_sub(s); - self.rng = if fl > 0 { - imul32(self.ext, fh - fl) - } else { - self.rng.wrapping_sub(s) - }; - self.normalize(); - } - - /// Decode a bit that has a 1/(1< bool { - let r = self.rng; - let d = self.val; - let s = r >> logp; - let ret = d < s; - if !ret { - self.val = d - s; - } - self.rng = if ret { s } else { r - s }; - self.normalize(); - ret - } - - /// Decodes a symbol from an "inverse CDF" table (`ec_dec_icdf`). - pub fn dec_icdf(&mut self, icdf: &[u8], ftb: u32) -> i32 { - let s0 = self.rng; - let d = self.val; - let r = s0 >> ftb; - let mut ret: i32 = -1; - let mut s = s0; - let mut t; - loop { - t = s; - ret += 1; - let idx = ret as usize; - if idx >= icdf.len() { - self.error = true; - return icdf.len() as i32 - 1; - } - s = imul32(r, icdf[idx] as u32); - if d >= s { - break; - } - } - self.val = d - s; - self.rng = t - s; - self.normalize(); - ret - } - - /// Decodes a symbol from an "inverse CDF" table (`ec_dec_icdf16`). - pub fn dec_icdf16(&mut self, icdf: &[u16], ftb: u32) -> i32 { - let s0 = self.rng; - let d = self.val; - let r = s0 >> ftb; - let mut ret: i32 = -1; - let mut s = s0; - let mut t; - loop { - t = s; - ret += 1; - let idx = ret as usize; - if idx >= icdf.len() { - self.error = true; - return icdf.len() as i32 - 1; - } - s = imul32(r, icdf[idx] as u32); - if d >= s { - break; - } - } - self.val = d - s; - self.rng = t - s; - self.normalize(); - ret - } - - /// Extract a raw unsigned integer with a non-power-of-2 range (`ec_dec_uint`). - pub fn dec_uint(&mut self, ft_in: u32) -> u32 { - // Match libopus behavior: undefined for ft<=1; we treat it as error. - if ft_in <= 1 { - self.error = true; - return 0; - } - - let mut ftm1 = ft_in - 1; - let mut ftb = ec_ilog(ftm1); - if ftb > EC_UINT_BITS { - ftb -= EC_UINT_BITS; - let ft = (ftm1 >> ftb) as u32 + 1; - let s = self.decode(ft); - self.update(s, s + 1, ft); - let t = (s << ftb) | self.dec_bits(ftb as u32); - if t <= ftm1 { - t - } else { - self.error = true; - ftm1 - } - } else { - ftm1 += 1; - let s = self.decode(ftm1); - self.update(s, s + 1, ftm1); - s - } - } - - /// Extract a sequence of raw bits from the stream (`ec_dec_bits`). - pub fn dec_bits(&mut self, bits: u32) -> u32 { - debug_assert!(bits <= 25); - - let mut window = self.end_window; - let mut available = self.nend_bits; - if available < bits as i32 { - loop { - window |= (self.read_byte_from_end() as EcWindow) << available; - available += EC_SYM_BITS; - if available > EC_WINDOW_SIZE - EC_SYM_BITS { - break; - } - } - } - let ret = window & ((1u32 << bits) - 1); - window >>= bits; - available -= bits as i32; - self.end_window = window; - self.nend_bits = available; - self.nbits_total += bits as i32; - ret - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn ilog_matches_expected() { - assert_eq!(ec_ilog(0), 0); - assert_eq!(ec_ilog(1), 1); - assert_eq!(ec_ilog(2), 2); - assert_eq!(ec_ilog(3), 2); - assert_eq!(ec_ilog(4), 3); - assert_eq!(ec_ilog(0xFFFF_FFFF), 32); - } - - #[test] - fn tell_frac_monotonicish() { - // Sanity: consuming bits should increase the reported usage. - let mut d = EcDec::new(&[0u8; 64]); - let a = d.tell_frac(); - let _ = d.dec_bits(8); - let b = d.tell_frac(); - assert!(b >= a); - } -} diff --git a/vendor/opus-decoder/src/error.rs b/vendor/opus-decoder/src/error.rs deleted file mode 100644 index 34fd8114..00000000 --- a/vendor/opus-decoder/src/error.rs +++ /dev/null @@ -1,30 +0,0 @@ -use core::fmt; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Error { - InvalidSampleRate(u32), - InvalidChannels(u8), - PacketTooLarge { max: usize, got: usize }, - OutputTooSmall { needed: usize, got: usize }, - BadPacket, - NotImplemented, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::InvalidSampleRate(fs) => write!(f, "invalid Opus output sample rate: {fs}"), - Error::InvalidChannels(ch) => write!(f, "invalid Opus channel count: {ch}"), - Error::PacketTooLarge { max, got } => { - write!(f, "Opus packet too large (max {max}, got {got})") - } - Error::OutputTooSmall { needed, got } => { - write!(f, "output buffer too small: needed {needed}, got {got}") - } - Error::BadPacket => write!(f, "invalid Opus packet"), - Error::NotImplemented => write!(f, "not implemented"), - } - } -} - -impl std::error::Error for Error {} diff --git a/vendor/opus-decoder/src/lib.rs b/vendor/opus-decoder/src/lib.rs deleted file mode 100644 index 904e4536..00000000 --- a/vendor/opus-decoder/src/lib.rs +++ /dev/null @@ -1,884 +0,0 @@ -//! Public API for the pure Rust Opus decoder port. -//! -//! The crate keeps the CELT and SILK internals close to libopus while exposing -//! stable packet-to-PCM decoding entry points. - -#![forbid(unsafe_code)] -#![allow( - clippy::erasing_op, - clippy::identity_op, - clippy::precedence, - clippy::int_plus_one -)] - -macro_rules! debug_trace { - ($($arg:tt)*) => {}; -} - -mod celt; -pub(crate) mod compare; -mod entropy; -mod error; -mod multistream; -mod packet; -mod silk; -use crate::entropy::EcDec; -use core::sync::atomic::{AtomicUsize, Ordering}; - -pub(crate) use error::Error; -pub use multistream::OpusMultistreamDecoder; - -static TRACE_DECODE_PACKET_IDX: AtomicUsize = AtomicUsize::new(0); - -/// High-level single-stream Opus decoder. -/// -/// This wrapper exposes the stable public API for packet-to-PCM decoding while -/// keeping CELT and SILK state management internal to the crate. -pub struct OpusDecoder { - decoder: Decoder, - float_scratch: Vec, - loss_count: u32, - last_packet_duration: usize, -} - -impl OpusDecoder { - /// Maximum decoded frame size per channel at 48 kHz. - pub const MAX_FRAME_SIZE_48K: usize = Decoder::MAX_FRAME_SIZE_48K; - - /// Create a new decoder. - /// - /// `sample_rate` must be `8000`, `12000`, `16000`, `24000`, or `48000`, - /// and `channels` must be `1` or `2`. - /// - /// # Examples - /// - /// ```rust - /// use opus_decoder::OpusDecoder; - /// - /// let decoder = OpusDecoder::new(48_000, 2)?; - /// # let _ = decoder; - /// # Ok::<(), opus_decoder::OpusError>(()) - /// ``` - pub fn new(sample_rate: u32, channels: usize) -> Result { - let channels = - u8::try_from(channels).map_err(|_| OpusError::InvalidArgument("channels"))?; - let decoder = Decoder::new(sample_rate, channels).map_err(OpusError::from)?; - - Ok(Self { - decoder, - float_scratch: Vec::new(), - loss_count: 0, - last_packet_duration: 0, - }) - } - - /// Return the maximum decoded frame size per channel for this output rate. - pub fn max_frame_size_per_channel(&self) -> usize { - self.decoder.max_frame_size_per_channel() - } - - /// Mirror internal PLC bookkeeping into the public wrapper. - /// - /// Parameters: none. - /// Returns: nothing. - fn sync_state_from_decoder(&mut self) { - self.loss_count = self.decoder.loss_count; - self.last_packet_duration = self.decoder.last_packet_duration; - } - - /// Decode a packet into 16-bit PCM samples (interleaved if stereo). - /// - /// Empty `packet` input triggers packet loss concealment using the previous - /// decoder state. Returns the number of decoded samples per channel. - /// - `fec`: reserved for future in-band FEC support. Currently treated as - /// packet loss concealment (PLC) when `true`. Pass `false` for normal decode. - /// - /// # Examples - /// - /// ```rust,no_run - /// use opus_decoder::OpusDecoder; - /// - /// let mut decoder = OpusDecoder::new(48_000, 2)?; - /// let packet = std::fs::read("frame.opus")?; - /// let mut pcm = vec![0i16; 960 * 2]; - /// let samples = decoder.decode(&packet, &mut pcm, false)?; - /// # let _ = samples; - /// # Ok::<(), Box>(()) - /// ``` - pub fn decode( - &mut self, - packet: &[u8], - pcm: &mut [i16], - fec: bool, - ) -> Result { - let samples_per_channel = if packet.is_empty() || fec { - self.decoder.decode(None, pcm).map_err(OpusError::from)? - } else { - self.decoder - .decode(Some(packet), pcm) - .map_err(OpusError::from)? - }; - self.sync_state_from_decoder(); - Ok(samples_per_channel) - } - - /// Decode a packet into f32 PCM samples (interleaved if stereo). - /// - /// Empty `packet` input triggers packet loss concealment using the previous - /// decoder state. Returns the number of decoded samples per channel. - /// - `fec`: reserved for future in-band FEC support. Currently treated as - /// packet loss concealment (PLC) when `true`. Pass `false` for normal decode. - /// - /// # Examples - /// - /// ```rust,no_run - /// use opus_decoder::OpusDecoder; - /// - /// let mut decoder = OpusDecoder::new(48_000, 1)?; - /// let packet = std::fs::read("frame.opus")?; - /// let mut pcm = vec![0.0f32; 960]; - /// let samples = decoder.decode_float(&packet, &mut pcm, false)?; - /// # let _ = samples; - /// # Ok::<(), Box>(()) - /// ``` - pub fn decode_float( - &mut self, - packet: &[u8], - pcm: &mut [f32], - fec: bool, - ) -> Result { - let samples_per_channel_hint = if packet.is_empty() || fec { - self.decoder.last_packet_duration - } else { - packet::parse_packet(packet) - .map_err(OpusError::from)? - .samples_per_channel(self.decoder.fs_hz()) - }; - let needed = samples_per_channel_hint * self.decoder.channels() as usize; - if pcm.len() < needed { - return Err(OpusError::BufferTooSmall); - } - - if self.float_scratch.len() < needed { - self.float_scratch.resize(needed, 0); - } - - let samples_per_channel = self - .decoder - .decode( - if packet.is_empty() || fec { - None - } else { - Some(packet) - }, - &mut self.float_scratch[..needed], - ) - .map_err(OpusError::from)?; - self.sync_state_from_decoder(); - let written = samples_per_channel * self.decoder.channels() as usize; - for (dst, src) in pcm.iter_mut().zip(self.float_scratch[..written].iter()) { - *dst = f32::from(*src) / 32768.0; - } - - Ok(samples_per_channel) - } - - /// Reset decoder state (e.g. after packet loss or seek). - /// - /// Parameters: none. - /// Returns: nothing. - pub fn reset(&mut self) { - self.decoder.reset(); - self.loss_count = 0; - self.last_packet_duration = 0; - } - - /// Return the last range-coder final state observed by the decoder. - pub fn final_range(&self) -> u32 { - self.decoder.final_range() - } - - /// Return the last CELT recursive split count. - #[doc(hidden)] - pub fn last_split_count(&self) -> usize { - self.decoder.last_split_count() - } - - /// Return the current deemphasis memory for channel 0. - #[doc(hidden)] - pub fn deemph_mem(&self) -> f32 { - self.decoder.deemph_mem() - } - - /// Return whether the last CELT frame used the transient path. - #[doc(hidden)] - pub fn last_is_transient(&self) -> bool { - self.decoder.last_is_transient() - } - - /// Return whether the last decoded packet carried SILK redundancy. - #[doc(hidden)] - pub fn last_had_redundancy(&self) -> bool { - self.decoder.last_had_redundancy() - } - - /// Return whether the last decoded SILK redundancy was CELT-to-SILK. - #[doc(hidden)] - pub fn last_celt_to_silk(&self) -> bool { - self.decoder.last_celt_to_silk() - } -} - -/// Errors returned by the public Opus decoding API. -#[derive(Debug, thiserror::Error)] -pub enum OpusError { - /// The provided Opus packet is malformed or internally inconsistent. - #[error("invalid packet")] - InvalidPacket, - /// The decoder hit an internal unsupported or unexpected state. - #[error("internal error")] - InternalError, - /// The output PCM buffer is too small for the decoded frame. - #[error("buffer too small")] - BufferTooSmall, - /// One of the public API arguments is invalid. - #[error("invalid argument: {0}")] - InvalidArgument(&'static str), -} - -impl From for OpusError { - fn from(value: Error) -> Self { - match value { - Error::InvalidSampleRate(_) => Self::InvalidArgument("sample_rate"), - Error::InvalidChannels(_) => Self::InvalidArgument("channels"), - Error::PacketTooLarge { .. } | Error::BadPacket => Self::InvalidPacket, - Error::OutputTooSmall { .. } => Self::BufferTooSmall, - Error::NotImplemented => Self::InternalError, - } - } -} - -#[derive(Debug, Clone)] -pub(crate) struct Decoder { - fs_hz: u32, - channels: u8, - celt: celt::CeltDecoder, - silk: silk::SilkDecoder, - last_packet_split_count: usize, - last_final_range: u32, - last_had_redundancy: bool, - last_celt_to_silk: bool, - prev_mode: Option, - prev_redundancy: bool, - loss_count: u32, - last_packet_duration: usize, - last_output: Vec, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum OpusMode { - SilkOnly, - Hybrid, - CeltOnly, -} - -impl Decoder { - pub(crate) const MAX_FRAME_SIZE_48K: usize = 5760; // 120 ms @ 48 kHz - - pub(crate) fn new(fs_hz: u32, channels: u8) -> Result { - if !matches!(fs_hz, 8000 | 12000 | 16000 | 24000 | 48000) { - return Err(Error::InvalidSampleRate(fs_hz)); - } - if !matches!(channels, 1 | 2) { - return Err(Error::InvalidChannels(channels)); - } - Ok(Self { - fs_hz, - channels, - celt: celt::CeltDecoder::new(fs_hz, channels), - silk: silk::SilkDecoder::new(fs_hz, channels), - last_packet_split_count: 0, - last_final_range: 0, - last_had_redundancy: false, - last_celt_to_silk: false, - prev_mode: None, - prev_redundancy: false, - loss_count: 0, - last_packet_duration: 0, - last_output: Vec::new(), - }) - } - - pub(crate) fn fs_hz(&self) -> u32 { - self.fs_hz - } - - pub(crate) fn channels(&self) -> u8 { - self.channels - } - - pub(crate) fn max_frame_size_per_channel(&self) -> usize { - // Opus internally decodes 48 kHz and can output other Fs by deterministic - // downsampling/decimation (libopus behavior). - match self.fs_hz { - 8000 => 960, - 12000 => 1440, - 16000 => 1920, - 24000 => 2880, - 48000 => Self::MAX_FRAME_SIZE_48K, - _ => 0, // guarded in new() - } - } - - /// Conceal a lost non-CELT frame by fading the previous PCM. - /// - /// Params: per-channel `frame_size`, consecutive `loss_count`, and mutable `out`. - /// Returns: nothing; `out` receives interleaved concealed PCM. - fn conceal_with_fade(&self, frame_size: usize, loss_count: u32, out: &mut [i16]) { - let channels = self.channels as usize; - let needed = frame_size * channels; - if self.last_output.len() < needed { - out[..needed].fill(0); - return; - } - - let fade = 0.9f32.powi((loss_count.min(10) + 1) as i32); - for (dst, src) in out[..needed] - .iter_mut() - .zip(self.last_output[..needed].iter()) - { - let sample = f32::from(*src) * fade; - *dst = sample.round().clamp(i16::MIN as f32, i16::MAX as f32) as i16; - } - } - - /// Persist the most recent decoded interleaved PCM frame. - /// - /// Params: decoded `out` buffer and `samples_per_channel` written into it. - /// Returns: nothing; the decoder keeps a copy for future PLC fallback. - fn store_last_output(&mut self, out: &[i16], samples_per_channel: usize) { - let written = samples_per_channel * self.channels as usize; - self.last_output.clear(); - self.last_output.extend_from_slice(&out[..written]); - } - - /// Decode a lost packet using CELT PLC or a safe fade fallback. - /// - /// Params: mutable interleaved `out` buffer. - /// Returns: concealed sample count per channel. - fn decode_lost_packet(&mut self, out: &mut [i16]) -> Result { - let samples_per_channel = self.last_packet_duration; - if samples_per_channel == 0 { - self.last_final_range = 0; - return Ok(0); - } - - let needed = samples_per_channel * self.channels as usize; - if out.len() < needed { - return Err(Error::OutputTooSmall { - needed, - got: out.len(), - }); - } - - out[..needed].fill(0); - match self.prev_mode { - Some(OpusMode::CeltOnly) => { - let concealed = self - .celt - .decode_lost(samples_per_channel, self.channels as usize); - for (dst, src) in out[..needed].iter_mut().zip(concealed.iter()) { - *dst = src.round().clamp(i16::MIN as f32, i16::MAX as f32) as i16; - } - } - Some(OpusMode::SilkOnly | OpusMode::Hybrid) => { - self.silk - .decode_lost(samples_per_channel, out, self.loss_count)?; - } - _ => { - self.conceal_with_fade(samples_per_channel, self.loss_count, out); - } - } - - self.loss_count = self.loss_count.saturating_add(1); - self.last_packet_split_count = 0; - self.last_final_range = 0; - self.last_had_redundancy = false; - self.last_celt_to_silk = false; - self.prev_redundancy = false; - self.store_last_output(out, samples_per_channel); - Ok(samples_per_channel) - } - - pub(crate) fn reset(&mut self) { - // Placeholder. The real implementation will reset SILK/CELT state. - self.celt.reset(); - self.silk.reset(); - self.last_packet_split_count = 0; - self.last_final_range = 0; - self.last_had_redundancy = false; - self.last_celt_to_silk = false; - self.prev_mode = None; - self.prev_redundancy = false; - self.loss_count = 0; - self.last_packet_duration = 0; - self.last_output.clear(); - } - - /// Return range coder final state for conformance comparison. - pub(crate) fn final_range(&self) -> u32 { - self.last_final_range - } - - /// Return recursive split count from last decoded frame. - /// - /// This is a debug metric used for CELT split-path diagnostics. - pub(crate) fn last_split_count(&self) -> usize { - self.last_packet_split_count - } - - /// Return deemphasis memory for channel 0 (debug metric). - /// - /// This is used for CELT synthesis diagnostics in mono vectors. - pub(crate) fn deemph_mem(&self) -> f32 { - self.celt.deemph_mem(0) - } - - /// Return whether the last CELT frame was transient. - /// - /// This is a compatibility helper for the local conformance harness. - pub(crate) fn last_is_transient(&self) -> bool { - false - } - - /// Return whether the last decoded packet carried SILK redundancy. - /// - /// This follows the top-level transition bookkeeping used by the decoder. - pub(crate) fn last_had_redundancy(&self) -> bool { - self.last_had_redundancy - } - - /// Return whether the last decoded SILK redundancy was CELT-to-SILK. - /// - /// This is a debug helper for transition tracing in the conformance harness. - pub(crate) fn last_celt_to_silk(&self) -> bool { - self.last_celt_to_silk - } - - /// Decode one Opus packet to interleaved i16 PCM. - /// - /// - `packet=None` triggers PLC (packet loss concealment). - /// - `out` must be large enough for the maximum frame size (120 ms). - /// - Returns the number of samples per channel written. - pub(crate) fn decode( - &mut self, - packet: Option<&[u8]>, - out: &mut [i16], - ) -> Result { - let packet_idx = TRACE_DECODE_PACKET_IDX.fetch_add(1, Ordering::SeqCst); - let (toc, samples_per_channel_needed) = match packet { - Some(packet) => { - let pp = packet::parse_packet(packet)?; - (Some(pp.toc), pp.samples_per_channel(self.fs_hz)) - } - None => (None, self.last_packet_duration), - }; - - let needed = samples_per_channel_needed * self.channels as usize; - if out.len() < needed { - return Err(Error::OutputTooSmall { - needed, - got: out.len(), - }); - } - - let Some(toc) = toc else { - return self.decode_lost_packet(out); - }; - - self.celt.reset_loss_count(); - let config = (toc >> 3) & 0x1f; - let mode = match config { - 0..=11 => OpusMode::SilkOnly, - 12..=15 => OpusMode::Hybrid, - 16..=31 => OpusMode::CeltOnly, - _ => unreachable!(), - }; - // Parse again to get per-frame slices. - let pp = packet::parse_packet(packet.unwrap())?; - self.last_packet_split_count = 0; - self.last_final_range = 0; - - let transition = self.prev_mode.is_some() - && ((mode == OpusMode::CeltOnly - && self.prev_mode != Some(OpusMode::CeltOnly) - && !self.prev_redundancy) - || (mode != OpusMode::CeltOnly && self.prev_mode == Some(OpusMode::CeltOnly))); - let transition_samples = (self.fs_hz as usize) / 200; - let transition_overlap = (self.fs_hz as usize) / 400; - let channels = self.channels as usize; - let celt_transition = if transition && mode == OpusMode::CeltOnly { - vec![0i16; transition_samples * channels] - } else { - Vec::new() - }; - let mut apply_celt_transition = transition && mode == OpusMode::CeltOnly; - let mut reset_silk = transition && self.prev_mode == Some(OpusMode::CeltOnly); - let mut had_redundancy = false; - let mut last_celt_to_silk = false; - let mut written_per_channel = 0usize; - for &frame in pp.frames().iter() { - let frame_samples_48k = pp.samples_per_frame_48k; - let out_frame = &mut out[written_per_channel * self.channels as usize..]; - match mode { - OpusMode::CeltOnly => { - if apply_celt_transition { - self.celt.reset(); - } - // CELT frame sizes are specified in 48 kHz samples; this is enough - // to drive the CELT-side LM selection. - let mut ec = EcDec::new(frame); - let celt_frame = self.celt.decode_frame_with_ec( - frame, - &mut ec, - frame_samples_48k, - config, - pp.packet_channels, - packet_idx, - out_frame, - false, - )?; - if apply_celt_transition { - apply_transition_fade_i16( - &celt_transition, - &mut out_frame[..celt_frame.samples_per_channel * channels], - transition_overlap.min(celt_frame.samples_per_channel / 2), - channels, - self.celt.window(), - self.fs_hz, - ); - apply_celt_transition = false; - } - self.last_packet_split_count += self.celt.last_split_count(); - self.last_final_range = self.celt.final_range(); - written_per_channel += celt_frame.samples_per_channel; - } - OpusMode::SilkOnly => { - if reset_silk { - self.silk.reset(); - reset_silk = false; - } - let packet_frame = frame; - let silk_frame = self.silk.decode_frame( - packet_frame, - frame_samples_48k, - config, - pp.packet_channels, - packet_idx, - out_frame, - )?; - last_celt_to_silk = silk_frame.celt_to_silk; - let mut redundancy_rng = 0u32; - if silk_frame.consumed_redundancy { - let redundancy_data = - &packet_frame[packet_frame.len() - silk_frame.redundancy_bytes..]; - let redundancy_frame_size_48k = 240usize; - let redundancy_samples = (self.fs_hz as usize) / 200; - let redundancy_end_band = silk_redundancy_end_band(config); - let mut redundancy_out = - vec![0i16; redundancy_samples * self.channels as usize]; - if !silk_frame.celt_to_silk { - self.celt.reset(); - } - self.celt.set_start_band(0); - self.celt.set_end_band(redundancy_end_band); - let redundancy_frame = self.celt.decode_frame( - redundancy_data, - redundancy_frame_size_48k, - config, - pp.packet_channels, - packet_idx, - &mut redundancy_out, - )?; - self.celt.clear_end_band(); - redundancy_rng = self.celt.final_range(); - if !silk_frame.celt_to_silk { - let overlap = (self.fs_hz as usize) / 400; - let channels = self.channels as usize; - let silk_tail_start = - (silk_frame.samples_per_channel - overlap) * channels; - let silk_tail_end = silk_frame.samples_per_channel * channels; - let redundancy_start = overlap * channels; - let redundancy_end = redundancy_start + overlap * channels; - let silk_tail = out_frame[silk_tail_start..silk_tail_end].to_vec(); - smooth_fade_i16( - &silk_tail, - &redundancy_out[redundancy_start..redundancy_end], - &mut out_frame[silk_tail_start..silk_tail_end], - overlap, - channels, - self.celt.window(), - self.fs_hz, - ); - } else { - let overlap = (self.fs_hz as usize) / 400; - let channels = self.channels as usize; - let frame_len = silk_frame.samples_per_channel * channels; - apply_transition_fade_i16( - &redundancy_out, - &mut out_frame[..frame_len], - overlap.min(redundancy_frame.samples_per_channel / 2), - channels, - self.celt.window(), - self.fs_hz, - ); - } - } - had_redundancy = silk_frame.consumed_redundancy && !silk_frame.celt_to_silk; - self.last_final_range = self.silk.final_range() ^ redundancy_rng; - written_per_channel += silk_frame.samples_per_channel; - } - OpusMode::Hybrid => { - let mut ec = EcDec::new(frame); - let silk_frame = self.silk.decode_frame_with_ec( - frame, - &mut ec, - frame_samples_48k, - config, - pp.packet_channels, - true, - packet_idx, - out_frame, - )?; - let mut redundancy = if ec.tell() + 17 + 20 <= (frame.len() as i32) * 8 { - ec.dec_bit_logp(12) - } else { - false - }; - let mut celt_to_silk = false; - let mut redundancy_bytes = 0usize; - if redundancy { - celt_to_silk = ec.dec_bit_logp(1); - redundancy_bytes = ec.dec_uint(256) as usize + 2; - // NoSlacking: libopus's sanity check (opus_decoder.c, - // "len*8 < ec_tell"): a length the packet can't hold - // means no redundancy, never a slice out of range. - let left = frame.len().checked_sub(redundancy_bytes); - if left.is_none_or(|left| (left as i64) * 8 < i64::from(ec.tell())) { - redundancy = false; - celt_to_silk = false; - redundancy_bytes = 0; - } else { - ec.shrink_storage(redundancy_bytes); - } - } - let mut celt_to_silk_audio = Vec::new(); - let mut celt_to_silk_samples = 0usize; - let mut redundant_rng = 0u32; - let apply_celt_to_silk_audio = redundancy - && celt_to_silk - && (self.prev_mode != Some(OpusMode::SilkOnly) || self.prev_redundancy); - let reset_main_celt = self.prev_mode.is_some() - && self.prev_mode != Some(mode) - && !self.prev_redundancy; - if redundancy && celt_to_silk { - let redundancy_samples = (self.fs_hz as usize) / 200; - let redundancy_data = &frame[frame.len() - redundancy_bytes..]; - self.celt.set_start_band(0); - celt_to_silk_audio = vec![0i16; redundancy_samples * channels]; - let redundancy_frame = self.celt.decode_frame( - redundancy_data, - 240, - config, - pp.packet_channels, - packet_idx, - &mut celt_to_silk_audio, - )?; - self.last_packet_split_count += self.celt.last_split_count(); - celt_to_silk_samples = redundancy_frame.samples_per_channel; - redundant_rng = self.celt.final_range(); - } - if reset_main_celt { - self.celt.reset(); - } - self.celt.set_start_band(17); - let celt_frame = match self.celt.decode_frame_with_ec( - frame, - &mut ec, - frame_samples_48k, - config, - pp.packet_channels, - packet_idx, - out_frame, - true, - ) { - Ok(frame) => frame, - Err(err) => { - self.celt.set_start_band(0); - return Err(err); - } - }; - self.celt.set_start_band(0); - debug_assert_eq!( - silk_frame.samples_per_channel, - celt_frame.samples_per_channel - ); - self.last_packet_split_count += self.celt.last_split_count(); - let main_celt_rng = self.celt.final_range(); - self.last_final_range = main_celt_rng; - if redundancy && !celt_to_silk { - let channels = self.channels as usize; - let redundancy_samples = (self.fs_hz as usize) / 200; - let overlap = (self.fs_hz as usize) / 400; - let frame_len = celt_frame.samples_per_channel * channels; - let redundancy_data = &frame[frame.len() - redundancy_bytes..]; - let mut redundancy_out = vec![0i16; redundancy_samples * channels]; - self.celt.reset(); - self.celt.set_start_band(0); - let redundancy_frame = self.celt.decode_frame( - redundancy_data, - 240, - config, - pp.packet_channels, - packet_idx, - &mut redundancy_out, - )?; - self.last_packet_split_count += self.celt.last_split_count(); - let redundant_rng = self.celt.final_range(); - let fade_len = overlap * channels; - let tail_start = frame_len.saturating_sub(fade_len); - let tail_end = tail_start + fade_len; - let redundancy_start = fade_len; - let redundancy_end = redundancy_start + fade_len; - if tail_end <= out_frame.len() - && redundancy_end <= redundancy_out.len() - && redundancy_frame.samples_per_channel == redundancy_samples - { - let celt_tail = out_frame[tail_start..tail_end].to_vec(); - smooth_fade_i16( - &celt_tail, - &redundancy_out[redundancy_start..redundancy_end], - &mut out_frame[tail_start..tail_end], - overlap, - channels, - self.celt.window(), - self.fs_hz, - ); - } - self.last_final_range ^= redundant_rng; - } - if redundancy && celt_to_silk { - if apply_celt_to_silk_audio { - let overlap = (self.fs_hz as usize) / 400; - let frame_len = celt_frame.samples_per_channel * channels; - apply_transition_fade_i16( - &celt_to_silk_audio, - &mut out_frame[..frame_len], - overlap.min(celt_to_silk_samples / 2), - channels, - self.celt.window(), - self.fs_hz, - ); - } - self.last_final_range ^= redundant_rng; - } - last_celt_to_silk = celt_to_silk; - had_redundancy = redundancy && !celt_to_silk; - written_per_channel += silk_frame.samples_per_channel; - } - } - } - - self.prev_mode = Some(mode); - self.last_had_redundancy = had_redundancy; - self.last_celt_to_silk = last_celt_to_silk; - self.prev_redundancy = had_redundancy; - self.loss_count = 0; - self.last_packet_duration = written_per_channel; - self.store_last_output(out, written_per_channel); - Ok(written_per_channel) - } -} - -/// Crossfade SILK PCM with redundant CELT PCM using the CELT overlap window. -/// -/// Params: previous `in1`, incoming `in2`, mutable `out`, overlap length, -/// interleaved `channels`, CELT `window`, and output sampling rate `fs_hz`. -/// Returns: nothing; `out` is updated in-place. -fn smooth_fade_i16( - in1: &[i16], - in2: &[i16], - out: &mut [i16], - overlap: usize, - channels: usize, - window: &[f32], - fs_hz: u32, -) { - let inc = (48_000 / fs_hz) as usize; - for c in 0..channels { - for i in 0..overlap { - let w = window[i * inc] * window[i * inc]; - let idx = i * channels + c; - let mixed = w * in2[idx] as f32 + (1.0 - w) * in1[idx] as f32; - out[idx] = mixed.round().clamp(i16::MIN as f32, i16::MAX as f32) as i16; - } - } -} - -/// Apply the SILK-to-CELT transition prefix and crossfade. -/// -/// Params: previous-mode `transition` PCM, mutable decoded `pcm`, fade length -/// `overlap`, interleaved `channels`, CELT `window`, and output rate `fs_hz`. -/// Returns: nothing; `pcm` is updated in-place. -fn apply_transition_fade_i16( - transition: &[i16], - pcm: &mut [i16], - overlap: usize, - channels: usize, - window: &[f32], - fs_hz: u32, -) { - if overlap == 0 || channels == 0 { - return; - } - - let prefix_len = overlap * channels; - let copy_len = prefix_len.min(transition.len()).min(pcm.len()); - pcm[..copy_len].copy_from_slice(&transition[..copy_len]); - - let fade_available = (transition.len().saturating_sub(prefix_len)) - .min(pcm.len().saturating_sub(prefix_len)) - / channels; - if fade_available == 0 { - return; - } - - let fade_samples = fade_available.min(overlap); - let fade_len = fade_samples * channels; - let fade_start = prefix_len; - let fade_end = fade_start + fade_len; - let incoming = pcm[fade_start..fade_end].to_vec(); - smooth_fade_i16( - &transition[fade_start..fade_end], - &incoming, - &mut pcm[fade_start..fade_end], - fade_samples, - channels, - window, - fs_hz, - ); -} - -/// Map SILK packet config to CELT redundancy end band. -/// -/// Params: Opus TOC `config`. -/// Returns: exclusive CELT end band matching libopus packet bandwidth. -fn silk_redundancy_end_band(config: u8) -> usize { - match config { - 0..=3 => 13, - 4..=11 => 17, - 12..=13 => 19, - 14..=15 => 21, - _ => 21, - } -} diff --git a/vendor/opus-decoder/src/multistream.rs b/vendor/opus-decoder/src/multistream.rs deleted file mode 100644 index 4c196dce..00000000 --- a/vendor/opus-decoder/src/multistream.rs +++ /dev/null @@ -1,452 +0,0 @@ -//! Opus multistream decoder — wraps multiple `OpusDecoder` instances -//! and demultiplexes channel mapping per RFC 7845 §5.1. -//! -//! Mirrors the wrapper logic from libopus `opus_multistream_decoder.c`. - -use crate::packet; -use crate::{OpusDecoder, OpusError}; - -/// Opus multistream decoder. -/// -/// Holds one `OpusDecoder` per stream. Coupled streams use stereo decoders and -/// remaining streams use mono decoders. -pub struct OpusMultistreamDecoder { - /// One decoder per stream. Coupled streams are stereo (2ch), rest mono. - decoders: Vec, - /// Total output channels (1–255). - nb_channels: usize, - /// Total streams in packet (coupled + mono). - nb_streams: usize, - /// Streams that are stereo-coupled (always the first `nb_coupled_streams`). - nb_coupled_streams: usize, - /// `mapping[output_channel] = index into interleaved decoder outputs`. - /// `255` means "silence this output channel". - mapping: Vec, - /// Output sample rate (same for all streams). - sample_rate: u32, -} - -impl OpusMultistreamDecoder { - /// Create a new multistream decoder. - /// - /// Parameters: `sample_rate`, total output `nb_channels`, `nb_streams`, - /// `nb_coupled_streams`, and per-output-channel `mapping`. - /// Returns: initialized multistream decoder on success. - /// - /// # Examples - /// - /// ```rust - /// use opus_decoder::OpusMultistreamDecoder; - /// - /// let decoder = OpusMultistreamDecoder::new(48_000, 2, 2, 0, &[0, 1])?; - /// # let _ = decoder; - /// # Ok::<(), opus_decoder::OpusError>(()) - /// ``` - pub fn new( - sample_rate: u32, - nb_channels: usize, - nb_streams: usize, - nb_coupled_streams: usize, - mapping: &[u8], - ) -> Result { - if nb_channels == 0 || nb_channels > 255 { - return Err(OpusError::InvalidArgument("nb_channels")); - } - if nb_streams == 0 { - return Err(OpusError::InvalidArgument("nb_streams")); - } - if nb_coupled_streams > nb_streams { - return Err(OpusError::InvalidArgument("nb_coupled_streams")); - } - if nb_streams > 255usize.saturating_sub(nb_coupled_streams) { - return Err(OpusError::InvalidArgument("nb_streams")); - } - if mapping.len() != nb_channels { - return Err(OpusError::InvalidArgument("mapping")); - } - - let total_decoded_channels = (2 * nb_coupled_streams) + (nb_streams - nb_coupled_streams); - for &slot in mapping { - if slot != 255 && usize::from(slot) >= total_decoded_channels { - return Err(OpusError::InvalidArgument("mapping")); - } - } - - let mut decoders = Vec::with_capacity(nb_streams); - for stream_idx in 0..nb_streams { - let channels = if stream_idx < nb_coupled_streams { - 2 - } else { - 1 - }; - decoders.push(OpusDecoder::new(sample_rate, channels)?); - } - - Ok(Self { - decoders, - nb_channels, - nb_streams, - nb_coupled_streams, - mapping: mapping.to_vec(), - sample_rate, - }) - } - - /// Decode a multistream packet into interleaved i16 PCM. - /// - /// Parameters: multistream `packet`, writable interleaved `pcm`, and `fec`. - /// - `fec`: reserved for future in-band FEC support. Currently treated as - /// packet loss concealment (PLC) when `true`. Pass `false` for normal decode. - /// - /// Returns: decoded samples per output channel. - /// - /// # Examples - /// - /// ```rust,no_run - /// use opus_decoder::OpusMultistreamDecoder; - /// - /// let mut decoder = OpusMultistreamDecoder::new(48_000, 2, 2, 0, &[0, 1])?; - /// let packet = std::fs::read("multistream-frame.opus")?; - /// let mut pcm = vec![0i16; 960 * 2]; - /// let samples = decoder.decode(&packet, &mut pcm, false)?; - /// # let _ = samples; - /// # Ok::<(), Box>(()) - /// ``` - pub fn decode( - &mut self, - packet: &[u8], - pcm: &mut [i16], - fec: bool, - ) -> Result { - let sub_packets = if packet.is_empty() || fec { - vec![&[][..]; self.nb_streams] - } else { - split_multistream_packet(packet, self.nb_streams)? - }; - let frame_size = self.expected_frame_size(&sub_packets, fec)?; - let needed = frame_size * self.nb_channels; - if pcm.len() < needed { - return Err(OpusError::BufferTooSmall); - } - if frame_size == 0 { - return Ok(0); - } - - let mut stream_pcm = Vec::with_capacity(self.nb_streams); - for (stream_idx, sub_packet) in sub_packets.iter().enumerate() { - let channels = self.stream_channels(stream_idx); - let mut buf = vec![0i16; frame_size * channels]; - let written = self.decoders[stream_idx].decode(sub_packet, &mut buf, fec)?; - if written != frame_size { - return Err(OpusError::InvalidPacket); - } - stream_pcm.push(buf); - } - - for (channel_idx, &slot) in self.mapping.iter().enumerate() { - if slot == 255 { - zero_output_channel_i16(pcm, self.nb_channels, channel_idx, frame_size); - continue; - } - - let (stream_idx, source_channel) = self.slot_to_stream_channel(usize::from(slot)); - copy_output_channel_i16( - pcm, - self.nb_channels, - channel_idx, - &stream_pcm[stream_idx], - self.stream_channels(stream_idx), - source_channel, - frame_size, - ); - } - - Ok(frame_size) - } - - /// Decode a multistream packet into interleaved f32 PCM. - /// - /// Parameters: multistream `packet`, writable interleaved `pcm`, and `fec`. - /// - `fec`: reserved for future in-band FEC support. Currently treated as - /// packet loss concealment (PLC) when `true`. Pass `false` for normal decode. - /// - /// Returns: decoded samples per output channel. - /// - /// # Examples - /// - /// ```rust,no_run - /// use opus_decoder::OpusMultistreamDecoder; - /// - /// let mut decoder = OpusMultistreamDecoder::new(48_000, 2, 2, 0, &[0, 1])?; - /// let packet = std::fs::read("multistream-frame.opus")?; - /// let mut pcm = vec![0.0f32; 960 * 2]; - /// let samples = decoder.decode_float(&packet, &mut pcm, false)?; - /// # let _ = samples; - /// # Ok::<(), Box>(()) - /// ``` - pub fn decode_float( - &mut self, - packet: &[u8], - pcm: &mut [f32], - fec: bool, - ) -> Result { - let sub_packets = if packet.is_empty() || fec { - vec![&[][..]; self.nb_streams] - } else { - split_multistream_packet(packet, self.nb_streams)? - }; - let frame_size = self.expected_frame_size(&sub_packets, fec)?; - let needed = frame_size * self.nb_channels; - if pcm.len() < needed { - return Err(OpusError::BufferTooSmall); - } - if frame_size == 0 { - return Ok(0); - } - - let mut stream_pcm = Vec::with_capacity(self.nb_streams); - for (stream_idx, sub_packet) in sub_packets.iter().enumerate() { - let channels = self.stream_channels(stream_idx); - let mut buf = vec![0.0f32; frame_size * channels]; - let written = self.decoders[stream_idx].decode_float(sub_packet, &mut buf, fec)?; - if written != frame_size { - return Err(OpusError::InvalidPacket); - } - stream_pcm.push(buf); - } - - for (channel_idx, &slot) in self.mapping.iter().enumerate() { - if slot == 255 { - zero_output_channel_f32(pcm, self.nb_channels, channel_idx, frame_size); - continue; - } - - let (stream_idx, source_channel) = self.slot_to_stream_channel(usize::from(slot)); - copy_output_channel_f32( - pcm, - self.nb_channels, - channel_idx, - &stream_pcm[stream_idx], - self.stream_channels(stream_idx), - source_channel, - frame_size, - ); - } - - Ok(frame_size) - } - - /// Reset all internal decoders. - /// - /// Parameters: none. - /// Returns: nothing. - pub fn reset(&mut self) { - for decoder in &mut self.decoders { - decoder.reset(); - } - } - - /// Compute the expected frame size for one multistream decode call. - /// - /// Parameters: per-stream `sub_packets` and FEC flag `fec`. - /// Returns: decoded samples per output channel. - fn expected_frame_size(&self, sub_packets: &[&[u8]], fec: bool) -> Result { - if fec || sub_packets.iter().all(|packet| packet.is_empty()) { - return Ok(self - .decoders - .first() - .map(|decoder| decoder.last_packet_duration) - .unwrap_or(0)); - } - - let mut frame_size = None; - for sub_packet in sub_packets.iter().filter(|packet| !packet.is_empty()) { - let samples = packet::parse_packet(sub_packet) - .map_err(OpusError::from)? - .samples_per_channel(self.sample_rate); - if let Some(expected) = frame_size { - if expected != samples { - return Err(OpusError::InvalidPacket); - } - } else { - frame_size = Some(samples); - } - } - - Ok(frame_size.unwrap_or(0)) - } - - /// Return the decoded channel count for one elementary stream. - /// - /// Parameters: `stream_idx` elementary stream index. - /// Returns: `2` for coupled streams and `1` for mono streams. - fn stream_channels(&self, stream_idx: usize) -> usize { - if stream_idx < self.nb_coupled_streams { - 2 - } else { - 1 - } - } - - /// Map one decoded channel slot to `(stream_idx, channel_idx)`. - /// - /// Parameters: flattened decoded `slot`. - /// Returns: elementary stream index and channel index within that stream. - fn slot_to_stream_channel(&self, slot: usize) -> (usize, usize) { - let coupled_slots = 2 * self.nb_coupled_streams; - if slot < coupled_slots { - (slot / 2, slot % 2) - } else { - (self.nb_coupled_streams + (slot - coupled_slots), 0) - } - } -} - -/// Split a multistream packet into per-stream sub-packets. -/// -/// Parameters: full multistream `packet` and total `nb_streams`. -/// Returns: borrowed sub-packet slices in stream order. -fn split_multistream_packet(packet: &[u8], nb_streams: usize) -> Result, OpusError> { - if nb_streams == 0 { - return Err(OpusError::InvalidArgument("nb_streams")); - } - if packet.is_empty() { - return Ok(vec![packet; nb_streams]); - } - - let mut out = Vec::with_capacity(nb_streams); - let mut offset = 0usize; - for _stream_idx in 0..nb_streams.saturating_sub(1) { - let (packet_len, used) = parse_self_delimited_size(&packet[offset..])?; - offset += used; - if offset + packet_len > packet.len() { - return Err(OpusError::InvalidPacket); - } - out.push(&packet[offset..offset + packet_len]); - offset += packet_len; - } - - if offset > packet.len() { - return Err(OpusError::InvalidPacket); - } - out.push(&packet[offset..]); - Ok(out) -} - -/// Parse one self-delimited multistream sub-packet length. -/// -/// Parameters: encoded length `data`. -/// Returns: tuple `(packet_len, bytes_used)`. -fn parse_self_delimited_size(data: &[u8]) -> Result<(usize, usize), OpusError> { - if data.is_empty() { - return Err(OpusError::InvalidPacket); - } - - let first = usize::from(data[0]); - if first < 252 { - Ok((first, 1)) - } else { - if data.len() < 2 { - return Err(OpusError::InvalidPacket); - } - Ok((first + (4 * usize::from(data[1])), 2)) - } -} - -/// Copy one decoded i16 channel into the interleaved multistream output buffer. -/// -/// Parameters: mutable destination `pcm`, destination `dst_stride`, output -/// channel index `dst_channel`, source `src`, source `src_stride`, source -/// channel index `src_channel`, and `frame_size`. -/// Returns: nothing; destination channel is overwritten. -fn copy_output_channel_i16( - pcm: &mut [i16], - dst_stride: usize, - dst_channel: usize, - src: &[i16], - src_stride: usize, - src_channel: usize, - frame_size: usize, -) { - for sample_idx in 0..frame_size { - pcm[sample_idx * dst_stride + dst_channel] = src[sample_idx * src_stride + src_channel]; - } -} - -/// Zero one interleaved i16 output channel. -/// -/// Parameters: mutable destination `pcm`, destination `dst_stride`, output -/// channel index `dst_channel`, and `frame_size`. -/// Returns: nothing; destination channel is zero-filled. -fn zero_output_channel_i16( - pcm: &mut [i16], - dst_stride: usize, - dst_channel: usize, - frame_size: usize, -) { - for sample_idx in 0..frame_size { - pcm[sample_idx * dst_stride + dst_channel] = 0; - } -} - -/// Copy one decoded f32 channel into the interleaved multistream output buffer. -/// -/// Parameters: mutable destination `pcm`, destination `dst_stride`, output -/// channel index `dst_channel`, source `src`, source `src_stride`, source -/// channel index `src_channel`, and `frame_size`. -/// Returns: nothing; destination channel is overwritten. -fn copy_output_channel_f32( - pcm: &mut [f32], - dst_stride: usize, - dst_channel: usize, - src: &[f32], - src_stride: usize, - src_channel: usize, - frame_size: usize, -) { - for sample_idx in 0..frame_size { - pcm[sample_idx * dst_stride + dst_channel] = src[sample_idx * src_stride + src_channel]; - } -} - -/// Zero one interleaved f32 output channel. -/// -/// Parameters: mutable destination `pcm`, destination `dst_stride`, output -/// channel index `dst_channel`, and `frame_size`. -/// Returns: nothing; destination channel is zero-filled. -fn zero_output_channel_f32( - pcm: &mut [f32], - dst_stride: usize, - dst_channel: usize, - frame_size: usize, -) { - for sample_idx in 0..frame_size { - pcm[sample_idx * dst_stride + dst_channel] = 0.0; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Split one two-stream packet with a one-byte length prefix. - /// - /// Parameters: none. - /// Returns: nothing; panics on mismatch. - #[test] - fn split_two_stream_packet_with_short_prefix() { - let packet = [3u8, 10, 11, 12, 20, 21]; - let sub_packets = split_multistream_packet(&packet, 2).unwrap(); - assert_eq!(sub_packets, vec![&[10, 11, 12][..], &[20, 21][..]]); - } - - /// Split one empty packet into PLC sub-packets for all streams. - /// - /// Parameters: none. - /// Returns: nothing; panics on mismatch. - #[test] - fn split_empty_packet_for_plc() { - let sub_packets = split_multistream_packet(&[], 3).unwrap(); - assert_eq!(sub_packets, vec![&[][..], &[][..], &[][..]]); - } -} diff --git a/vendor/opus-decoder/src/packet.rs b/vendor/opus-decoder/src/packet.rs deleted file mode 100644 index 8a6036a8..00000000 --- a/vendor/opus-decoder/src/packet.rs +++ /dev/null @@ -1,434 +0,0 @@ -use crate::Error; - -pub(crate) const MAX_FRAMES: usize = 48; -pub(crate) const MAX_PACKET_SIZE: usize = 1500; -pub(crate) const MAX_DURATION_SAMPLES_48K: usize = 5760; // 120 ms @ 48 kHz - -#[derive(Debug, Clone, Copy)] -pub(crate) struct ParsedPacket<'a> { - // Used by CELT/SILK mode selection and for debugging/conformance. - #[allow(dead_code)] - pub toc: u8, - #[allow(dead_code)] - pub packet_channels: u8, - pub frame_count: usize, - // Used by the actual decoder to feed individual frames into CELT/SILK. - #[allow(dead_code)] - pub frames: [&'a [u8]; MAX_FRAMES], - pub samples_per_frame_48k: usize, -} - -impl<'a> ParsedPacket<'a> { - #[allow(dead_code)] - pub fn frames(&self) -> &[&'a [u8]] { - &self.frames[..self.frame_count] - } - - pub fn samples_per_channel_48k(&self) -> usize { - self.samples_per_frame_48k * self.frame_count - } - - pub fn samples_per_channel(&self, fs_hz: u32) -> usize { - (self.samples_per_channel_48k() * fs_hz as usize) / 48_000 - } -} - -pub(crate) fn parse_packet(packet: &[u8]) -> Result, Error> { - if packet.is_empty() { - return Err(Error::BadPacket); - } - if packet.len() > MAX_PACKET_SIZE { - return Err(Error::PacketTooLarge { - max: MAX_PACKET_SIZE, - got: packet.len(), - }); - } - - let toc = packet[0]; - let packet_channels = if (toc & 0x04) != 0 { 2 } else { 1 }; - let samples_per_frame_48k = samples_per_frame_48k(toc); - - let mut frames: [&[u8]; MAX_FRAMES] = [&[]; MAX_FRAMES]; - - let code = toc & 0x03; - let frame_count = match code { - 0 => { - frames[0] = &packet[1..]; - 1 - } - 1 => { - let payload = &packet[1..]; - if payload.len() < 2 { - return Err(Error::BadPacket); - } - if payload.len() % 2 != 0 { - return Err(Error::BadPacket); - } - let sz0 = payload.len() / 2; - frames[0] = &payload[..sz0]; - frames[1] = &payload[sz0..]; - 2 - } - 2 => { - let payload = &packet[1..]; - let (sz0, used) = parse_size(payload)?; - let payload = &payload[used..]; - if sz0 > payload.len() { - return Err(Error::BadPacket); - } - frames[0] = &payload[..sz0]; - frames[1] = &payload[sz0..]; - 2 - } - 3 => { - let payload = &packet[1..]; - if payload.is_empty() { - return Err(Error::BadPacket); - } - - let ch = payload[0]; - let frame_count = (ch & 0x3f) as usize; - if !(1..=MAX_FRAMES).contains(&frame_count) { - return Err(Error::BadPacket); - } - - // RFC 6716: in the code-3 "frame count" byte, bit 6 indicates padding - // and bit 7 indicates VBR. - let has_padding = (ch & 0x40) != 0; - let vbr = (ch & 0x80) != 0; - - let mut idx = 1usize; // into payload - let mut data_end = payload.len(); // exclusive, relative to payload - - if has_padding { - let (pad_len, used) = parse_padding_len(&payload[idx..])?; - idx += used; - if pad_len > payload.len().saturating_sub(idx) { - return Err(Error::BadPacket); - } - data_end = payload.len() - pad_len; - if idx > data_end { - return Err(Error::BadPacket); - } - } - - let mut sizes = [0usize; MAX_FRAMES]; - if vbr { - let mut sum = 0usize; - for s in sizes.iter_mut().take(frame_count - 1) { - let (sz, used) = parse_size(&payload[idx..data_end])?; - idx += used; - *s = sz; - sum = sum.saturating_add(sz); - } - let remaining = data_end.saturating_sub(idx); - if sum > remaining { - return Err(Error::BadPacket); - } - sizes[frame_count - 1] = remaining - sum; - } else { - let remaining = data_end.saturating_sub(idx); - if remaining % frame_count != 0 { - return Err(Error::BadPacket); - } - let sz = remaining / frame_count; - for s in &mut sizes[..frame_count] { - *s = sz; - } - } - - // Now slice out each frame from the payload. - let mut off = idx; - for i in 0..frame_count { - let sz = sizes[i]; - if off + sz > data_end { - return Err(Error::BadPacket); - } - frames[i] = &payload[off..off + sz]; - off += sz; - } - if off != data_end { - return Err(Error::BadPacket); - } - - frame_count - } - _ => return Err(Error::BadPacket), - }; - - let total_samples_48k = samples_per_frame_48k.saturating_mul(frame_count); - if total_samples_48k > MAX_DURATION_SAMPLES_48K { - return Err(Error::BadPacket); - } - - Ok(ParsedPacket { - toc, - packet_channels, - frame_count, - frames, - samples_per_frame_48k, - }) -} - -fn parse_size(data: &[u8]) -> Result<(usize, usize), Error> { - if data.is_empty() { - return Err(Error::BadPacket); - } - let b0 = data[0] as usize; - if b0 < 252 { - Ok((b0, 1)) - } else { - if data.len() < 2 { - return Err(Error::BadPacket); - } - let b1 = data[1] as usize; - Ok(((b1 << 2) + b0, 2)) - } -} - -fn parse_padding_len(data: &[u8]) -> Result<(usize, usize), Error> { - let mut pad = 0usize; - let mut used = 0usize; - loop { - if used >= data.len() { - return Err(Error::BadPacket); - } - let p = data[used] as usize; - used += 1; - // RFC 6716 code-3 padding: each 255 byte contributes 254 and continues. - let add = if p == 255 { 254 } else { p }; - pad = pad.saturating_add(add); - if p != 255 { - break; - } - } - Ok((pad, used)) -} - -fn samples_per_frame_48k(toc: u8) -> usize { - // Port of libopus' `opus_packet_get_samples_per_frame()`, specialized to Fs=48000. - if (toc & 0x80) != 0 { - let audiosize = ((toc >> 3) & 0x03) as usize; - (48_000usize << audiosize) / 400 - } else if (toc & 0x60) == 0x60 { - if (toc & 0x08) != 0 { - 48_000usize / 50 - } else { - 48_000usize / 100 - } - } else { - let audiosize = ((toc >> 3) & 0x03) as usize; - if audiosize == 3 { - (48_000usize * 60) / 1000 - } else { - (48_000usize << audiosize) / 100 - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - use std::io::Read; - use std::path::{Path, PathBuf}; - - #[test] - fn parse_code0_single_frame() { - let packet = [0b0000_0000u8, 1, 2, 3, 4]; - let pp = parse_packet(&packet).unwrap(); - assert_eq!(pp.frame_count, 1); - assert_eq!(pp.frames()[0], &[1, 2, 3, 4]); - } - - #[test] - fn parse_code1_two_frames_cbr() { - let packet = [0b0000_0001u8, 1, 2, 3, 4]; - let pp = parse_packet(&packet).unwrap(); - assert_eq!(pp.frame_count, 2); - assert_eq!(pp.frames()[0], &[1, 2]); - assert_eq!(pp.frames()[1], &[3, 4]); - } - - #[test] - fn parse_code1_odd_payload_is_rejected() { - let packet = [0b0000_0001u8, 1, 2, 3]; - let err = parse_packet(&packet).unwrap_err(); - assert!(matches!(err, Error::BadPacket)); - } - - #[test] - fn parse_code2_two_frames_vbr_short_size() { - // size field <252 => one byte. - let packet = [0b0000_0010u8, 2, 0xaa, 0xbb, 0xcc]; - let pp = parse_packet(&packet).unwrap(); - assert_eq!(pp.frame_count, 2); - assert_eq!(pp.frames()[0], &[0xaa, 0xbb]); - assert_eq!(pp.frames()[1], &[0xcc]); - } - - #[test] - fn parse_code2_two_frames_vbr_long_size() { - // size field >=252 => two bytes; size = 4*b1 + b0. - // Pick b0=252, b1=1 => size=256. - let mut packet = Vec::::new(); - packet.push(0b0000_0010u8); - packet.push(252); - packet.push(1); - packet.extend(std::iter::repeat_n(0x11, 256)); - packet.extend(std::iter::repeat_n(0x22, 10)); - - let pp = parse_packet(&packet).unwrap(); - assert_eq!(pp.frame_count, 2); - assert_eq!(pp.frames()[0].len(), 256); - assert_eq!(pp.frames()[1].len(), 10); - assert!(pp.frames()[0].iter().all(|&b| b == 0x11)); - assert!(pp.frames()[1].iter().all(|&b| b == 0x22)); - } - - #[test] - fn parse_code3_cbr_n_frames() { - // code=3, count=3, CBR, no padding - let mut packet = Vec::::new(); - packet.push(0b0000_0011u8); - packet.push(0b0000_0011u8); // 3 frames - packet.extend([1, 2, 3, 4, 5, 6]); // 6 bytes => 2 bytes per frame - - let pp = parse_packet(&packet).unwrap(); - assert_eq!(pp.frame_count, 3); - assert_eq!(pp.frames()[0], &[1, 2]); - assert_eq!(pp.frames()[1], &[3, 4]); - assert_eq!(pp.frames()[2], &[5, 6]); - } - - #[test] - fn parse_code3_vbr_n_frames() { - // code=3, count=3, VBR, no padding - // sizes: 1,2, last is remainder (3) - let mut packet = vec![ - 0b0000_0011u8, - 0b1000_0011u8, // VBR + 3 frames - 1, // size0 - 2, // size1 - ]; - packet.extend([0xa0]); // frame0 (1) - packet.extend([0xb0, 0xb1]); // frame1 (2) - packet.extend([0xc0, 0xc1, 0xc2]); // frame2 (3) - - let pp = parse_packet(&packet).unwrap(); - assert_eq!(pp.frame_count, 3); - assert_eq!(pp.frames()[0], &[0xa0]); - assert_eq!(pp.frames()[1], &[0xb0, 0xb1]); - assert_eq!(pp.frames()[2], &[0xc0, 0xc1, 0xc2]); - } - - #[test] - fn parse_code3_padding() { - // code=3, count=1, CBR, padding present with pad_len=2. - let mut packet = Vec::::new(); - packet.push(0b0000_0011u8); - packet.push(0b0100_0001u8); // padding + 1 frame - packet.push(2); // pad len - packet.extend([0x42, 0x43, 0x44]); // frame data (3 bytes) - packet.extend([0, 0]); // padding bytes - - let pp = parse_packet(&packet).unwrap(); - assert_eq!(pp.frame_count, 1); - assert_eq!(pp.frames()[0], &[0x42, 0x43, 0x44]); - } - - #[test] - fn samples_per_frame_uses_config_low_bits() { - // CELT-only config 19 (0b10011) is 20 ms => 960 samples at 48 kHz. - // This validates we use config low bits (toc>>3)&0x03, not toc bits 5..6. - let toc = 19u8 << 3; - assert_eq!(samples_per_frame_48k(toc), 960); - } - - #[test] - #[ignore = "requires OPUS_TESTVECTORS_DIR (or testdata/opus_testvectors)"] - fn parse_official_vectors_smoke() { - let dir = vectors_dir(); - let entries = fs::read_dir(&dir).unwrap_or_else(|e| { - panic!("failed to read vectors dir {}: {e}", dir.to_string_lossy()) - }); - - let mut bit_files = Vec::::new(); - for ent in entries { - let ent = ent.unwrap(); - let p = ent.path(); - if p.extension().and_then(|s| s.to_str()) == Some("bit") { - bit_files.push(p); - } - } - bit_files.sort(); - assert!(!bit_files.is_empty(), "no .bit files in {}", dir.display()); - - for bit in bit_files { - let packets = read_opus_demo_stream(&bit).unwrap_or_else(|e| { - panic!("failed to read {}: {e}", bit.display()); - }); - for p in packets.into_iter().flatten() { - parse_packet(&p).unwrap_or_else(|e| { - panic!( - "parse failed for {} packet len {}: {e}", - bit.display(), - p.len() - ); - }); - } - } - } - - fn vectors_dir() -> PathBuf { - if let Ok(p) = std::env::var("OPUS_TESTVECTORS_DIR") { - return PathBuf::from(p); - } - PathBuf::from("testdata/opus_testvectors") - } - - fn read_opus_demo_stream(path: &Path) -> std::io::Result>>> { - let mut f = fs::File::open(path)?; - let mut bytes = Vec::new(); - f.read_to_end(&mut bytes)?; - - let mut i = 0usize; - let mut out = Vec::new(); - while i < bytes.len() { - if i + 4 > bytes.len() { - return Err(std::io::Error::new( - std::io::ErrorKind::UnexpectedEof, - "truncated packet_len", - )); - } - let packet_len = - u32::from_be_bytes([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]]) as usize; - i += 4; - - if i + 4 > bytes.len() { - return Err(std::io::Error::new( - std::io::ErrorKind::UnexpectedEof, - "truncated final_range", - )); - } - i += 4; - - if i + packet_len > bytes.len() { - return Err(std::io::Error::new( - std::io::ErrorKind::UnexpectedEof, - "truncated packet", - )); - } - let packet = if packet_len == 0 { - None - } else { - Some(bytes[i..i + packet_len].to_vec()) - }; - i += packet_len; - - out.push(packet); - } - - Ok(out) - } -} diff --git a/vendor/opus-decoder/src/silk/decode_core.rs b/vendor/opus-decoder/src/silk/decode_core.rs deleted file mode 100644 index af5c400e..00000000 --- a/vendor/opus-decoder/src/silk/decode_core.rs +++ /dev/null @@ -1,497 +0,0 @@ -//! SILK excitation reconstruction and LPC synthesis. -#![allow(clippy::needless_range_loop)] - -use super::gain; -use super::ltp; -use super::pitch; -use super::plc::SilkPlcState; -use super::tables::{ - LTP_MEM_LENGTH_MS, LTP_ORDER, MAX_FRAME_LENGTH, MAX_LPC_ORDER, MAX_NB_SUBFR, - MAX_SUB_FRAME_LENGTH, QUANT_LEVEL_ADJUST_Q10, QUANTIZATION_OFFSETS_Q10, -}; - -const TYPE_VOICED: i32 = 2; -const RAND_MULTIPLIER: i32 = 196_314_165; -const RAND_INCREMENT: i32 = 907_633_515; - -/// Decoded SILK parameters needed by the synthesis core. -#[derive(Debug, Clone)] -pub(super) struct SilkFrameParams { - /// Signal type for the internal frame. - pub signal_type: i32, - /// Quantizer offset type. - pub quant_offset_type: i32, - /// Gain indices per subframe. - pub gain_indices: [i8; MAX_NB_SUBFR], - /// Absolute lag index for voiced frames. - pub lag_index: i16, - /// Contour table index for voiced frames. - pub contour_index: i8, - /// LTP periodicity index. - pub per_index: i8, - /// LTP codebook index per subframe. - pub ltp_indices: [i8; MAX_NB_SUBFR], - /// LTP scale selector. - pub ltp_scale_index: i8, - /// Excitation randomization seed. - pub seed: i8, - /// Signed excitation pulses. - pub pulses: Vec, - /// Conditional gain decoding flag. - pub conditional: bool, - /// Internal frame length in samples. - pub frame_length: usize, - /// Number of active subframes. - pub nb_subfr: usize, - /// Internal sampling rate in kHz. - pub fs_khz: u32, - /// LPC order for the current bandwidth. - pub lpc_order: usize, -} - -/// Persistent SILK synthesis state for one coded channel. -#[derive(Debug, Clone)] -pub(super) struct SilkChannelState { - /// Previous linear gain in Q16. - pub prev_gain_q16: i32, - /// Previous gain entropy index. - pub last_gain_index: i8, - /// Previous decoded signal type. - pub prev_signal_type: i32, - /// Previous pitch lag. - pub lag_prev: i32, - /// Persistent LPC synthesis state in Q14. - pub s_lpc_q14_buf: [i32; MAX_LPC_ORDER], - /// Output buffer kept for future voiced re-whitening. - pub out_buf: [i16; MAX_FRAME_LENGTH + 2 * MAX_SUB_FRAME_LENGTH], - /// PLC state refreshed after every successful decode. - pub plc: SilkPlcState, -} - -impl Default for SilkChannelState { - /// Create zeroed decoder state with libopus-compatible gain defaults. - /// - /// Params: none. - /// Returns: initialized per-channel SILK synthesis state. - fn default() -> Self { - Self { - prev_gain_q16: 65_536, - last_gain_index: 10, - prev_signal_type: 0, - lag_prev: 100, - s_lpc_q14_buf: [0; MAX_LPC_ORDER], - out_buf: [0; MAX_FRAME_LENGTH + 2 * MAX_SUB_FRAME_LENGTH], - plc: SilkPlcState::default(), - } - } -} - -/// Short debug snapshot from the SILK core. -#[derive(Debug, Clone, Copy, Default)] -pub(super) struct CoreTrace { - /// First eight excitation samples in Q14. - pub exc_q14_head: [i32; 8], - /// First eight synthesized LPC-state samples in Q14. - pub s_lpc_q14_head: [i32; 8], - /// First eight PCM samples at internal rate. - pub pcm_head: [i16; 8], -} - -/// Decode one SILK internal frame to PCM at the internal sample rate. -/// -/// Params: frame `params`, per-half `lpc_coeffs_q12`, mutable `output_pcm`, and channel `state`. -/// Returns: trace snapshot for packet-0 comparisons. -pub(super) fn decode_core( - params: &SilkFrameParams, - lpc_coeffs_q12: &[[i16; MAX_LPC_ORDER]; 2], - output_pcm: &mut [i16], - state: &mut SilkChannelState, - packet_idx: usize, - frame_idx: usize, -) -> CoreTrace { - let subfr_length = params.frame_length / params.nb_subfr; - let ltp_mem_length = params.fs_khz as usize * LTP_MEM_LENGTH_MS; - let nlsf_interpolation_flag = - lpc_coeffs_q12[0][..params.lpc_order] != lpc_coeffs_q12[1][..params.lpc_order]; - let gains_q16 = gain::decode_gains( - ¶ms.gain_indices[..params.nb_subfr], - &mut state.last_gain_index, - params.conditional, - params.nb_subfr, - ); - let pitch_lags = if params.signal_type == TYPE_VOICED { - pitch::decode_pitch_lags( - params.lag_index as i32, - params.contour_index as i32, - params.fs_khz as i32, - params.nb_subfr, - ) - } else { - [0; MAX_NB_SUBFR] - }; - let (ltp_coeffs_q14, ltp_scale_q14) = if params.signal_type == TYPE_VOICED { - ltp::decode_ltp_coeffs( - params.per_index, - ¶ms.ltp_indices, - params.nb_subfr, - params.ltp_scale_index, - ) - } else { - ([[0; super::tables::LTP_ORDER]; MAX_NB_SUBFR], 0) - }; - - let mut trace = CoreTrace::default(); - let offset_q10 = QUANTIZATION_OFFSETS_Q10[(params.signal_type >> 1) as usize] - [params.quant_offset_type as usize]; - let mut rand_seed = params.seed as i32; - let mut exc_q14 = [0i32; MAX_FRAME_LENGTH]; - for i in 0..params.frame_length { - rand_seed = silk_rand(rand_seed); - let mut sample = (params.pulses[i] as i32) << 14; - if sample > 0 { - sample -= QUANT_LEVEL_ADJUST_Q10 << 4; - } else if sample < 0 { - sample += QUANT_LEVEL_ADJUST_Q10 << 4; - } - sample += offset_q10 << 4; - if rand_seed < 0 { - sample = -sample; - } - rand_seed = rand_seed.wrapping_add(params.pulses[i] as i32); - exc_q14[i] = sample; - if i < 8 { - trace.exc_q14_head[i] = sample; - } - } - - let mut s_lpc_q14 = [0i32; MAX_LPC_ORDER + MAX_SUB_FRAME_LENGTH]; - s_lpc_q14[..MAX_LPC_ORDER].copy_from_slice(&state.s_lpc_q14_buf); - let mut s_ltp = [0i16; MAX_FRAME_LENGTH]; - let mut ltp_history_q15 = [0i32; MAX_FRAME_LENGTH * 2]; - let mut ltp_buf_idx = ltp_mem_length; - - for k in 0..params.nb_subfr { - let a_q12 = &lpc_coeffs_q12[k >> 1]; - let gain_q16 = gains_q16[k]; - let gain_q10 = gains_q16[k] >> 6; - let pitch_lag = pitch_lags[k]; - let mut inv_gain_q31 = inverse32_var_q(gain_q16, 47); - let gain_adj_q16 = if gain_q16 != state.prev_gain_q16 { - div32_var_q(state.prev_gain_q16, gain_q16, 16) - } else { - 1 << 16 - }; - - if gain_q16 != state.prev_gain_q16 { - for sample in s_lpc_q14.iter_mut().take(MAX_LPC_ORDER) { - *sample = smulww(gain_adj_q16, *sample); - } - } - state.prev_gain_q16 = gain_q16; - - if params.signal_type == TYPE_VOICED { - if k == 0 || (k == 2 && nlsf_interpolation_flag) { - let start_idx = - ltp_mem_length - pitch_lag as usize - params.lpc_order - (LTP_ORDER / 2); - if k == 2 { - let copy_len = 2 * subfr_length; - state.out_buf[ltp_mem_length..ltp_mem_length + copy_len] - .copy_from_slice(&output_pcm[..copy_len]); - } - lpc_analysis_filter( - &mut s_ltp[start_idx..ltp_mem_length], - &state.out_buf[start_idx + k * subfr_length - ..start_idx + k * subfr_length + (ltp_mem_length - start_idx)], - a_q12, - ltp_mem_length - start_idx, - params.lpc_order, - ); - if k == 0 { - inv_gain_q31 = (smulwb(inv_gain_q31, ltp_scale_q14) << 2) as i32; - } - for i in 0..(pitch_lag as usize + LTP_ORDER / 2) { - ltp_history_q15[ltp_buf_idx - i - 1] = - smulwb(inv_gain_q31, s_ltp[ltp_mem_length - i - 1] as i32); - } - } else if gain_adj_q16 != (1 << 16) { - for i in 0..(pitch_lag as usize + LTP_ORDER / 2) { - ltp_history_q15[ltp_buf_idx - i - 1] = - smulww(gain_adj_q16, ltp_history_q15[ltp_buf_idx - i - 1]); - } - } - } - - let subframe_range = k * subfr_length..(k + 1) * subfr_length; - let exc_subframe = &exc_q14[subframe_range.clone()]; - let mut pres_q14 = [0i32; MAX_SUB_FRAME_LENGTH]; - pres_q14[..subfr_length].copy_from_slice(exc_subframe); - if params.signal_type == TYPE_VOICED && pitch_lags[k] > 0 { - ltp::ltp_filter( - &mut pres_q14[..subfr_length], - exc_subframe, - &mut ltp_history_q15, - &mut ltp_buf_idx, - pitch_lags[k] as usize, - subfr_length, - <p_coeffs_q14[k], - packet_idx, - frame_idx, - k, - ); - } - - for i in 0..subfr_length { - let mut lpc_pred_q10 = (params.lpc_order as i32) >> 1; - for j in 0..params.lpc_order { - lpc_pred_q10 = smlawb( - lpc_pred_q10, - s_lpc_q14[MAX_LPC_ORDER + i - 1 - j], - a_q12[j] as i32, - ); - } - let current_q14 = add_sat32(pres_q14[i], lpc_pred_q10 << 4); - s_lpc_q14[MAX_LPC_ORDER + i] = current_q14; - - let pcm = sat16(rshift_round(smulww(current_q14, gain_q10), 8)); - output_pcm[k * subfr_length + i] = pcm; - - if k == 0 && i < 8 { - trace.s_lpc_q14_head[i] = current_q14; - trace.pcm_head[i] = pcm; - } - } - s_lpc_q14.copy_within(subfr_length..subfr_length + MAX_LPC_ORDER, 0); - } - - state - .s_lpc_q14_buf - .copy_from_slice(&s_lpc_q14[..MAX_LPC_ORDER]); - state.prev_signal_type = params.signal_type; - if params.signal_type == TYPE_VOICED && pitch_lags[params.nb_subfr - 1] > 0 { - state.lag_prev = pitch_lags[params.nb_subfr - 1]; - } - let mv_len = ltp_mem_length.saturating_sub(params.frame_length); - if mv_len > 0 { - state - .out_buf - .copy_within(params.frame_length..params.frame_length + mv_len, 0); - } - state.out_buf[mv_len..mv_len + params.frame_length] - .copy_from_slice(&output_pcm[..params.frame_length]); - trace -} - -/// Compute SILK RNG state update with wraparound. -/// -/// Params: signed `seed`. -/// Returns: updated pseudo-random state. -fn silk_rand(seed: i32) -> i32 { - seed.wrapping_mul(RAND_MULTIPLIER) - .wrapping_add(RAND_INCREMENT) -} - -/// Approximate signed `((a32 * b32) >> 16)`. -/// -/// Params: signed `a32` and `b32`. -/// Returns: signed Q16-scaled product. -fn smulww(a32: i32, b32: i32) -> i32 { - ((a32 as i64 * b32 as i64) >> 16) as i32 -} - -/// Approximate `a32 + ((b32 * (i16)c32) >> 16)`. -/// -/// Params: accumulator `a32`, multiplicand `b32`, and signed `c32`. -/// Returns: accumulated high-word product. -fn smlawb(a32: i32, b32: i32, c32: i32) -> i32 { - a32.wrapping_add(smulwb(b32, c32)) -} - -/// Saturating 32-bit addition. -/// -/// Params: signed `a` and `b`. -/// Returns: saturated sum. -fn add_sat32(a: i32, b: i32) -> i32 { - a.saturating_add(b) -} - -/// Saturate to `i16`. -/// -/// Params: signed `value`. -/// Returns: clamped 16-bit PCM sample. -fn sat16(value: i32) -> i16 { - value.clamp(i16::MIN as i32, i16::MAX as i32) as i16 -} - -/// Round a signed arithmetic right shift. -/// -/// Params: signed `value` and positive `shift`. -/// Returns: rounded shifted value. -fn rshift_round(value: i32, shift: usize) -> i32 { - if shift == 1 { - (value >> 1) + (value & 1) - } else { - ((value >> (shift - 1)) + 1) >> 1 - } -} - -/// Compute a saturating absolute value for normalization. -/// -/// Params: signed `value`. -/// Returns: non-negative magnitude, saturating `i32::MIN`. -fn abs32_for_clz(value: i32) -> i32 { - if value == i32::MIN { - i32::MAX - } else { - value.abs() - } -} - -/// Compute the signed high word of a 64-bit product. -/// -/// Params: signed `a32` and `b32`. -/// Returns: arithmetic `(a32 * b32) >> 32`. -fn smmul(a32: i32, b32: i32) -> i32 { - ((a32 as i64 * b32 as i64) >> 32) as i32 -} - -/// Left-shift with wraparound semantics. -/// -/// Params: signed `value` and non-negative `shift`. -/// Returns: wrapped left-shifted value. -fn lshift_ovflw(value: i32, shift: usize) -> i32 { - ((value as u32) << shift) as i32 -} - -/// Subtract with wraparound semantics. -/// -/// Params: signed `a` and `b`. -/// Returns: wrapped difference. -fn sub32_ovflw(a: i32, b: i32) -> i32 { - a.wrapping_sub(b) -} - -/// Left-shift with saturation. -/// -/// Params: signed `value` and non-negative `shift`. -/// Returns: left-shifted value clamped to `i32`. -fn lshift_sat32(value: i32, shift: usize) -> i32 { - if shift >= 31 { - if value > 0 { - i32::MAX - } else if value < 0 { - i32::MIN - } else { - 0 - } - } else { - value - .clamp(i32::MIN >> shift, i32::MAX >> shift) - .wrapping_shl(shift as u32) - } -} - -/// Approximate `(a32 << q_res) / b32` using SILK's reciprocal refinement. -/// -/// Params: numerator `a32`, denominator `b32`, and output Q-domain `q_res`. -/// Returns: scaled signed quotient. -fn div32_var_q(a32: i32, b32: i32, q_res: usize) -> i32 { - debug_assert!(b32 != 0); - if a32 == 0 { - return 0; - } - - let a_headrm = abs32_for_clz(a32).leading_zeros() as usize - 1; - let mut a32_nrm = a32 << a_headrm; - let b_headrm = abs32_for_clz(b32).leading_zeros() as usize - 1; - let b32_nrm = b32 << b_headrm; - - let b32_inv = (i32::MAX >> 2) / (b32_nrm >> 16); - let mut result = smulwb(a32_nrm, b32_inv); - a32_nrm = sub32_ovflw(a32_nrm, lshift_ovflw(smmul(b32_nrm, result), 3)); - result = smlawb(result, a32_nrm, b32_inv); - - let lshift = 29isize + a_headrm as isize - b_headrm as isize - q_res as isize; - if lshift < 0 { - lshift_sat32(result, (-lshift) as usize) - } else if lshift < 32 { - result >> lshift - } else { - 0 - } -} - -/// Approximate `a32 + ((b32 * c32) >> 16)` with SILK wraparound semantics. -/// -/// Params: accumulator `a32` and signed multiplicands `b32` and `c32`. -/// Returns: accumulated high-word product. -fn smlaww(a32: i32, b32: i32, c32: i32) -> i32 { - a32.wrapping_add(smulww(b32, c32)) -} - -/// Approximate `(1 << q_res) / value` using SILK's reciprocal refinement. -/// -/// Params: signed non-zero `value` and target result Q-domain `q_res`. -/// Returns: reciprocal approximation in fixed-point. -fn inverse32_var_q(value: i32, q_res: usize) -> i32 { - debug_assert!(value != 0); - debug_assert!(q_res > 0); - - let b_headrm = abs32_for_clz(value).leading_zeros() as usize - 1; - let b32_nrm = value << b_headrm; - let b32_inv = (i32::MAX >> 2) / (b32_nrm >> 16); - let mut result = b32_inv << 16; - let err_q32 = ((1i32 << 29) - smulwb(b32_nrm, b32_inv)) << 3; - result = smlaww(result, err_q32, b32_inv); - - let lshift = 61isize - b_headrm as isize - q_res as isize; - if lshift <= 0 { - lshift_sat32(result, (-lshift) as usize) - } else if lshift < 32 { - result >> lshift - } else { - 0 - } -} - -/// Port of `silk_LPC_analysis_filter` used for voiced re-whitening. -/// -/// Params: mutable `output`, past decoded `input`, LPC `a_q12`, signal `len`, and LPC `order`. -/// Returns: nothing; `output[..len]` receives Q0 re-whitened samples. -fn lpc_analysis_filter(output: &mut [i16], input: &[i16], a_q12: &[i16], len: usize, order: usize) { - debug_assert!(order <= len); - output[..order].fill(0); - for ix in order..len { - let mut out32_q12 = smulbb(input[ix - 1] as i32, a_q12[0] as i32); - out32_q12 = out32_q12.wrapping_add(smulbb(input[ix - 2] as i32, a_q12[1] as i32)); - out32_q12 = out32_q12.wrapping_add(smulbb(input[ix - 3] as i32, a_q12[2] as i32)); - out32_q12 = out32_q12.wrapping_add(smulbb(input[ix - 4] as i32, a_q12[3] as i32)); - out32_q12 = out32_q12.wrapping_add(smulbb(input[ix - 5] as i32, a_q12[4] as i32)); - out32_q12 = out32_q12.wrapping_add(smulbb(input[ix - 6] as i32, a_q12[5] as i32)); - for j in (6..order).step_by(2) { - out32_q12 = out32_q12.wrapping_add(smulbb(input[ix - 1 - j] as i32, a_q12[j] as i32)); - out32_q12 = - out32_q12.wrapping_add(smulbb(input[ix - 2 - j] as i32, a_q12[j + 1] as i32)); - } - let out32_q12 = ((input[ix] as i32) << 12).wrapping_sub(out32_q12); - output[ix] = sat16(rshift_round(out32_q12, 12)); - } -} - -/// Multiply signed low 16-bit halves. -/// -/// Params: signed `a32` and `b32`. -/// Returns: signed 32-bit product. -fn smulbb(a32: i32, b32: i32) -> i32 { - (a32 as i16 as i32) * (b32 as i16 as i32) -} - -/// Multiply by the signed low 16-bit half of `b32` and keep high word. -/// -/// Params: signed `a32` and signed `b32`. -/// Returns: high-word signed product. -fn smulwb(a32: i32, b32: i32) -> i32 { - let b16 = b32 as i16 as i32; - let high = (a32 >> 16) * b16; - let low = ((a32 & 0xFFFF) * b16) >> 16; - high.wrapping_add(low) -} diff --git a/vendor/opus-decoder/src/silk/entropy_tables.rs b/vendor/opus-decoder/src/silk/entropy_tables.rs deleted file mode 100644 index 52e3b029..00000000 --- a/vendor/opus-decoder/src/silk/entropy_tables.rs +++ /dev/null @@ -1,289 +0,0 @@ -//! SILK entropy decoding tables used by Phase 3a LBRR payload consumption. - -/// Number of SILK rate levels. -pub(super) const N_RATE_LEVELS: usize = 10; -/// Maximum pulse count symbol before LSB extension (`SILK_MAX_PULSES`). -pub(super) const SILK_MAX_PULSES: i32 = 16; -/// Shell frame length in samples. -pub(super) const SHELL_CODEC_FRAME_LENGTH: usize = 16; -/// Maximum NLSF quant amplitude used by entropy decode. -pub(super) const NLSF_QUANT_MAX_AMPLITUDE: i32 = 4; - -/// VAD-aware signal type + quant-offset iCDF. -pub(super) const TYPE_OFFSET_VAD_ICDF: [u8; 4] = [232, 158, 10, 0]; -/// No-VAD signal type + quant-offset iCDF. -pub(super) const TYPE_OFFSET_NO_VAD_ICDF: [u8; 2] = [230, 0]; - -/// NLSF interpolation-factor iCDF. -pub(super) const NLSF_INTERP_FACTOR_ICDF: [u8; 5] = [243, 221, 192, 181, 0]; -/// NLSF extension iCDF used for tails. -pub(super) const NLSF_EXT_ICDF: [u8; 7] = [100, 40, 16, 7, 3, 1, 0]; - -/// Uniform entropy tables used by SILK side information decode. -pub(super) const UNIFORM4_ICDF: [u8; 4] = [192, 128, 64, 0]; -pub(super) const UNIFORM6_ICDF: [u8; 6] = [213, 171, 128, 85, 43, 0]; -pub(super) const UNIFORM8_ICDF: [u8; 8] = [224, 192, 160, 128, 96, 64, 32, 0]; - -/// LSB-pulse extension iCDF. -pub(super) const LSB_ICDF: [u8; 2] = [120, 0]; - -/// Gain coding tables. -pub(super) const GAIN_ICDF: [[u8; 8]; 3] = [ - [224, 112, 44, 15, 3, 2, 1, 0], - [254, 237, 192, 132, 70, 23, 4, 0], - [255, 252, 226, 155, 61, 11, 2, 0], -]; -pub(super) const DELTA_GAIN_ICDF: [u8; 41] = [ - 250, 245, 234, 203, 71, 50, 42, 38, 35, 33, 31, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, - 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -]; - -/// Pitch decoding tables. -pub(super) const PITCH_LAG_ICDF: [u8; 32] = [ - 253, 250, 244, 233, 212, 182, 150, 131, 120, 110, 98, 85, 72, 60, 49, 40, 32, 25, 19, 15, 13, - 11, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -]; -pub(super) const PITCH_DELTA_ICDF: [u8; 21] = [ - 210, 208, 206, 203, 199, 193, 183, 168, 142, 104, 74, 52, 37, 27, 20, 14, 10, 6, 4, 2, 0, -]; -pub(super) const PITCH_CONTOUR_ICDF: [u8; 34] = [ - 223, 201, 183, 167, 152, 138, 124, 111, 98, 88, 79, 70, 62, 56, 50, 44, 39, 35, 31, 27, 24, 21, - 18, 16, 14, 12, 10, 8, 6, 4, 3, 2, 1, 0, -]; -pub(super) const PITCH_CONTOUR_NB_ICDF: [u8; 11] = [188, 176, 155, 138, 119, 97, 67, 43, 26, 10, 0]; -pub(super) const PITCH_CONTOUR_10MS_ICDF: [u8; 12] = - [165, 119, 80, 61, 47, 35, 27, 20, 14, 9, 4, 0]; -pub(super) const PITCH_CONTOUR_10MS_NB_ICDF: [u8; 3] = [113, 63, 0]; - -/// LTP coding tables. -pub(super) const LTP_PER_INDEX_ICDF: [u8; 3] = [179, 99, 0]; -pub(super) const LTP_GAIN_ICDF_0: [u8; 8] = [71, 56, 43, 30, 21, 12, 6, 0]; -pub(super) const LTP_GAIN_ICDF_1: [u8; 16] = [ - 199, 165, 144, 124, 109, 96, 84, 71, 61, 51, 42, 32, 23, 15, 8, 0, -]; -pub(super) const LTP_GAIN_ICDF_2: [u8; 32] = [ - 241, 225, 211, 199, 187, 175, 164, 153, 142, 132, 123, 114, 105, 96, 88, 80, 72, 64, 57, 50, - 44, 38, 33, 29, 24, 20, 16, 12, 9, 5, 2, 0, -]; -pub(super) const LTPSCALE_ICDF: [u8; 3] = [128, 64, 0]; - -/// Pulse coding tables. -pub(super) const RATE_LEVELS_ICDF: [[u8; 9]; 2] = [ - [241, 190, 178, 132, 87, 74, 41, 14, 0], - [223, 193, 157, 140, 106, 57, 39, 18, 0], -]; -pub(super) const PULSES_PER_BLOCK_ICDF: [[u8; 18]; 10] = [ - [ - 125, 51, 26, 18, 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - ], - [ - 198, 105, 45, 22, 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - ], - [ - 213, 162, 116, 83, 59, 43, 32, 24, 18, 15, 12, 9, 7, 6, 5, 3, 2, 0, - ], - [ - 239, 187, 116, 59, 28, 16, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - ], - [ - 250, 229, 188, 135, 86, 51, 30, 19, 13, 10, 8, 6, 5, 4, 3, 2, 1, 0, - ], - [ - 249, 235, 213, 185, 156, 128, 103, 83, 66, 53, 42, 33, 26, 21, 17, 13, 10, 0, - ], - [ - 254, 249, 235, 206, 164, 118, 77, 46, 27, 16, 10, 7, 5, 4, 3, 2, 1, 0, - ], - [ - 255, 253, 249, 239, 220, 191, 156, 119, 85, 57, 37, 23, 15, 10, 6, 4, 2, 0, - ], - [ - 255, 253, 251, 246, 237, 223, 203, 179, 152, 124, 98, 75, 55, 40, 29, 21, 15, 0, - ], - [ - 255, 254, 253, 247, 220, 162, 106, 67, 42, 28, 18, 12, 9, 6, 4, 3, 2, 0, - ], -]; -pub(super) const SHELL_CODE_TABLE0: [u8; 152] = [ - 128, 0, 214, 42, 0, 235, 128, 21, 0, 244, 184, 72, 11, 0, 248, 214, 128, 42, 7, 0, 248, 225, - 170, 80, 25, 5, 0, 251, 236, 198, 126, 54, 18, 3, 0, 250, 238, 211, 159, 82, 35, 15, 5, 0, 250, - 231, 203, 168, 128, 88, 53, 25, 6, 0, 252, 238, 216, 185, 148, 108, 71, 40, 18, 4, 0, 253, 243, - 225, 199, 166, 128, 90, 57, 31, 13, 3, 0, 254, 246, 233, 212, 183, 147, 109, 73, 44, 23, 10, 2, - 0, 255, 250, 240, 223, 198, 166, 128, 90, 58, 33, 16, 6, 1, 0, 255, 251, 244, 231, 210, 181, - 146, 110, 75, 46, 25, 12, 5, 1, 0, 255, 253, 248, 238, 221, 196, 164, 128, 92, 60, 35, 18, 8, - 3, 1, 0, 255, 253, 249, 242, 229, 208, 180, 146, 110, 76, 48, 27, 14, 7, 3, 1, 0, -]; -pub(super) const SHELL_CODE_TABLE1: [u8; 152] = [ - 129, 0, 207, 50, 0, 236, 129, 20, 0, 245, 185, 72, 10, 0, 249, 213, 129, 42, 6, 0, 250, 226, - 169, 87, 27, 4, 0, 251, 233, 194, 130, 62, 20, 4, 0, 250, 236, 207, 160, 99, 47, 17, 3, 0, 255, - 240, 217, 182, 131, 81, 41, 11, 1, 0, 255, 254, 233, 201, 159, 107, 61, 20, 2, 1, 0, 255, 249, - 233, 206, 170, 128, 86, 50, 23, 7, 1, 0, 255, 250, 238, 217, 186, 148, 108, 70, 39, 18, 6, 1, - 0, 255, 252, 243, 226, 200, 166, 128, 90, 56, 30, 13, 4, 1, 0, 255, 252, 245, 231, 209, 180, - 146, 110, 76, 47, 25, 11, 4, 1, 0, 255, 253, 248, 237, 219, 194, 163, 128, 93, 62, 37, 19, 8, - 3, 1, 0, 255, 254, 250, 241, 226, 205, 177, 145, 111, 79, 51, 30, 15, 6, 2, 1, 0, -]; -pub(super) const SHELL_CODE_TABLE2: [u8; 152] = [ - 129, 0, 203, 54, 0, 234, 129, 23, 0, 245, 184, 73, 10, 0, 250, 215, 129, 41, 5, 0, 252, 232, - 173, 86, 24, 3, 0, 253, 240, 200, 129, 56, 15, 2, 0, 253, 244, 217, 164, 94, 38, 10, 1, 0, 253, - 245, 226, 189, 132, 71, 27, 7, 1, 0, 253, 246, 231, 203, 159, 105, 56, 23, 6, 1, 0, 255, 248, - 235, 213, 179, 133, 85, 47, 19, 5, 1, 0, 255, 254, 243, 221, 194, 159, 117, 70, 37, 12, 2, 1, - 0, 255, 254, 248, 234, 208, 171, 128, 85, 48, 22, 8, 2, 1, 0, 255, 254, 250, 240, 220, 189, - 149, 107, 67, 36, 16, 6, 2, 1, 0, 255, 254, 251, 243, 227, 201, 166, 128, 90, 55, 29, 13, 5, 2, - 1, 0, 255, 254, 252, 246, 234, 213, 183, 147, 109, 73, 43, 22, 10, 4, 2, 1, 0, -]; -pub(super) const SHELL_CODE_TABLE3: [u8; 152] = [ - 130, 0, 200, 58, 0, 231, 130, 26, 0, 244, 184, 76, 12, 0, 249, 214, 130, 43, 6, 0, 252, 232, - 173, 87, 24, 3, 0, 253, 241, 203, 131, 56, 14, 2, 0, 254, 246, 221, 167, 94, 35, 8, 1, 0, 254, - 249, 232, 193, 130, 65, 23, 5, 1, 0, 255, 251, 239, 211, 162, 99, 45, 15, 4, 1, 0, 255, 251, - 243, 223, 186, 131, 74, 33, 11, 3, 1, 0, 255, 252, 245, 230, 202, 158, 105, 57, 24, 8, 2, 1, 0, - 255, 253, 247, 235, 214, 179, 132, 84, 44, 19, 7, 2, 1, 0, 255, 254, 250, 240, 223, 196, 159, - 112, 69, 36, 15, 6, 2, 1, 0, 255, 254, 253, 245, 231, 209, 176, 136, 93, 55, 27, 11, 3, 2, 1, - 0, 255, 254, 253, 252, 239, 221, 194, 158, 117, 76, 42, 18, 4, 3, 2, 1, 0, -]; -pub(super) const SHELL_CODE_TABLE_OFFSETS: [u8; 17] = [ - 0, 0, 2, 5, 9, 14, 20, 27, 35, 44, 54, 65, 77, 90, 104, 119, 135, -]; -pub(super) const SIGN_ICDF: [u8; 42] = [ - 254, 49, 67, 77, 82, 93, 99, 198, 11, 18, 24, 31, 36, 45, 255, 46, 66, 78, 87, 94, 104, 208, - 14, 21, 32, 42, 51, 66, 255, 94, 104, 109, 112, 115, 118, 248, 53, 69, 80, 88, 95, 102, -]; - -/// Minimal NLSF entropy codebook fields needed by decode_indices. -#[derive(Debug, Clone, Copy)] -pub(super) struct NlsfEntropyCodebook { - /// NLSF order. - pub order: usize, - /// Number of stage-1 vectors. - pub n_vectors: usize, - /// Stage-1 iCDF table. - pub cb1_icdf: &'static [u8], - /// Packed selector map (`ec_sel`) for per-dimension stage-2 tables. - pub ec_sel: &'static [u8], - /// Stage-2 iCDF table bank (`ec_iCDF`). - pub ec_icdf: &'static [u8], -} - -/// NB/MB codebook stage-1 iCDF. -pub(super) const NLSF_CB1_ICDF_NB_MB: [u8; 64] = [ - 212, 178, 148, 129, 108, 96, 85, 82, 79, 77, 61, 59, 57, 56, 51, 49, 48, 45, 42, 41, 40, 38, - 36, 34, 31, 30, 21, 12, 10, 3, 1, 0, 255, 245, 244, 236, 233, 225, 217, 203, 190, 176, 175, - 161, 149, 136, 125, 114, 102, 91, 81, 71, 60, 52, 43, 35, 28, 20, 19, 18, 12, 11, 5, 0, -]; -/// NB/MB codebook selector map. -pub(super) const NLSF_CB2_SELECT_NB_MB: [u8; 160] = [ - 16, 0, 0, 0, 0, 99, 66, 36, 36, 34, 36, 34, 34, 34, 34, 83, 69, 36, 52, 34, 116, 102, 70, 68, - 68, 176, 102, 68, 68, 34, 65, 85, 68, 84, 36, 116, 141, 152, 139, 170, 132, 187, 184, 216, 137, - 132, 249, 168, 185, 139, 104, 102, 100, 68, 68, 178, 218, 185, 185, 170, 244, 216, 187, 187, - 170, 244, 187, 187, 219, 138, 103, 155, 184, 185, 137, 116, 183, 155, 152, 136, 132, 217, 184, - 184, 170, 164, 217, 171, 155, 139, 244, 169, 184, 185, 170, 164, 216, 223, 218, 138, 214, 143, - 188, 218, 168, 244, 141, 136, 155, 170, 168, 138, 220, 219, 139, 164, 219, 202, 216, 137, 168, - 186, 246, 185, 139, 116, 185, 219, 185, 138, 100, 100, 134, 100, 102, 34, 68, 68, 100, 68, 168, - 203, 221, 218, 168, 167, 154, 136, 104, 70, 164, 246, 171, 137, 139, 137, 155, 218, 219, 139, -]; -/// NB/MB codebook stage-2 iCDF table bank. -pub(super) const NLSF_CB2_ICDF_NB_MB: [u8; 72] = [ - 255, 254, 253, 238, 14, 3, 2, 1, 0, 255, 254, 252, 218, 35, 3, 2, 1, 0, 255, 254, 250, 208, 59, - 4, 2, 1, 0, 255, 254, 246, 194, 71, 10, 2, 1, 0, 255, 252, 236, 183, 82, 8, 2, 1, 0, 255, 252, - 235, 180, 90, 17, 2, 1, 0, 255, 248, 224, 171, 97, 30, 4, 1, 0, 255, 254, 236, 173, 95, 37, 7, - 1, 0, -]; - -/// WB codebook stage-1 iCDF. -pub(super) const NLSF_CB1_ICDF_WB: [u8; 64] = [ - 225, 204, 201, 184, 183, 175, 158, 154, 153, 135, 119, 115, 113, 110, 109, 99, 98, 95, 79, 68, - 52, 50, 48, 45, 43, 32, 31, 27, 18, 10, 3, 0, 255, 251, 235, 230, 212, 201, 196, 182, 167, 166, - 163, 151, 138, 124, 110, 104, 90, 78, 76, 70, 69, 57, 45, 34, 24, 21, 11, 6, 5, 4, 3, 0, -]; -/// WB codebook selector map. -pub(super) const NLSF_CB2_SELECT_WB: [u8; 256] = [ - 0, 0, 0, 0, 0, 0, 0, 1, 100, 102, 102, 68, 68, 36, 34, 96, 164, 107, 158, 185, 180, 185, 139, - 102, 64, 66, 36, 34, 34, 0, 1, 32, 208, 139, 141, 191, 152, 185, 155, 104, 96, 171, 104, 166, - 102, 102, 102, 132, 1, 0, 0, 0, 0, 16, 16, 0, 80, 109, 78, 107, 185, 139, 103, 101, 208, 212, - 141, 139, 173, 153, 123, 103, 36, 0, 0, 0, 0, 0, 0, 1, 48, 0, 0, 0, 0, 0, 0, 32, 68, 135, 123, - 119, 119, 103, 69, 98, 68, 103, 120, 118, 118, 102, 71, 98, 134, 136, 157, 184, 182, 153, 139, - 134, 208, 168, 248, 75, 189, 143, 121, 107, 32, 49, 34, 34, 34, 0, 17, 2, 210, 235, 139, 123, - 185, 137, 105, 134, 98, 135, 104, 182, 100, 183, 171, 134, 100, 70, 68, 70, 66, 66, 34, 131, - 64, 166, 102, 68, 36, 2, 1, 0, 134, 166, 102, 68, 34, 34, 66, 132, 212, 246, 158, 139, 107, - 107, 87, 102, 100, 219, 125, 122, 137, 118, 103, 132, 114, 135, 137, 105, 171, 106, 50, 34, - 164, 214, 141, 143, 185, 151, 121, 103, 192, 34, 0, 0, 0, 0, 0, 1, 208, 109, 74, 187, 134, 249, - 159, 137, 102, 110, 154, 118, 87, 101, 119, 101, 0, 2, 0, 36, 36, 66, 68, 35, 96, 164, 102, - 100, 36, 0, 2, 33, 167, 138, 174, 102, 100, 84, 2, 2, 100, 107, 120, 119, 36, 197, 24, 0, -]; -/// WB codebook stage-2 iCDF table bank. -pub(super) const NLSF_CB2_ICDF_WB: [u8; 72] = [ - 255, 254, 253, 244, 12, 3, 2, 1, 0, 255, 254, 252, 224, 38, 3, 2, 1, 0, 255, 254, 251, 209, 57, - 4, 2, 1, 0, 255, 254, 244, 195, 69, 4, 2, 1, 0, 255, 251, 232, 184, 84, 7, 2, 1, 0, 255, 254, - 240, 186, 86, 14, 2, 1, 0, 255, 254, 239, 178, 91, 30, 5, 1, 0, 255, 248, 227, 177, 100, 19, 2, - 1, 0, -]; - -const NLSF_CODEBOOK_NB_MB: NlsfEntropyCodebook = NlsfEntropyCodebook { - order: 10, - n_vectors: 32, - cb1_icdf: &NLSF_CB1_ICDF_NB_MB, - ec_sel: &NLSF_CB2_SELECT_NB_MB, - ec_icdf: &NLSF_CB2_ICDF_NB_MB, -}; - -const NLSF_CODEBOOK_WB: NlsfEntropyCodebook = NlsfEntropyCodebook { - order: 16, - n_vectors: 32, - cb1_icdf: &NLSF_CB1_ICDF_WB, - ec_sel: &NLSF_CB2_SELECT_WB, - ec_icdf: &NLSF_CB2_ICDF_WB, -}; - -/// Pick NLSF entropy codebook for internal SILK rate. -/// -/// Params: `fs_khz` internal SILK sample rate (8/12/16). -/// Returns: NB/MB codebook for 8/12 and WB codebook for 16 kHz. -pub(super) fn nlsf_codebook(fs_khz: u32) -> &'static NlsfEntropyCodebook { - if fs_khz == 16 { - &NLSF_CODEBOOK_WB - } else { - &NLSF_CODEBOOK_NB_MB - } -} - -/// Pick pitch low-bits iCDF for given internal SILK rate. -/// -/// Params: `fs_khz` internal SILK sample rate (8/12/16). -/// Returns: corresponding uniform iCDF table. -pub(super) fn pitch_lag_low_bits_icdf(fs_khz: u32) -> &'static [u8] { - match fs_khz { - 16 => &UNIFORM8_ICDF, - 12 => &UNIFORM6_ICDF, - _ => &UNIFORM4_ICDF, - } -} - -/// Pick pitch contour iCDF by bandwidth and subframe count. -/// -/// Params: `fs_khz` internal SILK sample rate and `nb_subfr` (2 or 4). -/// Returns: contour iCDF table used by decode_indices. -pub(super) fn pitch_contour_icdf(fs_khz: u32, nb_subfr: usize) -> &'static [u8] { - if fs_khz == 8 { - if nb_subfr == 4 { - &PITCH_CONTOUR_NB_ICDF - } else { - &PITCH_CONTOUR_10MS_NB_ICDF - } - } else if nb_subfr == 4 { - &PITCH_CONTOUR_ICDF - } else { - &PITCH_CONTOUR_10MS_ICDF - } -} - -/// Pick LTP gain iCDF table by PER index. -/// -/// Params: `per_index` decoded LTP periodicity class. -/// Returns: iCDF table for that class, or `None` when out of range. -pub(super) fn ltp_gain_icdf(per_index: usize) -> Option<&'static [u8]> { - match per_index { - 0 => Some(<P_GAIN_ICDF_0), - 1 => Some(<P_GAIN_ICDF_1), - 2 => Some(<P_GAIN_ICDF_2), - _ => None, - } -} diff --git a/vendor/opus-decoder/src/silk/gain.rs b/vendor/opus-decoder/src/silk/gain.rs deleted file mode 100644 index e7dda86b..00000000 --- a/vendor/opus-decoder/src/silk/gain.rs +++ /dev/null @@ -1,114 +0,0 @@ -//! SILK gain dequantization helpers. - -use super::tables::{MAX_DELTA_GAIN_QUANT, MIN_DELTA_GAIN_QUANT, N_LEVELS_QGAIN}; - -const MIN_QGAIN_DB: i32 = 2; -const MAX_QGAIN_DB: i32 = 88; -const OFFSET: i32 = ((MIN_QGAIN_DB * 128) / 6) + 16 * 128; -const INV_SCALE_Q16: i32 = - (65_536 * (((MAX_QGAIN_DB - MIN_QGAIN_DB) * 128) / 6)) / (N_LEVELS_QGAIN - 1); - -/// Decode SILK gain indices to linear Q16 gains. -/// -/// Params: `gain_indices` decoded entropy indices, mutable `prev_index` state, -/// `conditional` first-subframe coding mode, and active `nb_subfr`. -/// Returns: decoded per-subframe gains in Q16. -pub(super) fn decode_gains( - gain_indices: &[i8], - prev_index: &mut i8, - conditional: bool, - nb_subfr: usize, -) -> [i32; super::tables::MAX_NB_SUBFR] { - let mut gains_q16 = [0i32; super::tables::MAX_NB_SUBFR]; - let mut prev = *prev_index as i32; - - for k in 0..nb_subfr { - if k == 0 && !conditional { - prev = (gain_indices[k] as i32).max(prev - 16); - } else { - let ind_tmp = gain_indices[k] as i32 + MIN_DELTA_GAIN_QUANT; - let double_step_size_threshold = 2 * MAX_DELTA_GAIN_QUANT - N_LEVELS_QGAIN + prev; - if ind_tmp > double_step_size_threshold { - prev += (ind_tmp << 1) - double_step_size_threshold; - } else { - prev += ind_tmp; - } - } - - prev = prev.clamp(0, N_LEVELS_QGAIN - 1); - gains_q16[k] = log2lin(smulwb(INV_SCALE_Q16, prev) + OFFSET); - } - - *prev_index = prev as i8; - gains_q16 -} - -/// Convert SILK log-domain gain to linear Q16. -/// -/// Params: `in_log_q7` log-domain value in Q7. -/// Returns: linear gain in Q16-compatible integer form. -fn log2lin(in_log_q7: i32) -> i32 { - if in_log_q7 < 0 { - return 0; - } - if in_log_q7 >= 3967 { - return i32::MAX; - } - - let mut out = 1i32 << (in_log_q7 >> 7); - let frac_q7 = in_log_q7 & 0x7F; - let curve = smlawb(frac_q7, smulbb(frac_q7, 128 - frac_q7), -174); - if in_log_q7 < 2048 { - out = add_rshift32(out, out.saturating_mul(curve), 7); - } else { - out = out.saturating_add((out >> 7).saturating_mul(curve)); - } - out -} - -/// Approximate `(a32 * (i16)b32) >> 16`. -/// -/// Params: signed `a32` and signed `b32`. -/// Returns: high-word signed product. -fn smulwb(a32: i32, b32: i32) -> i32 { - let b16 = b32 as i16 as i32; - let high = (a32 >> 16) * b16; - let low = ((a32 & 0xFFFF) * b16) >> 16; - high.wrapping_add(low) -} - -/// Approximate `a32 + ((b32 * (i16)c32) >> 16)`. -/// -/// Params: accumulator `a32`, multiplicand `b32`, and signed `c32`. -/// Returns: accumulated high-word product. -fn smlawb(a32: i32, b32: i32, c32: i32) -> i32 { - a32.wrapping_add(smulwb(b32, c32)) -} - -/// Multiply lower 16-bit halves of two signed values. -/// -/// Params: signed `a32` and `b32`. -/// Returns: signed 32-bit product. -fn smulbb(a32: i32, b32: i32) -> i32 { - (a32 as i16 as i32) * (b32 as i16 as i32) -} - -/// Add a rounded right-shifted value. -/// -/// Params: base `a`, addend `b`, and arithmetic `shift`. -/// Returns: `a + round(b >> shift)`. -fn add_rshift32(a: i32, b: i32, shift: usize) -> i32 { - a.saturating_add(rshift_round(b, shift)) -} - -/// Round a signed arithmetic right shift. -/// -/// Params: signed `value` and positive `shift`. -/// Returns: rounded shifted value. -fn rshift_round(value: i32, shift: usize) -> i32 { - if shift == 1 { - (value >> 1) + (value & 1) - } else { - ((value >> (shift - 1)) + 1) >> 1 - } -} diff --git a/vendor/opus-decoder/src/silk/lbrr.rs b/vendor/opus-decoder/src/silk/lbrr.rs deleted file mode 100644 index eee64789..00000000 --- a/vendor/opus-decoder/src/silk/lbrr.rs +++ /dev/null @@ -1,400 +0,0 @@ -//! SILK LBRR payload bit-consumption helpers (Phase 3a). - -use crate::Error; -use crate::entropy::EcDec; - -use super::entropy_tables; -use super::tables; - -const MAX_INTERNAL_FRAMES: usize = 3; -const MAX_NB_SUBFR: usize = 4; -const MAX_NB_SHELL_BLOCKS: usize = 20; -const TYPE_VOICED: i32 = 2; -const NLSF_STAGE2_ROW: usize = (2 * entropy_tables::NLSF_QUANT_MAX_AMPLITUDE + 1) as usize; - -/// Entropy-coding condition mode for SILK index parsing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(super) enum CondCoding { - /// Independent coding (`CODE_INDEPENDENTLY`). - Independently, - /// Independent coding without LTP scaling (`CODE_INDEPENDENTLY_NO_LTP_SCALING`). - IndependentlyNoLtpScaling, - /// Conditional coding (`CODE_CONDITIONALLY`). - Conditionally, -} - -/// Channel-local entropy state needed by SILK index decoding. -#[derive(Debug, Clone, Copy, Default)] -pub(super) struct ChannelState { - /// Internal SILK sample rate in kHz. - pub fs_khz: u32, - /// Number of subframes in current internal frame (2 for 10 ms, 4 for 20 ms). - pub nb_subfr: usize, - /// Previous decoded signal type for entropy context. - pub ec_prev_signal_type: i32, - /// Previous decoded lag index for delta-lag entropy path. - pub ec_prev_lag_index: i32, -} - -/// Minimal decoded side information needed to continue entropy parsing. -#[derive(Debug, Clone, Copy)] -pub(super) struct DecodedSideInfo { - /// Signal type (no-voice, unvoiced, voiced). - pub signal_type: i32, - /// Quantizer offset type. - pub quant_offset_type: i32, - /// Decoded NLSF quantization path for Phase 3b. - pub nlsf: DecodedNlsfIndices, - /// Decoded gain indices for all subframes. - pub gain_indices: [i8; MAX_NB_SUBFR], - /// Absolute pitch lag index for voiced frames. - pub lag_index: i16, - /// Pitch contour index for voiced frames. - pub contour_index: i8, - /// LTP periodicity index. - pub per_index: i8, - /// LTP codebook index per subframe. - pub ltp_indices: [i8; MAX_NB_SUBFR], - /// LTP scaling index. - pub ltp_scale_index: i8, - /// Excitation randomization seed. - pub seed: i8, -} - -/// Stored SILK NLSF indices (stage1 plus stage2 residuals). -#[derive(Debug, Clone, Copy)] -pub(super) struct DecodedNlsfIndices { - /// Packed indices as `[I1, res0, ..., res(order-1)]`. - pub values: [i8; tables::MAX_LPC_ORDER + 1], - /// Decoded interpolation factor in Q2. - pub interp_coef_q2: i8, -} - -/// Configure channel-state decode context for current SILK frame shape. -/// -/// Params: mutable `state`, internal sampling rate `fs_khz`, and `nb_subfr`. -/// Returns: nothing. -pub(super) fn configure_channel_state(state: &mut ChannelState, fs_khz: u32, nb_subfr: usize) { - state.fs_khz = fs_khz; - state.nb_subfr = nb_subfr; -} - -/// Decode SILK side indices (`silk_decode_indices`) for entropy consumption. -/// -/// Params: mutable channel `state`, range decoder `dec`, `frame_vad` for current -/// internal frame, `decode_lbrr` flag, and entropy `cond_coding` mode. -/// Returns: decoded `signal_type` and `quant_offset_type`. -pub(super) fn decode_indices( - state: &mut ChannelState, - dec: &mut EcDec<'_>, - frame_vad: bool, - decode_lbrr: bool, - cond_coding: CondCoding, -) -> Result { - let ix = if decode_lbrr || frame_vad { - dec.dec_icdf(&entropy_tables::TYPE_OFFSET_VAD_ICDF, 8) + 2 - } else { - dec.dec_icdf(&entropy_tables::TYPE_OFFSET_NO_VAD_ICDF, 8) - }; - let signal_type = ix >> 1; - let quant_offset_type = ix & 1; - let mut gain_indices = [0i8; MAX_NB_SUBFR]; - let mut lag_index = 0i16; - let mut contour_index = 0i8; - let mut per_index = 0i8; - let mut ltp_indices = [0i8; MAX_NB_SUBFR]; - let mut ltp_scale_index = 0i8; - - if cond_coding == CondCoding::Conditionally { - gain_indices[0] = dec.dec_icdf(&entropy_tables::DELTA_GAIN_ICDF, 8) as i8; - } else { - gain_indices[0] = - (dec.dec_icdf(&entropy_tables::GAIN_ICDF[signal_type as usize], 8) << 3) as i8; - gain_indices[0] += dec.dec_icdf(&entropy_tables::UNIFORM8_ICDF, 8) as i8; - } - for gain in gain_indices.iter_mut().take(state.nb_subfr).skip(1) { - *gain = dec.dec_icdf(&entropy_tables::DELTA_GAIN_ICDF, 8) as i8; - } - - let cb = entropy_tables::nlsf_codebook(state.fs_khz); - let cb1_offset = ((signal_type >> 1) as usize) * cb.n_vectors; - let cb1_index = dec.dec_icdf(&cb.cb1_icdf[cb1_offset..], 8) as usize; - if cb1_index >= cb.n_vectors { - return Err(Error::BadPacket); - } - let mut nlsf_indices = [0i8; tables::MAX_LPC_ORDER + 1]; - nlsf_indices[0] = cb1_index as i8; - - let mut ec_ix = [0usize; 16]; - unpack_nlsf_ec_ix(&mut ec_ix, cb, cb1_index)?; - for (i, &row) in ec_ix.iter().take(cb.order).enumerate() { - let mut stage2 = dec.dec_icdf(&cb.ec_icdf[row..], 8); - if stage2 == 0 { - stage2 -= dec.dec_icdf(&entropy_tables::NLSF_EXT_ICDF, 8); - } else if stage2 == 2 * entropy_tables::NLSF_QUANT_MAX_AMPLITUDE { - stage2 += dec.dec_icdf(&entropy_tables::NLSF_EXT_ICDF, 8); - } - nlsf_indices[i + 1] = (stage2 - entropy_tables::NLSF_QUANT_MAX_AMPLITUDE) as i8; - } - - let mut interp_coef_q2 = 4i8; - if state.nb_subfr == MAX_NB_SUBFR { - interp_coef_q2 = dec.dec_icdf(&entropy_tables::NLSF_INTERP_FACTOR_ICDF, 8) as i8; - } - - if signal_type == TYPE_VOICED { - let mut decode_absolute_lag = true; - if cond_coding == CondCoding::Conditionally && state.ec_prev_signal_type == TYPE_VOICED { - let mut delta_lag = dec.dec_icdf(&entropy_tables::PITCH_DELTA_ICDF, 8); - if delta_lag > 0 { - delta_lag -= 9; - state.ec_prev_lag_index += delta_lag; - decode_absolute_lag = false; - } - } - if decode_absolute_lag { - state.ec_prev_lag_index = dec.dec_icdf(&entropy_tables::PITCH_LAG_ICDF, 8) - * ((state.fs_khz as i32) >> 1) - + dec.dec_icdf(entropy_tables::pitch_lag_low_bits_icdf(state.fs_khz), 8); - } - lag_index = state.ec_prev_lag_index as i16; - - contour_index = dec.dec_icdf( - entropy_tables::pitch_contour_icdf(state.fs_khz, state.nb_subfr), - 8, - ) as i8; - per_index = dec.dec_icdf(&entropy_tables::LTP_PER_INDEX_ICDF, 8) as i8; - let ltp_gain_icdf = - entropy_tables::ltp_gain_icdf(per_index as usize).ok_or(Error::BadPacket)?; - for ltp_index in ltp_indices.iter_mut().take(state.nb_subfr) { - *ltp_index = dec.dec_icdf(ltp_gain_icdf, 8) as i8; - } - if cond_coding == CondCoding::Independently { - ltp_scale_index = dec.dec_icdf(&entropy_tables::LTPSCALE_ICDF, 8) as i8; - } - } - - state.ec_prev_signal_type = signal_type; - let seed = dec.dec_icdf(&entropy_tables::UNIFORM4_ICDF, 8) as i8; - debug_trace!("silk lbrr side info consumed marker={}", 0); - - Ok(DecodedSideInfo { - signal_type, - quant_offset_type, - nlsf: DecodedNlsfIndices { - values: nlsf_indices, - interp_coef_q2, - }, - gain_indices, - lag_index, - contour_index, - per_index, - ltp_indices, - ltp_scale_index, - seed, - }) -} - -/// Decode SILK pulse payload (`silk_decode_pulses`) for entropy consumption. -/// -/// Params: range decoder `dec`, decoded `signal_type`, `quant_offset_type`, -/// and internal `frame_length` in samples. -/// Returns: decoded pulse magnitudes with signs, truncated to `frame_length`. -pub(super) fn decode_pulses( - dec: &mut EcDec<'_>, - signal_type: i32, - quant_offset_type: i32, - frame_length: usize, -) -> Result, Error> { - let rate_level = dec.dec_icdf( - &entropy_tables::RATE_LEVELS_ICDF[(signal_type >> 1) as usize], - 8, - ) as usize; - if rate_level >= entropy_tables::N_RATE_LEVELS { - return Err(Error::BadPacket); - } - - let mut iter = frame_length / entropy_tables::SHELL_CODEC_FRAME_LENGTH; - if iter * entropy_tables::SHELL_CODEC_FRAME_LENGTH < frame_length { - iter += 1; - } - if iter > MAX_NB_SHELL_BLOCKS { - return Err(Error::BadPacket); - } - - let mut sum_pulses = [0i32; MAX_NB_SHELL_BLOCKS]; - let mut n_lshifts = [0i32; MAX_NB_SHELL_BLOCKS]; - for i in 0..iter { - sum_pulses[i] = dec.dec_icdf(&entropy_tables::PULSES_PER_BLOCK_ICDF[rate_level], 8); - while sum_pulses[i] == entropy_tables::SILK_MAX_PULSES + 1 { - n_lshifts[i] += 1; - let row = entropy_tables::N_RATE_LEVELS - 1; - let offset = if n_lshifts[i] == 10 { 1 } else { 0 }; - sum_pulses[i] = dec.dec_icdf(&entropy_tables::PULSES_PER_BLOCK_ICDF[row][offset..], 8); - } - } - - let mut pulses = vec![0i16; iter * entropy_tables::SHELL_CODEC_FRAME_LENGTH]; - for i in 0..iter { - let block = &mut pulses[i * entropy_tables::SHELL_CODEC_FRAME_LENGTH - ..(i + 1) * entropy_tables::SHELL_CODEC_FRAME_LENGTH]; - if sum_pulses[i] > 0 { - shell_decoder(block, dec, sum_pulses[i])?; - } else { - block.fill(0); - } - } - - for i in 0..iter { - if n_lshifts[i] > 0 { - let block = &mut pulses[i * entropy_tables::SHELL_CODEC_FRAME_LENGTH - ..(i + 1) * entropy_tables::SHELL_CODEC_FRAME_LENGTH]; - for sample in block.iter_mut() { - let mut abs_q = *sample as i32; - for _ in 0..n_lshifts[i] { - abs_q = (abs_q << 1) + dec.dec_icdf(&entropy_tables::LSB_ICDF, 8); - } - *sample = abs_q as i16; - } - sum_pulses[i] |= n_lshifts[i] << 5; - } - } - - decode_signs( - dec, - &mut pulses, - signal_type, - quant_offset_type, - &sum_pulses, - iter, - )?; - pulses.truncate(frame_length); - Ok(pulses) -} - -/// Expand NLSF selector entries to stage-2 row offsets. -/// -/// Params: mutable `out_rows`, codebook descriptor `cb`, and `cb1_index`. -/// Returns: `Ok(())` on success, `BadPacket` on malformed selector bounds. -fn unpack_nlsf_ec_ix( - out_rows: &mut [usize; 16], - cb: &entropy_tables::NlsfEntropyCodebook, - cb1_index: usize, -) -> Result<(), Error> { - let sel_base = cb1_index * cb.order / 2; - if sel_base + cb.order / 2 > cb.ec_sel.len() { - return Err(Error::BadPacket); - } - for i in (0..cb.order).step_by(2) { - let entry = cb.ec_sel[sel_base + i / 2]; - out_rows[i] = (((entry >> 1) & 7) as usize) * NLSF_STAGE2_ROW; - out_rows[i + 1] = (((entry >> 5) & 7) as usize) * NLSF_STAGE2_ROW; - } - Ok(()) -} - -/// Decode one split in shell-tree coding. -/// -/// Params: mutable output split tuple, range decoder `dec`, parent pulse count `p`, -/// and shell table slice. -/// Returns: decoded child pulse counts. -fn decode_split(dec: &mut EcDec<'_>, p: i32, shell_table: &[u8]) -> Result<(i16, i16), Error> { - if p <= 0 { - return Ok((0, 0)); - } - let p_usize = p as usize; - if p_usize >= entropy_tables::SHELL_CODE_TABLE_OFFSETS.len() { - return Err(Error::BadPacket); - } - let offset = entropy_tables::SHELL_CODE_TABLE_OFFSETS[p_usize] as usize; - if offset >= shell_table.len() { - return Err(Error::BadPacket); - } - let child1 = dec.dec_icdf(&shell_table[offset..], 8) as i16; - let child2 = (p as i16).saturating_sub(child1); - Ok((child1, child2)) -} - -/// Decode one 16-sample shell block (`silk_shell_decoder`). -/// -/// Params: mutable `out_block`, range decoder `dec`, and total pulses `pulses4`. -/// Returns: nothing. -fn shell_decoder(out_block: &mut [i16], dec: &mut EcDec<'_>, pulses4: i32) -> Result<(), Error> { - if out_block.len() != entropy_tables::SHELL_CODEC_FRAME_LENGTH { - return Err(Error::BadPacket); - } - let (p30, p31) = decode_split(dec, pulses4, &entropy_tables::SHELL_CODE_TABLE3)?; - let (p20, p21) = decode_split(dec, p30 as i32, &entropy_tables::SHELL_CODE_TABLE2)?; - let (p10, p11) = decode_split(dec, p20 as i32, &entropy_tables::SHELL_CODE_TABLE1)?; - let (p00, p01) = decode_split(dec, p10 as i32, &entropy_tables::SHELL_CODE_TABLE0)?; - let (p02, p03) = decode_split(dec, p11 as i32, &entropy_tables::SHELL_CODE_TABLE0)?; - let (p12, p13) = decode_split(dec, p21 as i32, &entropy_tables::SHELL_CODE_TABLE1)?; - let (p04, p05) = decode_split(dec, p12 as i32, &entropy_tables::SHELL_CODE_TABLE0)?; - let (p06, p07) = decode_split(dec, p13 as i32, &entropy_tables::SHELL_CODE_TABLE0)?; - let (p22, p23) = decode_split(dec, p31 as i32, &entropy_tables::SHELL_CODE_TABLE2)?; - let (p14, p15) = decode_split(dec, p22 as i32, &entropy_tables::SHELL_CODE_TABLE1)?; - let (p08, p09) = decode_split(dec, p14 as i32, &entropy_tables::SHELL_CODE_TABLE0)?; - let (p10b, p11b) = decode_split(dec, p15 as i32, &entropy_tables::SHELL_CODE_TABLE0)?; - let (p16, p17) = decode_split(dec, p23 as i32, &entropy_tables::SHELL_CODE_TABLE1)?; - let (p12b, p13b) = decode_split(dec, p16 as i32, &entropy_tables::SHELL_CODE_TABLE0)?; - let (p14b, p15b) = decode_split(dec, p17 as i32, &entropy_tables::SHELL_CODE_TABLE0)?; - out_block.copy_from_slice(&[ - p00, p01, p02, p03, p04, p05, p06, p07, p08, p09, p10b, p11b, p12b, p13b, p14b, p15b, - ]); - Ok(()) -} - -/// Decode pulse signs (`silk_decode_signs`) for already decoded magnitudes. -/// -/// Params: range decoder `dec`, mutable `pulses` blocks, `signal_type`, -/// `quant_offset_type`, per-block `sum_pulses`, and active `iter` block count. -/// Returns: nothing. -fn decode_signs( - dec: &mut EcDec<'_>, - pulses: &mut [i16], - signal_type: i32, - quant_offset_type: i32, - sum_pulses: &[i32; MAX_NB_SHELL_BLOCKS], - iter: usize, -) -> Result<(), Error> { - if pulses.len() < iter * entropy_tables::SHELL_CODEC_FRAME_LENGTH { - return Err(Error::BadPacket); - } - let base = 7 * (quant_offset_type + (signal_type << 1)) as usize; - if base + 6 >= entropy_tables::SIGN_ICDF.len() { - return Err(Error::BadPacket); - } - for (i, &p) in sum_pulses.iter().take(iter).enumerate() { - if p <= 0 { - continue; - } - let idx = (p & 0x1F).min(6) as usize; - let mut icdf = [0u8; 2]; - icdf[0] = entropy_tables::SIGN_ICDF[base + idx]; - let block = &mut pulses[i * entropy_tables::SHELL_CODEC_FRAME_LENGTH - ..(i + 1) * entropy_tables::SHELL_CODEC_FRAME_LENGTH]; - for sample in block.iter_mut() { - if *sample > 0 { - let sign = dec.dec_icdf(&icdf, 8); - if sign == 0 { - *sample = -*sample; - } - } - } - } - Ok(()) -} - -/// Check whether parsed packet has any LBRR frame flagged. -/// -/// Params: per-channel per-frame LBRR flag matrix and coded channel count. -/// Returns: true when at least one LBRR frame is present. -pub(super) fn any_lbrr( - lbrr_flags: &[[bool; MAX_INTERNAL_FRAMES]; 2], - packet_channels: usize, -) -> bool { - lbrr_flags - .iter() - .take(packet_channels) - .any(|ch| ch.iter().any(|&flag| flag)) -} diff --git a/vendor/opus-decoder/src/silk/lpc.rs b/vendor/opus-decoder/src/silk/lpc.rs deleted file mode 100644 index 94150a9b..00000000 --- a/vendor/opus-decoder/src/silk/lpc.rs +++ /dev/null @@ -1,335 +0,0 @@ -//! SILK fixed-point LPC helpers. - -use super::tables::{LSF_COS_TAB, MAX_LPC_ORDER}; - -const QA: usize = 16; -const INV_PRED_GAIN_QA: usize = 24; -const MAX_LPC_STABILIZE_ITERATIONS: usize = 16; -const A_LIMIT_QA24: i32 = 16_773_022; -const MAX_PREDICTION_POWER_GAIN_INV_Q30: i32 = 107_374; - -/// Convert NLSF values in Q15 to LPC coefficients in Q12. -/// -/// Params: mutable `lpc_q12`, stabilized `nlsf_q15`, and LPC `order` (10 or 16). -/// Returns: nothing; `lpc_q12[..order]` is overwritten. -pub(super) fn nlsf2a(lpc_q12: &mut [i16], nlsf_q15: &[i16], order: usize) { - let ordering = match order { - 10 => &[0usize, 9, 6, 3, 4, 5, 8, 1, 2, 7][..], - 16 => &[0usize, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1][..], - _ => panic!("unsupported LPC order {order}"), - }; - - let mut cos_lsf_qa = [0i32; MAX_LPC_ORDER]; - for k in 0..order { - let f_int = (nlsf_q15[k] as i32 >> 8) as usize; - let f_frac = nlsf_q15[k] as i32 - ((f_int as i32) << 8); - let cos_val = LSF_COS_TAB[f_int] as i32; - let delta = LSF_COS_TAB[f_int + 1] as i32 - cos_val; - let interp = (cos_val << 8) + delta * f_frac; - cos_lsf_qa[ordering[k]] = rshift_round(interp, 20 - QA); - } - - let dd = order / 2; - let mut p = [0i32; MAX_LPC_ORDER / 2 + 1]; - let mut q = [0i32; MAX_LPC_ORDER / 2 + 1]; - nlsf2a_find_poly(&mut p, &cos_lsf_qa[0..], dd); - nlsf2a_find_poly(&mut q, &cos_lsf_qa[1..], dd); - - let mut a32_qa1 = [0i32; MAX_LPC_ORDER]; - for k in 0..dd { - let ptmp = p[k + 1] + p[k]; - let qtmp = q[k + 1] - q[k]; - a32_qa1[k] = -qtmp - ptmp; - a32_qa1[order - k - 1] = qtmp - ptmp; - } - - lpc_fit(lpc_q12, &mut a32_qa1, 12, QA + 1, order); - for i in 0..MAX_LPC_STABILIZE_ITERATIONS { - let inv_gain = lpc_inv_pred_gain(&lpc_q12[..order], order); - if inv_gain != 0 { - break; - } - bw_expand_32(&mut a32_qa1[..order], order, 65_536 - (2 << i)); - for k in 0..order { - lpc_q12[k] = rshift_round(a32_qa1[k], QA + 1 - 12) as i16; - } - } -} - -/// Compute inverse prediction gain and reject unstable LPC filters. -/// -/// Params: `lpc_q12` LPC coefficients in Q12 and LPC `order`. -/// Returns: inverse prediction gain in Q30, or `0` if unstable. -pub(super) fn lpc_inv_pred_gain(lpc_q12: &[i16], order: usize) -> i32 { - let mut a_tmp_qa = [0i32; MAX_LPC_ORDER]; - let mut dc_resp = 0i32; - for k in 0..order { - dc_resp += lpc_q12[k] as i32; - a_tmp_qa[k] = (lpc_q12[k] as i32) << (INV_PRED_GAIN_QA - 12); - } - if dc_resp >= 4096 { - return 0; - } - lpc_inverse_pred_gain_qa(&mut a_tmp_qa[..order], order) -} - -/// Apply Q16 bandwidth expansion to Q12 LPC coefficients. -/// -/// Params: mutable `lpc_q12`, LPC `order`, and `chirp_q16` factor. -/// Returns: nothing; coefficients are scaled in place. -#[allow(dead_code)] -pub(super) fn bw_expand(lpc_q12: &mut [i16], order: usize, chirp_q16: i32) { - let mut chirp_q16_local = chirp_q16; - let chirp_minus_one_q16 = chirp_q16_local - 65_536; - for coeff in lpc_q12.iter_mut().take(order.saturating_sub(1)) { - *coeff = rshift_round(((chirp_q16_local as i64) * (*coeff as i64)) as i32, 16) as i16; - chirp_q16_local += rshift_round( - ((chirp_q16_local as i64) * (chirp_minus_one_q16 as i64)) as i32, - 16, - ); - } - lpc_q12[order - 1] = rshift_round( - ((chirp_q16_local as i64) * (lpc_q12[order - 1] as i64)) as i32, - 16, - ) as i16; -} - -/// Build the even or odd Chebyshev polynomial used by `nlsf2a`. -/// -/// Params: mutable `out`, interleaved `c_lsf_qa`, and half-order `dd`. -/// Returns: nothing; `out[..=dd]` is written. -fn nlsf2a_find_poly(out: &mut [i32; MAX_LPC_ORDER / 2 + 1], c_lsf_qa: &[i32], dd: usize) { - out[0] = 1 << QA; - out[1] = -c_lsf_qa[0]; - for k in 1..dd { - let ftmp = c_lsf_qa[2 * k]; - out[k + 1] = (out[k - 1] << 1) - rshift_round64((ftmp as i64) * out[k] as i64, QA); - for n in (2..=k).rev() { - out[n] += out[n - 2] - rshift_round64((ftmp as i64) * out[n - 1] as i64, QA); - } - out[1] -= ftmp; - } -} - -/// Fit wide intermediate LPC coefficients into `i16` Q12 range. -/// -/// Params: mutable `lpc_q12`, mutable `a_qin`, output Q `qout`, input Q `qin`, and LPC `order`. -/// Returns: nothing; `lpc_q12[..order]` receives clipped or chirped coefficients. -fn lpc_fit( - lpc_q12: &mut [i16], - a_qin: &mut [i32; MAX_LPC_ORDER], - qout: usize, - qin: usize, - order: usize, -) { - let mut peak_index = 0usize; - for _ in 0..10 { - let mut max_abs = 0i32; - for (idx, &value) in a_qin.iter().take(order).enumerate() { - let abs_value = value.saturating_abs(); - if abs_value > max_abs { - max_abs = abs_value; - peak_index = idx; - } - } - let shifted = rshift_round(max_abs, qin - qout); - if shifted <= i16::MAX as i32 { - for k in 0..order { - lpc_q12[k] = rshift_round(a_qin[k], qin - qout) as i16; - } - return; - } - - let capped = shifted.min(163_838); - let numerator = ((capped - i16::MAX as i32) as i64) << 14; - let denominator = ((capped as i64) * (peak_index as i64 + 1)) >> 2; - let chirp_q16 = 65_470 - (numerator / denominator) as i32; - bw_expand_32(&mut a_qin[..order], order, chirp_q16); - } - - for k in 0..order { - let rounded = rshift_round(a_qin[k], qin - qout); - let clipped = rounded.clamp(i16::MIN as i32, i16::MAX as i32) as i16; - lpc_q12[k] = clipped; - a_qin[k] = (clipped as i32) << (qin - qout); - } -} - -/// Apply bandwidth expansion directly to 32-bit LPC intermediates. -/// -/// Params: mutable `ar_qa1`, LPC `order`, and `chirp_q16` factor. -/// Returns: nothing; coefficients are chirped in place. -fn bw_expand_32(ar_qa1: &mut [i32], order: usize, chirp_q16: i32) { - let mut chirp_q16_local = chirp_q16; - let chirp_minus_one_q16 = chirp_q16_local - 65_536; - for coeff in ar_qa1.iter_mut().take(order.saturating_sub(1)) { - *coeff = ((*coeff as i64 * chirp_q16_local as i64) >> 16) as i32; - chirp_q16_local += rshift_round( - ((chirp_q16_local as i64) * (chirp_minus_one_q16 as i64)) as i32, - 16, - ); - } - ar_qa1[order - 1] = ((ar_qa1[order - 1] as i64 * chirp_q16_local as i64) >> 16) as i32; -} - -/// Port of `LPC_inverse_pred_gain_QA_c` for Q24 LPC coefficients. -/// -/// Params: mutable `a_qa` scratch LPC vector and LPC `order`. -/// Returns: inverse prediction gain in Q30, or `0` when unstable. -fn lpc_inverse_pred_gain_qa(a_qa: &mut [i32], order: usize) -> i32 { - let mut inv_gain_q30 = 1 << 30; - for k in (1..order).rev() { - let coeff = a_qa[k]; - if !(-A_LIMIT_QA24..=A_LIMIT_QA24).contains(&coeff) { - return 0; - } - - let rc_q31 = -(coeff << (31 - INV_PRED_GAIN_QA)); - let rc_mult1_q30 = (1 << 30) - smmul(rc_q31, rc_q31); - inv_gain_q30 = smmul(inv_gain_q30, rc_mult1_q30) << 2; - if inv_gain_q30 < MAX_PREDICTION_POWER_GAIN_INV_Q30 { - return 0; - } - - let mult2_q = 32 - (rc_mult1_q30.unsigned_abs().leading_zeros() as usize); - let rc_mult2 = inverse32_var_q(rc_mult1_q30, mult2_q + 30); - for n in 0..((k + 1) >> 1) { - let tmp1 = a_qa[n]; - let tmp2 = a_qa[k - n - 1]; - let lhs = tmp1.saturating_sub(mul32_frac_q(tmp2, rc_q31, 31)); - let rhs = tmp2.saturating_sub(mul32_frac_q(tmp1, rc_q31, 31)); - let next0 = rshift_round64((lhs as i64) * rc_mult2 as i64, mult2_q); - let next1 = rshift_round64((rhs as i64) * rc_mult2 as i64, mult2_q); - a_qa[n] = next0; - a_qa[k - n - 1] = next1; - } - } - - if !(-A_LIMIT_QA24..=A_LIMIT_QA24).contains(&a_qa[0]) { - return 0; - } - let rc_q31 = -(a_qa[0] << (31 - INV_PRED_GAIN_QA)); - let rc_mult1_q30 = (1 << 30) - smmul(rc_q31, rc_q31); - inv_gain_q30 = smmul(inv_gain_q30, rc_mult1_q30) << 2; - if inv_gain_q30 < MAX_PREDICTION_POWER_GAIN_INV_Q30 { - 0 - } else { - inv_gain_q30 - } -} - -/// Multiply two signed 32-bit values and keep the high 32 bits. -/// -/// Params: signed `a` and `b`. -/// Returns: top-word signed product. -fn smmul(a: i32, b: i32) -> i32 { - (((a as i64) * (b as i64)) >> 32) as i32 -} - -/// Multiply by a Q-domain fractional value. -/// -/// Params: signed `a`, signed `b`, and fractional `q`. -/// Returns: rounded signed product in the original Q-domain. -fn mul32_frac_q(a: i32, b: i32, q: usize) -> i32 { - rshift_round64((a as i64) * (b as i64), q) -} - -/// Compute a saturating absolute value for normalization. -/// -/// Params: signed `value`. -/// Returns: non-negative magnitude, saturating `i32::MIN`. -fn abs32_for_clz(value: i32) -> i32 { - if value == i32::MIN { - i32::MAX - } else { - value.abs() - } -} - -/// Multiply by the signed low 16-bit half of `b32` and keep high word. -/// -/// Params: signed `a32` and signed `b32`. -/// Returns: high-word signed product. -fn smulwb(a32: i32, b32: i32) -> i32 { - let b16 = b32 as i16 as i32; - let high = (a32 >> 16) * b16; - let low = ((a32 & 0xFFFF) * b16) >> 16; - high.wrapping_add(low) -} - -/// Approximate `a32 + ((b32 * c32) >> 16)` with wraparound semantics. -/// -/// Params: accumulator `a32` and signed multiplicands `b32` and `c32`. -/// Returns: accumulated high-word product. -fn smlaww(a32: i32, b32: i32, c32: i32) -> i32 { - a32.wrapping_add(((b32 as i64 * c32 as i64) >> 16) as i32) -} - -/// Left-shift with saturation. -/// -/// Params: signed `value` and non-negative `shift`. -/// Returns: left-shifted value clamped to `i32`. -fn lshift_sat32(value: i32, shift: usize) -> i32 { - if shift >= 31 { - if value > 0 { - i32::MAX - } else if value < 0 { - i32::MIN - } else { - 0 - } - } else { - value - .clamp(i32::MIN >> shift, i32::MAX >> shift) - .wrapping_shl(shift as u32) - } -} - -/// Approximate `(1 << q_res) / value` in fixed-point. -/// -/// Params: signed non-zero `value` and target result Q `q_res`. -/// Returns: fixed-point inverse approximation. -fn inverse32_var_q(value: i32, q_res: usize) -> i32 { - debug_assert!(value != 0); - debug_assert!(q_res > 0); - - let b_headrm = abs32_for_clz(value).leading_zeros() as usize - 1; - let b32_nrm = value << b_headrm; - let b32_inv = (i32::MAX >> 2) / (b32_nrm >> 16); - let mut result = b32_inv << 16; - let err_q32 = ((1i32 << 29) - smulwb(b32_nrm, b32_inv)) << 3; - result = smlaww(result, err_q32, b32_inv); - - let lshift = 61isize - b_headrm as isize - q_res as isize; - if lshift <= 0 { - lshift_sat32(result, (-lshift) as usize) - } else if lshift < 32 { - result >> lshift - } else { - 0 - } -} - -/// Round a signed 32-bit arithmetic right shift. -/// -/// Params: signed `value` and positive `shift`. -/// Returns: rounded arithmetic shift result. -fn rshift_round(value: i32, shift: usize) -> i32 { - if shift == 1 { - (value >> 1) + (value & 1) - } else { - ((value >> (shift - 1)) + 1) >> 1 - } -} - -/// Round a signed 64-bit arithmetic right shift. -/// -/// Params: signed `value` and positive `shift`. -/// Returns: rounded arithmetic shift result. -fn rshift_round64(value: i64, shift: usize) -> i32 { - if shift == 1 { - ((value >> 1) + (value & 1)) as i32 - } else { - (((value >> (shift - 1)) + 1) >> 1) as i32 - } -} diff --git a/vendor/opus-decoder/src/silk/ltp.rs b/vendor/opus-decoder/src/silk/ltp.rs deleted file mode 100644 index d6a247a2..00000000 --- a/vendor/opus-decoder/src/silk/ltp.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! SILK long-term prediction helpers. -#![allow(clippy::too_many_arguments)] - -use super::tables::{ - LTP_ORDER, LTP_SCALES_Q14, LTP_VQ_0_Q7, LTP_VQ_1_Q7, LTP_VQ_2_Q7, MAX_NB_SUBFR, -}; - -/// Decode LTP codebook entries to Q14 taps for each active subframe. -/// -/// Params: `per_index` codebook selector, per-subframe `ltp_indices`, active `nb_subfr`, -/// and mutable `ltp_scale_q14` output. -/// Returns: decoded per-subframe LTP taps in Q14. -pub(super) fn decode_ltp_coeffs( - per_index: i8, - ltp_indices: &[i8; MAX_NB_SUBFR], - nb_subfr: usize, - ltp_scale_index: i8, -) -> ([[i16; LTP_ORDER]; MAX_NB_SUBFR], i32) { - let mut coeffs_q14 = [[0i16; LTP_ORDER]; MAX_NB_SUBFR]; - let ltp_scale_q14 = if per_index >= 0 { - LTP_SCALES_Q14[ltp_scale_index as usize] as i32 - } else { - 0 - }; - - for k in 0..nb_subfr { - let row = codebook_row(per_index, ltp_indices[k] as usize); - for i in 0..LTP_ORDER { - coeffs_q14[k][i] = (row[i] as i16) << 7; - } - } - - (coeffs_q14, ltp_scale_q14) -} - -/// Apply the 5-tap SILK LTP filter to a subframe. -/// -/// Params: mutable `pres_q14`, Q14 `exc_q14`, Q14 `ltp_q15` history buffer, write `buf_idx`, -/// decoded `pitch_lag`, active `subfr_length`, and subframe `ltp_coefs_q14`. -/// Returns: nothing; `pres_q14` and `ltp_q15` are updated in place. -pub(super) fn ltp_filter( - pres_q14: &mut [i32], - exc_q14: &[i32], - ltp_q15: &mut [i32], - buf_idx: &mut usize, - pitch_lag: usize, - subfr_length: usize, - ltp_coefs_q14: &[i16; LTP_ORDER], - _packet_idx: usize, - _frame_idx: usize, - _subframe_idx: usize, -) { - let start = *buf_idx - pitch_lag + (LTP_ORDER / 2); - for i in 0..subfr_length { - let pred_base = start + i; - let mut ltp_pred_q13 = 2i32; - ltp_pred_q13 = smlawb(ltp_pred_q13, ltp_q15[pred_base], ltp_coefs_q14[0] as i32); - ltp_pred_q13 = smlawb( - ltp_pred_q13, - ltp_q15[pred_base - 1], - ltp_coefs_q14[1] as i32, - ); - ltp_pred_q13 = smlawb( - ltp_pred_q13, - ltp_q15[pred_base - 2], - ltp_coefs_q14[2] as i32, - ); - ltp_pred_q13 = smlawb( - ltp_pred_q13, - ltp_q15[pred_base - 3], - ltp_coefs_q14[3] as i32, - ); - ltp_pred_q13 = smlawb( - ltp_pred_q13, - ltp_q15[pred_base - 4], - ltp_coefs_q14[4] as i32, - ); - pres_q14[i] = exc_q14[i].saturating_add(ltp_pred_q13 << 1); - ltp_q15[*buf_idx] = pres_q14[i] << 1; - *buf_idx += 1; - } -} - -/// Select one LTP VQ row from the periodicity codebook. -/// -/// Params: `per_index` codebook selector and row `index`. -/// Returns: reference to the codebook row in Q7. -fn codebook_row(per_index: i8, index: usize) -> &'static [i8; LTP_ORDER] { - match per_index { - 0 => <P_VQ_0_Q7[index], - 1 => <P_VQ_1_Q7[index], - 2 => <P_VQ_2_Q7[index], - _ => panic!("unsupported LTP periodicity index {per_index}"), - } -} - -/// Approximate `a32 + ((b32 * (i16)c32) >> 16)`. -/// -/// Params: accumulator `a32`, multiplicand `b32`, and signed `c32`. -/// Returns: accumulated high-word product. -fn smlawb(a32: i32, b32: i32, c32: i32) -> i32 { - let c16 = c32 as i16 as i32; - let high = (b32 >> 16) * c16; - let low = ((b32 & 0xFFFF) * c16) >> 16; - a32.wrapping_add(high).wrapping_add(low) -} diff --git a/vendor/opus-decoder/src/silk/mod.rs b/vendor/opus-decoder/src/silk/mod.rs deleted file mode 100644 index 7db04e23..00000000 --- a/vendor/opus-decoder/src/silk/mod.rs +++ /dev/null @@ -1,1349 +0,0 @@ -//! SILK decoder scaffolding (Phase 3a). -//! -//! This module decodes SILK packet headers, side information, and synthesis -//! state while keeping the implementation close to the reference decoder. -#![allow(clippy::too_many_arguments, clippy::needless_borrow)] - -use crate::Error; -use crate::entropy::EcDec; - -mod decode_core; -mod entropy_tables; -mod gain; -mod lbrr; -mod lpc; -mod ltp; -mod nlsf; -mod pitch; -mod plc; -mod resampler; -mod resampler_private; -mod resampler_rom; -mod stereo; -mod tables; - -const MAX_INTERNAL_FRAMES: usize = 3; -const MAX_LPC_HALVES: usize = 2; -const MAX_API_FRAME_SAMPLES_48K: usize = 960 * MAX_INTERNAL_FRAMES; -const TYPE_VOICED: i32 = 2; - -/// Result of decoding one SILK Opus frame. -#[derive(Debug, Clone, Copy)] -pub(crate) struct SilkFrameDecode { - /// Number of decoded samples per output channel. - pub samples_per_channel: usize, - /// Whether a top-level SILK redundancy trailer was consumed. - pub consumed_redundancy: bool, - /// Number of trailing redundancy bytes reserved for CELT. - pub redundancy_bytes: usize, - /// Transition direction flag from the top-level redundancy trailer. - pub celt_to_silk: bool, -} - -#[derive(Debug, Clone, Copy, Default)] -struct ChannelHeader { - vad_flags: [bool; MAX_INTERNAL_FRAMES], - lbrr_flags: [bool; MAX_INTERNAL_FRAMES], -} - -#[derive(Debug, Clone, Copy, Default)] -struct ParsedHeader { - channels: [ChannelHeader; 2], -} - -/// Stateful SILK decoder shell used by top-level Opus decoder. -#[derive(Debug, Clone)] -pub(crate) struct SilkDecoder { - fs_hz: u32, - channels: u8, - final_range: u32, - lbrr_state: [lbrr::ChannelState; 2], - core_state: [decode_core::SilkChannelState; 2], - resampler_state: [Option; 2], - stereo_state: stereo::StereoState, - prev_nlsf_q15: [[i16; tables::MAX_LPC_ORDER]; 2], - pred_coef_q12: [[[[i16; tables::MAX_LPC_ORDER]; MAX_LPC_HALVES]; MAX_INTERNAL_FRAMES]; 2], - first_frame_after_reset: [bool; 2], - prev_decode_only_middle: bool, - prev_packet_channels: u8, -} - -impl SilkDecoder { - /// Create SILK decoder state. - /// - /// Params: `fs_hz` is Opus API output sample rate, `channels` is output channels. - /// Returns: initialized SILK decoder state with cleared range state. - pub fn new(fs_hz: u32, channels: u8) -> Self { - Self { - fs_hz, - channels, - final_range: 0, - lbrr_state: [lbrr::ChannelState::default(), lbrr::ChannelState::default()], - core_state: [ - decode_core::SilkChannelState::default(), - decode_core::SilkChannelState::default(), - ], - resampler_state: [None, None], - stereo_state: stereo::StereoState::default(), - prev_nlsf_q15: [[0; tables::MAX_LPC_ORDER]; 2], - pred_coef_q12: [[[[0; tables::MAX_LPC_ORDER]; MAX_LPC_HALVES]; MAX_INTERNAL_FRAMES]; 2], - first_frame_after_reset: [true; 2], - prev_decode_only_middle: false, - prev_packet_channels: 1, - } - } - - /// Reset SILK decoder persistent state. - /// - /// Params: none. - /// Returns: nothing. - pub fn reset(&mut self) { - self.final_range = 0; - self.lbrr_state = [lbrr::ChannelState::default(), lbrr::ChannelState::default()]; - self.core_state = [ - decode_core::SilkChannelState::default(), - decode_core::SilkChannelState::default(), - ]; - self.resampler_state = [None, None]; - self.stereo_state = stereo::StereoState::default(); - self.prev_nlsf_q15 = [[0; tables::MAX_LPC_ORDER]; 2]; - self.pred_coef_q12 = - [[[[0; tables::MAX_LPC_ORDER]; MAX_LPC_HALVES]; MAX_INTERNAL_FRAMES]; 2]; - self.first_frame_after_reset = [true; 2]; - self.prev_decode_only_middle = false; - self.prev_packet_channels = 1; - } - - /// Return SILK range coder final state from last decoded frame. - /// - /// Params: none. - /// Returns: `final_range` value for conformance checks. - pub fn final_range(&self) -> u32 { - self.final_range - } - - /// Update the per-channel rewhitening history after PLC synthesis. - /// - /// Params: mutable channel `state`, concealed internal `frame`, and internal `fs_khz`. - /// Returns: nothing; the history buffers are refreshed in place. - fn refresh_conceal_history( - out_buf: &mut [i16], - lag_prev: &mut i32, - pitch_lag: i32, - frame: &[i16], - fs_khz: u32, - ) { - let ltp_mem_length = fs_khz as usize * tables::LTP_MEM_LENGTH_MS; - let frame_len = frame.len(); - let mv_len = ltp_mem_length.saturating_sub(frame_len); - if mv_len > 0 { - out_buf.copy_within(frame_len..frame_len + mv_len, 0); - } - out_buf[mv_len..mv_len + frame_len].copy_from_slice(frame); - if pitch_lag > 0 { - *lag_prev = pitch_lag; - } - } - - /// Conceal one lost SILK or Hybrid packet using the stored PLC state. - /// - /// Params: target `samples_per_channel`, mutable interleaved `out`, and packet `loss_count`. - /// Returns: concealed sample count per output channel. - pub(crate) fn decode_lost( - &mut self, - samples_per_channel: usize, - out: &mut [i16], - loss_count: u32, - ) -> Result { - let packet_channels = self.prev_packet_channels.clamp(1, 2) as usize; - let channels = self.channels as usize; - let needed = samples_per_channel * channels; - if out.len() < needed { - return Err(Error::OutputTooSmall { - needed, - got: out.len(), - }); - } - - let plc0 = &self.core_state[0].plc; - let internal_frame_samples = plc0.subfr_length.saturating_mul(plc0.nb_subfr); - if internal_frame_samples == 0 || plc0.fs_khz <= 0 { - out[..needed].fill(0); - return Ok(samples_per_channel); - } - - let frame_output_samples = if self.fs_hz == 48_000 { - self.resampler_state[0] - .as_ref() - .map(|resampler| resampler.output_len(internal_frame_samples)) - .unwrap_or(0) - } else { - internal_frame_samples - }; - if frame_output_samples == 0 { - out[..needed].fill(0); - return Ok(samples_per_channel); - } - - out[..needed].fill(0); - let conceal_frames = samples_per_channel.div_ceil(frame_output_samples); - let mut mono_resampler_history = self.stereo_state.s_mid; - let mut written_total = 0usize; - - for _ in 0..conceal_frames { - let mut internal_pcm = [[0i16; tables::MAX_FRAME_LENGTH]; 2]; - for (ch, frame_pcm) in internal_pcm.iter_mut().enumerate().take(packet_channels) { - let ch_state = &mut self.core_state[ch]; - let frame_pcm = &mut frame_pcm[..internal_frame_samples]; - let (plc_state, lpc_state) = (&mut ch_state.plc, &mut ch_state.s_lpc_q14_buf[..]); - plc::plc_conceal(plc_state, lpc_state, frame_pcm, loss_count); - let fs_khz = plc_state.fs_khz as u32; - let pitch_lag = plc_state.pitch_lag; - Self::refresh_conceal_history( - &mut ch_state.out_buf, - &mut ch_state.lag_prev, - pitch_lag, - frame_pcm, - fs_khz, - ); - } - - if packet_channels == 1 { - self.stereo_state.s_mid.copy_from_slice( - &internal_pcm[0][internal_frame_samples - 2..internal_frame_samples], - ); - } - - let frame_write = - frame_output_samples.min(samples_per_channel.saturating_sub(written_total)); - if self.fs_hz == 48_000 { - let mut resampled_pcm = [[0i16; MAX_API_FRAME_SAMPLES_48K]; 2]; - for ch in 0..packet_channels { - if let Some(resampler) = self.resampler_state[ch].as_mut() { - let frame_pcm = &internal_pcm[ch][..internal_frame_samples]; - let expected = resampler.output_len(internal_frame_samples); - let written = if packet_channels == 2 { - resampler.process(frame_pcm, &mut resampled_pcm[ch][..expected]) - } else { - let mut resamp_input = vec![0i16; internal_frame_samples + 2]; - resamp_input[..2].copy_from_slice(&mono_resampler_history); - resamp_input[2..2 + internal_frame_samples].copy_from_slice(frame_pcm); - let written = resampler.process( - &resamp_input[1..1 + internal_frame_samples], - &mut resampled_pcm[ch][..expected], - ); - mono_resampler_history.copy_from_slice( - &frame_pcm[internal_frame_samples - 2..internal_frame_samples], - ); - written - }; - debug_assert_eq!(written, expected); - } - } - - for n in 0..frame_write { - let left = resampled_pcm[0][n]; - match channels { - 1 => out[written_total + n] = left, - 2 => { - let base = 2 * (written_total + n); - out[base] = left; - out[base + 1] = if packet_channels == 2 { - resampled_pcm[1][n] - } else { - left - }; - } - _ => unreachable!(), - } - } - } else { - let copy_len = internal_frame_samples.min(frame_write); - for n in 0..copy_len { - let left = internal_pcm[0][n]; - match channels { - 1 => out[written_total + n] = left, - 2 => { - let base = 2 * (written_total + n); - out[base] = left; - out[base + 1] = if packet_channels == 2 { - internal_pcm[1][n] - } else { - left - }; - } - _ => unreachable!(), - } - } - } - - written_total += frame_write; - if written_total >= samples_per_channel { - break; - } - } - - Ok(samples_per_channel) - } - - /// Decode one SILK Opus frame payload. - /// - /// Params: `frame` payload bytes, `frame_samples_48k` frame duration in 48 kHz samples, - /// `config` TOC config, `packet_channels` coded channels, `_packet_idx` debug packet index, - /// and `out` interleaved output slice. - /// Returns: decoded sample count per output channel at the API output rate. - pub fn decode_frame( - &mut self, - frame: &[u8], - frame_samples_48k: usize, - config: u8, - packet_channels: u8, - packet_idx: usize, - out: &mut [i16], - ) -> Result { - let mut dec = EcDec::new(frame); - self.decode_frame_with_ec( - frame, - &mut dec, - frame_samples_48k, - config, - packet_channels, - false, - packet_idx, - out, - ) - } - - /// Decode one SILK Opus frame payload using a shared entropy decoder. - /// - /// Params: original `frame`, shared mutable `dec`, `frame_samples_48k`, TOC `config`, - /// coded `packet_channels`, `is_hybrid` mode flag, debug `packet_idx`, and interleaved `out`. - /// Returns: decoded sample count per output channel at the API output rate. - pub(crate) fn decode_frame_with_ec( - &mut self, - frame: &[u8], - mut dec: &mut EcDec<'_>, - frame_samples_48k: usize, - config: u8, - packet_channels: u8, - is_hybrid: bool, - packet_idx: usize, - out: &mut [i16], - ) -> Result { - if !matches!(packet_channels, 1 | 2) { - return Err(Error::BadPacket); - } - - let internal_fs_hz = silk_internal_fs_hz(config)?; - let (internal_frames, nb_subfr, frame_length) = - internal_frame_shape(frame_samples_48k, internal_fs_hz)?; - let mut mono_resampler_history = self.stereo_state.s_mid; - if packet_idx == 0 && (12..=15).contains(&config) { - append_silk_debug_log( - "silk-hybrid-entry", - "H65", - &format!( - "{{\"packet_idx\":{},\"config\":{},\"internal_fs_khz\":{},\"nb_subfr\":{},\"internal_frames\":{},\"frame_length\":{},\"tell\":{},\"tell_frac\":{}}}", - packet_idx, - config, - internal_fs_hz / 1000, - nb_subfr, - internal_frames, - frame_length, - dec.tell(), - dec.tell_frac() - ), - ); - // #endregion - } - let samples_per_channel = (frame_samples_48k * self.fs_hz as usize) / 48_000; - let needed = samples_per_channel * self.channels as usize; - if out.len() < needed { - return Err(Error::OutputTooSmall { - needed, - got: out.len(), - }); - } - - let mut internal_pcm = [[0i16; MAX_INTERNAL_FRAMES * tables::MAX_FRAME_LENGTH]; 2]; - let mut resampled_pcm = [[0i16; MAX_API_FRAME_SAMPLES_48K]; 2]; - let trace_pkt0 = packet_idx == 0 && silk_trace_enabled(); - if trace_pkt0 { - trace_checkpoint(&dec, packet_idx, "init"); - } - for ch in 0..packet_channels as usize { - let prev_fs_khz = self.lbrr_state[ch].fs_khz; - let new_fs_khz = internal_fs_hz / 1000; - if prev_fs_khz != 0 && prev_fs_khz != new_fs_khz { - self.core_state[ch] = decode_core::SilkChannelState::default(); - self.first_frame_after_reset[ch] = true; - if (1116..=1120).contains(&packet_idx) { - append_silk_debug_log( - "tv12-silk-fs-reset", - "H112", - &format!( - "{{\"packet_idx\":{},\"channel\":{},\"prev_fs_khz\":{},\"new_fs_khz\":{}}}", - packet_idx, ch, prev_fs_khz, new_fs_khz - ), - ); - // #endregion - } - } - lbrr::configure_channel_state(&mut self.lbrr_state[ch], new_fs_khz, nb_subfr); - } - if self.fs_hz == 48_000 { - for ch in 0..packet_channels as usize { - let needs_reset = self.resampler_state[ch] - .as_ref() - .is_none_or(|state| !state.matches(internal_fs_hz, self.fs_hz)); - if needs_reset { - self.resampler_state[ch] = - Some(resampler::SilkResampler::new(internal_fs_hz, self.fs_hz)?); - } - } - if self.channels == 2 && packet_channels == 2 && self.prev_packet_channels == 1 { - self.stereo_state.pred_prev_q13 = [0; 2]; - self.stereo_state.s_side = [0; 2]; - if let Some(left_resampler) = self.resampler_state[0].clone() { - self.resampler_state[1] = Some(left_resampler); - } - } - } - let parsed = match parse_header( - &mut dec, - packet_channels as usize, - internal_frames, - packet_idx, - trace_pkt0, - ) { - Ok(parsed) => parsed, - Err(err) => { - if packet_idx == 0 && (12..=15).contains(&config) { - // #region agent log H66 - append_silk_debug_log( - "silk-hybrid-parse-header-error", - "H66", - &format!( - "{{\"packet_idx\":{},\"error\":\"{}\",\"tell\":{},\"tell_frac\":{}}}", - packet_idx, - err, - dec.tell(), - dec.tell_frac() - ), - ); - // #endregion - } - return Err(err); - } - }; - if packet_idx == 1118 { - let ch = &self.core_state[0]; - let tail_start = ch.out_buf.len().saturating_sub(8); - append_silk_debug_log( - "tv12-pkt1118-silk-entry", - "H110", - &format!( - "{{\"packet_idx\":1118,\"out_buf_tail\":{:?},\"s_lpc_head4\":{:?},\"lag_prev\":{},\"prev_gain_index\":{}}}", - &ch.out_buf[tail_start..], - &ch.s_lpc_q14_buf[..4], - ch.lag_prev, - ch.last_gain_index - ), - ); - // #endregion - } - if let Err(err) = consume_lbrr_payload( - &mut dec, - &parsed, - packet_channels as usize, - internal_frames, - &mut self.lbrr_state, - frame_length, - packet_idx, - trace_pkt0, - ) { - if packet_idx == 0 && (12..=15).contains(&config) { - // #region agent log H67 - append_silk_debug_log( - "silk-hybrid-lbrr-error", - "H67", - &format!( - "{{\"packet_idx\":{},\"error\":\"{}\",\"tell\":{},\"tell_frac\":{}}}", - packet_idx, - err, - dec.tell(), - dec.tell_frac() - ), - ); - // #endregion - } - return Err(err); - } - if trace_pkt0 { - trace_checkpoint(&dec, packet_idx, "after_lbrr_consume"); - } - if let Err(err) = consume_main_payload( - &mut dec, - &parsed, - packet_channels as usize, - internal_frames, - &mut self.lbrr_state, - frame_length, - &mut self.prev_nlsf_q15, - &mut self.pred_coef_q12, - &mut self.first_frame_after_reset, - &mut self.core_state, - &mut internal_pcm, - &mut self.stereo_state, - &mut self.prev_decode_only_middle, - packet_idx, - trace_pkt0, - ) { - if packet_idx == 0 && (12..=15).contains(&config) { - // #region agent log H68 - append_silk_debug_log( - "silk-hybrid-main-payload-error", - "H68", - &format!( - "{{\"packet_idx\":{},\"error\":\"{}\",\"tell\":{},\"tell_frac\":{}}}", - packet_idx, - err, - dec.tell(), - dec.tell_frac() - ), - ); - // #endregion - } - return Err(err); - } - let mut consumed_redundancy = false; - let mut redundancy_bytes = 0usize; - let mut celt_to_silk = false; - if !is_hybrid && dec.tell() + 17 <= (8 * frame.len() as i32) { - celt_to_silk = dec.dec_bit_logp(1); - redundancy_bytes = frame - .len() - .saturating_sub(((dec.tell() + 7).max(0) as usize) >> 3); - consumed_redundancy = redundancy_bytes > 0; - } - if trace_pkt0 { - trace_checkpoint(&dec, packet_idx, "after_main_decode"); - } - if packet_idx == 1117 { - let ch = &self.core_state[0]; - let tail_start = ch.out_buf.len().saturating_sub(8); - append_silk_debug_log( - "tv12-post-pkt1117-silk-state", - "H111", - &format!( - "{{\"packet_idx\":1117,\"out_buf_tail\":{:?},\"s_lpc_head4\":{:?},\"lag_prev\":{},\"prev_gain_index\":{}}}", - &ch.out_buf[tail_start..], - &ch.s_lpc_q14_buf[..4], - ch.lag_prev, - ch.last_gain_index - ), - ); - // #endregion - } - out[..needed].fill(0); - let internal_samples_per_channel = internal_frames * frame_length; - if self.fs_hz == 48_000 { - for ch in 0..packet_channels as usize { - if let Some(resampler) = self.resampler_state[ch].as_mut() { - let mut written_total = 0usize; - for iframe_idx in 0..internal_frames { - let pcm_range = iframe_idx * frame_length..(iframe_idx + 1) * frame_length; - let frame_pcm = &internal_pcm[ch][pcm_range]; - let expected = resampler.output_len(frame_length); - let written = if packet_channels == 2 { - resampler.process( - frame_pcm, - &mut resampled_pcm[ch][written_total..written_total + expected], - ) - } else { - let mut resamp_input = vec![0i16; frame_length + 2]; - resamp_input[..2].copy_from_slice(&mono_resampler_history); - resamp_input[2..2 + frame_length].copy_from_slice(frame_pcm); - let written = resampler.process( - &resamp_input[1..1 + frame_length], - &mut resampled_pcm[ch][written_total..written_total + expected], - ); - mono_resampler_history - .copy_from_slice(&frame_pcm[frame_length - 2..frame_length]); - written - }; - debug_assert_eq!(written, expected); - written_total += written; - } - debug_assert_eq!(written_total, samples_per_channel); - } - } - } - if self.fs_hz == 48_000 { - for n in 0..samples_per_channel { - let left = resampled_pcm[0][n]; - match self.channels { - 1 => out[n] = left, - 2 => { - out[2 * n] = left; - out[2 * n + 1] = if packet_channels == 2 { - resampled_pcm[1][n] - } else { - left - }; - } - _ => unreachable!(), - } - } - } else { - for n in 0..internal_samples_per_channel.min(samples_per_channel) { - let left = internal_pcm[0][n]; - match self.channels { - 1 => out[n] = left, - 2 => { - out[2 * n] = left; - out[2 * n + 1] = if packet_channels == 2 { - internal_pcm[1][n] - } else { - left - }; - } - _ => unreachable!(), - } - } - } - - self.prev_packet_channels = packet_channels; - self.final_range = dec.final_range(); - if trace_pkt0 { - trace_checkpoint(&dec, packet_idx, "after_decode"); - } - if dec.is_error() { - return Err(Error::BadPacket); - } - - if lbrr::any_lbrr( - &[parsed.channels[0].lbrr_flags, parsed.channels[1].lbrr_flags], - packet_channels as usize, - ) { - debug_trace!("silk: consumed LBRR side payload"); - } - - Ok(SilkFrameDecode { - samples_per_channel, - consumed_redundancy, - redundancy_bytes, - celt_to_silk, - }) - } -} - -/// Parse SILK packet header signaling used before core frame decode. -/// -/// Params: `dec` shared entropy decoder, `packet_channels` coded channels, and -/// `internal_frames` count of 20 ms SILK-internal frames (1/2/3). -/// Returns: parsed VAD/LBRR signaling snapshot for the frame. -fn parse_header( - dec: &mut EcDec<'_>, - packet_channels: usize, - internal_frames: usize, - packet_idx: usize, - trace_pkt0: bool, -) -> Result { - let mut parsed = ParsedHeader::default(); - - for ch in 0..packet_channels { - if packet_idx == 0 { - append_silk_debug_log( - "silk-header-before-vad", - "H69", - &format!( - "{{\"packet_idx\":{},\"channel\":{},\"tell\":{},\"tell_frac\":{},\"internal_frames\":{}}}", - packet_idx, - ch, - dec.tell(), - dec.tell_frac(), - internal_frames - ), - ); - // #endregion - } - for i in 0..internal_frames { - parsed.channels[ch].vad_flags[i] = dec.dec_bit_logp(1); - } - if packet_idx == 0 { - append_silk_debug_log( - "silk-header-after-vad", - "H70", - &format!( - "{{\"packet_idx\":{},\"channel\":{},\"tell\":{},\"tell_frac\":{},\"vad_flags\":\"{:?}\"}}", - packet_idx, - ch, - dec.tell(), - dec.tell_frac(), - &parsed.channels[ch].vad_flags[..internal_frames] - ), - ); - // #endregion - } - if trace_pkt0 { - trace_checkpoint(dec, packet_idx, &format!("after_vad_flags_ch{}", ch)); - } - - let has_lbrr = dec.dec_bit_logp(1); - parsed.channels[ch].lbrr_flags = unpack_lbrr_flags(dec, has_lbrr, internal_frames)?; - if packet_idx == 0 { - append_silk_debug_log( - "silk-header-after-lbrr", - "H71", - &format!( - "{{\"packet_idx\":{},\"channel\":{},\"tell\":{},\"tell_frac\":{},\"has_lbrr\":{},\"lbrr_flags\":\"{:?}\"}}", - packet_idx, - ch, - dec.tell(), - dec.tell_frac(), - has_lbrr, - &parsed.channels[ch].lbrr_flags[..internal_frames] - ), - ); - // #endregion - } - if trace_pkt0 { - trace_checkpoint(dec, packet_idx, &format!("after_lbrr_flag_ch{}", ch)); - } - } - - Ok(parsed) -} - -/// Consume LBRR payload side information for all flagged internal frames. -/// -/// Params: entropy decoder `dec`, parsed `header`, coded channel count, -/// `internal_frames`, mutable per-channel `states`, and internal `frame_length`. -/// Returns: nothing on success, `BadPacket` on malformed entropy payload. -fn consume_lbrr_payload( - dec: &mut EcDec<'_>, - header: &ParsedHeader, - packet_channels: usize, - internal_frames: usize, - states: &mut [lbrr::ChannelState; 2], - frame_length: usize, - packet_idx: usize, - trace_pkt0: bool, -) -> Result<(), Error> { - for i in 0..internal_frames { - for (ch, state) in states.iter_mut().enumerate().take(packet_channels) { - if !header.channels[ch].lbrr_flags[i] { - continue; - } - if packet_channels == 2 && ch == 0 { - let _ = stereo::decode_stereo_pred(dec); - if !header.channels[1].lbrr_flags[i] { - let _ = stereo::decode_mid_only(dec); - } - } - let cond = if i > 0 && header.channels[ch].lbrr_flags[i - 1] { - lbrr::CondCoding::Conditionally - } else { - lbrr::CondCoding::Independently - }; - let side = - lbrr::decode_indices(state, dec, header.channels[ch].vad_flags[i], true, cond)?; - let _ = - lbrr::decode_pulses(dec, side.signal_type, side.quant_offset_type, frame_length)?; - if trace_pkt0 { - trace_checkpoint(dec, packet_idx, &format!("after_lbrr_i{}_ch{}", i, ch)); - } - } - } - Ok(()) -} - -/// Consume main SILK frame side information and pulses for all internal frames. -/// -/// Params: entropy decoder `dec`, parsed packet `header`, coded channel count, -/// `internal_frames`, mutable per-channel `states`, internal `frame_length`, -/// packet-level `prev_decode_only_middle`, `packet_idx`, and tracing flag. -/// Returns: nothing on success, `BadPacket` on malformed entropy payload. -fn consume_main_payload( - dec: &mut EcDec<'_>, - header: &ParsedHeader, - packet_channels: usize, - internal_frames: usize, - states: &mut [lbrr::ChannelState; 2], - frame_length: usize, - prev_nlsf_q15: &mut [[i16; tables::MAX_LPC_ORDER]; 2], - pred_coef_q12: &mut [[[[i16; tables::MAX_LPC_ORDER]; MAX_LPC_HALVES]; MAX_INTERNAL_FRAMES]; 2], - first_frame_after_reset: &mut [bool; 2], - core_state: &mut [decode_core::SilkChannelState; 2], - internal_pcm: &mut [[i16; MAX_INTERNAL_FRAMES * tables::MAX_FRAME_LENGTH]; 2], - stereo_state: &mut stereo::StereoState, - prev_decode_only_middle: &mut bool, - packet_idx: usize, - trace_pkt0: bool, -) -> Result<(), Error> { - let mut signal_types = [[-1i32; MAX_INTERNAL_FRAMES]; 2]; - let mut mid_only_flags = [false; MAX_INTERNAL_FRAMES]; - for i in 0..internal_frames { - let mut decode_only_middle = false; - let mut pred_q13 = [0i32; 2]; - let mut decoded_frames: [Option; 2] = [None, None]; - let mut plc_lpc_q12 = [[0i32; tables::MAX_LPC_ORDER]; 2]; - let mut plc_nb_coefs = [0usize; 2]; - if packet_channels == 2 { - pred_q13 = stereo::decode_stereo_pred(dec); - decode_only_middle = !header.channels[1].vad_flags[i] && stereo::decode_mid_only(dec); - mid_only_flags[i] = decode_only_middle; - if trace_pkt0 { - trace_checkpoint(dec, packet_idx, &format!("after_stereo_i{}", i)); - } - debug_trace!( - "silk: stereo signaling i={} pred0={} pred1={} mid_only={}", - i, - pred_q13[0], - pred_q13[1], - decode_only_middle - ); - } - - for ch in 0..packet_channels { - if packet_idx == 0 && i == 0 && ch == 0 { - append_silk_debug_log( - "silk-main-after-header", - "H72", - &format!( - "{{\"packet_idx\":{},\"iframe\":{},\"channel\":{},\"tell\":{},\"tell_frac\":{},\"rng\":{}}}", - packet_idx, - i, - ch, - dec.tell(), - dec.tell_frac(), - dec.rng() - ), - ); - // #endregion - } - if ch == 1 && decode_only_middle { - continue; - } - let cond = if i == 0 { - if ch > 0 && *prev_decode_only_middle { - lbrr::CondCoding::IndependentlyNoLtpScaling - } else { - lbrr::CondCoding::Independently - } - } else if ch > 0 && *prev_decode_only_middle { - lbrr::CondCoding::IndependentlyNoLtpScaling - } else { - lbrr::CondCoding::Conditionally - }; - let side = lbrr::decode_indices( - &mut states[ch], - dec, - header.channels[ch].vad_flags[i], - false, - cond, - )?; - if packet_idx == 0 && i == 0 && ch == 0 { - append_silk_debug_log( - "silk-main-after-indices", - "H73", - &format!( - "{{\"packet_idx\":{},\"iframe\":{},\"channel\":{},\"tell\":{},\"tell_frac\":{},\"rng\":{},\"signal_type\":{},\"quant_offset_type\":{}}}", - packet_idx, - i, - ch, - dec.tell(), - dec.tell_frac(), - dec.rng(), - side.signal_type, - side.quant_offset_type - ), - ); - // #endregion - } - update_lpc_from_nlsf( - states[ch].fs_khz, - &side.nlsf, - &mut prev_nlsf_q15[ch], - &mut pred_coef_q12[ch][i], - &mut first_frame_after_reset[ch], - packet_idx, - i, - ch, - ); - if trace_pkt0 && packet_idx == 0 && i == 0 && ch == 0 { - trace_lpc( - packet_idx, - ch, - i, - "pred0", - &pred_coef_q12[ch][i][0], - states[ch].fs_khz, - ); - trace_lpc( - packet_idx, - ch, - i, - "pred1", - &pred_coef_q12[ch][i][1], - states[ch].fs_khz, - ); - } - let pulses = - lbrr::decode_pulses(dec, side.signal_type, side.quant_offset_type, frame_length)?; - if packet_idx == 0 && i == 0 && ch == 0 { - append_silk_debug_log( - "silk-main-after-pulses", - "H74", - &format!( - "{{\"packet_idx\":{},\"iframe\":{},\"channel\":{},\"tell\":{},\"tell_frac\":{},\"rng\":{}}}", - packet_idx, - i, - ch, - dec.tell(), - dec.tell_frac(), - dec.rng() - ), - ); - // #endregion - } - let frame_params = decode_core::SilkFrameParams { - signal_type: side.signal_type, - quant_offset_type: side.quant_offset_type, - gain_indices: side.gain_indices, - lag_index: side.lag_index, - contour_index: side.contour_index, - per_index: side.per_index, - ltp_indices: side.ltp_indices, - ltp_scale_index: side.ltp_scale_index, - seed: side.seed, - pulses, - conditional: matches!(cond, lbrr::CondCoding::Conditionally), - frame_length, - nb_subfr: states[ch].nb_subfr, - fs_khz: states[ch].fs_khz, - lpc_order: tables::nlsf_codebook(states[ch].fs_khz).order, - }; - signal_types[ch][i] = frame_params.signal_type as i32; - let pcm_range = i * frame_length..(i + 1) * frame_length; - let trace = decode_core::decode_core( - &frame_params, - &pred_coef_q12[ch][i], - &mut internal_pcm[ch][pcm_range], - &mut core_state[ch], - packet_idx, - i, - ); - let mut ltp_gains = [0i32; tables::MAX_NB_SUBFR]; - let lag = if frame_params.signal_type == TYPE_VOICED { - let pitch_lags = pitch::decode_pitch_lags( - frame_params.lag_index as i32, - frame_params.contour_index as i32, - frame_params.fs_khz as i32, - frame_params.nb_subfr, - ); - let (ltp_coeffs_q14, _) = ltp::decode_ltp_coeffs( - frame_params.per_index, - &frame_params.ltp_indices, - frame_params.nb_subfr, - frame_params.ltp_scale_index, - ); - for (subframe_idx, gain) in - ltp_gains.iter_mut().enumerate().take(frame_params.nb_subfr) - { - let summed_q14 = ltp_coeffs_q14[subframe_idx] - .iter() - .map(|coef| i32::from(*coef)) - .sum::() - .max(0); - *gain = summed_q14.clamp(0, 1 << 16); - } - pitch_lags[frame_params.nb_subfr - 1] - } else { - 0 - }; - for (dst, src) in plc_lpc_q12[ch] - .iter_mut() - .zip(pred_coef_q12[ch][i][1].iter()) - .take(frame_params.lpc_order) - { - *dst = i32::from(*src); - } - plc_nb_coefs[ch] = frame_params.lpc_order; - decoded_frames[ch] = Some(plc::SilkDecodedFrame { - lag, - ltp_gains, - output: Vec::new(), - }); - if trace_pkt0 && packet_idx == 0 && i == 0 && ch == 0 { - trace_core(packet_idx, "exc_Q14", &trace.exc_q14_head); - trace_core(packet_idx, "sLPC_Q14", &trace.s_lpc_q14_head); - } - if trace_pkt0 { - trace_checkpoint(dec, packet_idx, &format!("after_main_i{}_ch{}", i, ch)); - } - } - - if packet_channels == 2 { - let pcm_range = i * frame_length..(i + 1) * frame_length; - let (left_channels, right_channels) = internal_pcm.split_at_mut(1); - stereo::ms_to_lr( - stereo_state, - &mut left_channels[0][pcm_range.clone()], - &mut right_channels[0][pcm_range.clone()], - pred_q13, - states[0].fs_khz, - packet_idx, - ); - } else { - let pcm_range = i * frame_length..(i + 1) * frame_length; - let frame = &internal_pcm[0][pcm_range.clone()]; - stereo_state - .s_mid - .copy_from_slice(&frame[frame_length - 2..frame_length]); - } - - let pcm_range = i * frame_length..(i + 1) * frame_length; - for ch in 0..packet_channels { - let Some(mut decoded_frame) = decoded_frames[ch].take() else { - continue; - }; - decoded_frame - .output - .extend_from_slice(&internal_pcm[ch][pcm_range.clone()]); - plc::plc_update( - &mut core_state[ch].plc, - &decoded_frame, - &plc_lpc_q12[ch][..plc_nb_coefs[ch]], - states[ch].nb_subfr, - frame_length / states[ch].nb_subfr, - states[ch].fs_khz as i32, - plc_nb_coefs[ch], - ); - } - - *prev_decode_only_middle = decode_only_middle; - } - if packet_idx == 1118 { - let internal_samples_per_channel = internal_frames * frame_length; - let head_end = 8.min(internal_samples_per_channel); - let tail_start = internal_samples_per_channel.saturating_sub(8); - append_silk_debug_log( - "tv12-pkt1118-internal-pcm", - "H109A", - &format!( - "{{\"packet_idx\":1118,\"internal_fs_khz\":{},\"signal_type\":{},\"packet_channels\":{},\"internal_frames\":{},\"frame_length\":{},\"head\":{:?},\"tail\":{:?}}}", - states[0].fs_khz, - signal_types[0][0], - packet_channels, - internal_frames, - frame_length, - &internal_pcm[0][..head_end], - &internal_pcm[0][tail_start..internal_samples_per_channel] - ), - ); - } - Ok(()) -} - -/// Check whether SILK entropy tracing is enabled. -/// -/// Params: none. -/// Returns: `false`; runtime tracing is disabled in cleanup builds. -fn silk_trace_enabled() -> bool { - false -} - -/// Append one NDJSON debug line for SILK hybrid tracing. -/// -/// Params: event `message`, `hypothesis_id`, and raw JSON `data`. -/// Returns: nothing; runtime debug logging is disabled in cleanup builds. -fn append_silk_debug_log(message: &str, hypothesis_id: &str, data: &str) { - let _ = (message, hypothesis_id, data); -} - -/// Emit packet-0 SILK entropy checkpoint with tell/tell_frac/rng. -/// -/// Params: immutable entropy decoder `dec`, `packet_idx`, and textual `label`. -/// Returns: nothing. -fn trace_checkpoint(dec: &EcDec<'_>, packet_idx: usize, label: &str) { - let _ = (dec, packet_idx, label); -} - -/// Decode, optionally interpolate, and convert one NLSF vector to LPC halves. -/// -/// Params: internal `fs_khz`, decoded `nlsf_indices`, mutable `prev_nlsf_q15`, -/// mutable `pred_coef_q12`, and per-channel `first_frame_after_reset`. -/// Returns: nothing; previous NLSF and both LPC halves are updated in place. -fn update_lpc_from_nlsf( - fs_khz: u32, - nlsf_indices: &lbrr::DecodedNlsfIndices, - prev_nlsf_q15: &mut [i16; tables::MAX_LPC_ORDER], - pred_coef_q12: &mut [[i16; tables::MAX_LPC_ORDER]; MAX_LPC_HALVES], - first_frame_after_reset: &mut bool, - _packet_idx: usize, - _frame_idx: usize, - _channel_idx: usize, -) { - let codebook = tables::nlsf_codebook(fs_khz); - let order = codebook.order; - let mut curr_nlsf_q15 = [0i16; tables::MAX_LPC_ORDER]; - nlsf::nlsf_decode( - &mut curr_nlsf_q15[..order], - &nlsf_indices.values[..order + 1], - codebook, - ); - lpc::nlsf2a( - &mut pred_coef_q12[1][..order], - &curr_nlsf_q15[..order], - order, - ); - - let interp_coef_q2 = if *first_frame_after_reset { - 4 - } else { - nlsf_indices.interp_coef_q2 - }; - if interp_coef_q2 < 4 { - let mut interp_nlsf_q15 = [0i16; tables::MAX_LPC_ORDER]; - for i in 0..order { - let delta = curr_nlsf_q15[i] as i32 - prev_nlsf_q15[i] as i32; - interp_nlsf_q15[i] = - (prev_nlsf_q15[i] as i32 + ((interp_coef_q2 as i32 * delta) >> 2)) as i16; - } - lpc::nlsf2a( - &mut pred_coef_q12[0][..order], - &interp_nlsf_q15[..order], - order, - ); - } else { - let pred1 = pred_coef_q12[1]; - pred_coef_q12[0][..order].copy_from_slice(&pred1[..order]); - } - - prev_nlsf_q15[..order].copy_from_slice(&curr_nlsf_q15[..order]); - *first_frame_after_reset = false; -} - -/// Emit packet-0 LPC trace for one channel/frame half. -/// -/// Params: `packet_idx`, coded `channel`, internal frame `frame_idx`, `label`, -/// LPC slice `lpc_q12`, and internal `fs_khz`. -/// Returns: nothing. -fn trace_lpc( - packet_idx: usize, - channel: usize, - frame_idx: usize, - label: &str, - lpc_q12: &[i16], - fs_khz: u32, -) { - let _ = (packet_idx, channel, frame_idx, label, lpc_q12, fs_khz); -} - -/// Emit packet-0 SILK core trace for excitation or synthesis state. -/// -/// Params: `packet_idx`, textual `label`, and signed sample `values`. -/// Returns: nothing. -fn trace_core(packet_idx: usize, label: &str, values: &[i32; 8]) { - let _ = (packet_idx, label, values); -} - -/// Decode and expand LBRR per-internal-frame flags for one coded channel. -/// -/// Params: `dec` entropy decoder, `has_lbrr` packet-level LBRR present bit, and -/// `internal_frames` number of SILK-internal frames. -/// Returns: fixed-size LBRR flag array with active entries in `[0..internal_frames)`. -fn unpack_lbrr_flags( - dec: &mut EcDec<'_>, - has_lbrr: bool, - internal_frames: usize, -) -> Result<[bool; MAX_INTERNAL_FRAMES], Error> { - if !has_lbrr { - return Ok([false; MAX_INTERNAL_FRAMES]); - } - - if internal_frames == 1 { - return Ok([true, false, false]); - } - - let icdf = tables::lbrr_flags_icdf(internal_frames).ok_or(Error::BadPacket)?; - let symbol = dec.dec_icdf(icdf, 8) as u32 + 1; - Ok(unpack_lbrr_symbol(symbol, internal_frames)) -} - -/// Expand packed LBRR symbol bits to boolean frame flags. -/// -/// Params: `symbol` packed bit mask and `internal_frames` active frame count. -/// Returns: fixed-size LBRR flag array with decoded bits in low indices. -fn unpack_lbrr_symbol(symbol: u32, internal_frames: usize) -> [bool; MAX_INTERNAL_FRAMES] { - let mut out = [false; MAX_INTERNAL_FRAMES]; - for (i, flag) in out.iter_mut().take(internal_frames).enumerate() { - *flag = ((symbol >> i) & 1) != 0; - } - out -} - -/// Map Opus TOC config (SILK-only) to SILK internal decode sample rate. -/// -/// Params: `config` is TOC config value `(toc >> 3) & 0x1f`. -/// Returns: internal SILK sample rate in Hz (8000/12000/16000). -fn silk_internal_fs_hz(config: u8) -> Result { - match config >> 2 { - 0 => Ok(8_000), - 1 => Ok(12_000), - 2 | 3 => Ok(16_000), - _ => Err(Error::BadPacket), - } -} - -/// Derive SILK internal frame shape from Opus frame duration. -/// -/// Params: `frame_samples_48k` Opus frame size at 48 kHz and `internal_fs_hz`. -/// Returns: tuple `(internal_frames, nb_subfr, frame_length_samples)`. -fn internal_frame_shape( - frame_samples_48k: usize, - internal_fs_hz: u32, -) -> Result<(usize, usize, usize), Error> { - let fs_khz = (internal_fs_hz / 1000) as usize; - match frame_samples_48k { - 480 => Ok((1, 2, 10 * fs_khz)), - 960 => Ok((1, 4, 20 * fs_khz)), - 1_920 => Ok((2, 4, 20 * fs_khz)), - 2_880 => Ok((3, 4, 20 * fs_khz)), - _ => Err(Error::BadPacket), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Decoder; - use std::fs; - use std::io::Read; - use std::path::{Path, PathBuf}; - - const TV02_PKT0_LPC_Q12: [i16; 10] = [5041, -1246, 1017, -614, 431, -560, 131, -357, 284, -46]; - - #[test] - fn maps_internal_frame_counts() { - assert_eq!(internal_frame_shape(480, 8_000).unwrap(), (1, 2, 80)); - assert_eq!(internal_frame_shape(960, 8_000).unwrap(), (1, 4, 160)); - assert_eq!(internal_frame_shape(1_920, 12_000).unwrap(), (2, 4, 240)); - assert_eq!(internal_frame_shape(2_880, 16_000).unwrap(), (3, 4, 320)); - assert!(internal_frame_shape(2_400, 12_000).is_err()); - } - - #[test] - fn unpacks_lbrr_bitfield() { - assert_eq!(unpack_lbrr_symbol(0b001, 3), [true, false, false]); - assert_eq!(unpack_lbrr_symbol(0b011, 3), [true, true, false]); - assert_eq!(unpack_lbrr_symbol(0b101, 3), [true, false, true]); - } - - #[ignore = "requires OPUS_TESTVECTORS_DIR"] - #[test] - fn testvector02_pkt0_nlsf_to_lpc_matches_cref() { - let packet = read_first_opus_demo_packet(&vectors_dir().join("testvector02.bit")); - let mut decoder = Decoder::new(48_000, 2).unwrap(); - let mut out = vec![0i16; decoder.max_frame_size_per_channel() * 2]; - let samples = decoder.decode(Some(&packet), &mut out).unwrap(); - assert_eq!(samples, 2880); - assert_eq!(decoder.final_range(), 0x5037_3c71); - assert_eq!( - &decoder.silk.pred_coef_q12[0][0][0][..10], - &TV02_PKT0_LPC_Q12 - ); - assert_eq!( - &decoder.silk.pred_coef_q12[0][0][1][..10], - &TV02_PKT0_LPC_Q12 - ); - } - - #[ignore = "requires OPUS_TESTVECTORS_DIR"] - #[test] - fn testvector02_final_range_matches_reference_for_all_packets() { - let packets = read_opus_demo_stream(&vectors_dir().join("testvector02.bit")); - let mut decoder = Decoder::new(48_000, 2).unwrap(); - let mut out = vec![0i16; decoder.max_frame_size_per_channel() * 2]; - for (packet_idx, packet) in packets.iter().enumerate() { - let _samples = decoder.decode(Some(&packet.payload), &mut out).unwrap(); - assert_eq!( - decoder.final_range(), - packet.expected_final_range, - "packet {packet_idx} final_range" - ); - } - } - - /// Resolve the local RFC vector directory used by decoder tests. - /// - /// Params: none. - /// Returns: absolute path to `testdata/opus_testvectors`. - fn vectors_dir() -> PathBuf { - if let Ok(path) = std::env::var("OPUS_TESTVECTORS_DIR") { - return PathBuf::from(path); - } - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../testdata/opus_testvectors") - .canonicalize() - .unwrap() - } - - /// Read the first packet payload from an `opus_demo` `.bit` stream. - /// - /// Params: absolute `path` to the vector stream file. - /// Returns: first packet payload bytes. - fn read_first_opus_demo_packet(path: &Path) -> Vec { - let mut file = fs::File::open(path).unwrap(); - let mut bytes = Vec::new(); - file.read_to_end(&mut bytes).unwrap(); - let packet_len = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize; - bytes[8..8 + packet_len].to_vec() - } - - /// Read an `opus_demo` `.bit` stream into packet payloads plus expected ranges. - /// - /// Params: absolute `path` to the `.bit` vector file. - /// Returns: ordered packet list with payloads and reference `final_range`. - fn read_opus_demo_stream(path: &Path) -> Vec { - let mut file = fs::File::open(path).unwrap(); - let mut bytes = Vec::new(); - file.read_to_end(&mut bytes).unwrap(); - let mut packets = Vec::new(); - let mut index = 0usize; - while index < bytes.len() { - let packet_len = u32::from_be_bytes([ - bytes[index], - bytes[index + 1], - bytes[index + 2], - bytes[index + 3], - ]) as usize; - index += 4; - let expected_final_range = u32::from_be_bytes([ - bytes[index], - bytes[index + 1], - bytes[index + 2], - bytes[index + 3], - ]); - index += 4; - let payload = bytes[index..index + packet_len].to_vec(); - index += packet_len; - packets.push(DemoPacket { - payload, - expected_final_range, - }); - } - packets - } - - /// One `opus_demo` packet plus its reference range coder state. - #[derive(Debug, Clone)] - struct DemoPacket { - /// Encoded Opus payload bytes. - payload: Vec, - /// Reference decoder final range after decoding the packet. - expected_final_range: u32, - } -} diff --git a/vendor/opus-decoder/src/silk/nlsf.rs b/vendor/opus-decoder/src/silk/nlsf.rs deleted file mode 100644 index fdec9c35..00000000 --- a/vendor/opus-decoder/src/silk/nlsf.rs +++ /dev/null @@ -1,182 +0,0 @@ -//! SILK NLSF reconstruction helpers. - -use super::tables::{self, MAX_LPC_ORDER, NlsfCodebook}; - -const MAX_LOOPS: usize = 20; -const NLSF_QUANT_LEVEL_ADJ_Q10: i32 = 102; - -/// Two-stage VQ decode: stage1 codebook plus predictive residuals. -/// -/// Params: `nlsf_q15` output slice, `nlsf_indices` packed stage1+stage2 indices, -/// and `codebook` selecting NB/MB or WB tables. -/// Returns: nothing; `nlsf_q15[..order]` is filled with stabilized Q15 NLSFs. -pub(super) fn nlsf_decode(nlsf_q15: &mut [i16], nlsf_indices: &[i8], codebook: &NlsfCodebook) { - let order = codebook.order; - debug_assert!(nlsf_q15.len() >= order); - debug_assert!(nlsf_indices.len() > order); - - let cb1_index = nlsf_indices[0] as usize; - let mut pred_q8 = [0u8; MAX_LPC_ORDER]; - let mut res_q10 = [0i16; MAX_LPC_ORDER]; - unpack_predictors(&mut pred_q8, codebook, cb1_index); - residual_dequant( - &mut res_q10, - &nlsf_indices[1..=order], - &pred_q8, - codebook.quant_step_size_q16, - order, - ); - - let cb_base = cb1_index * order; - for i in 0..order { - let weighted_q15 = ((res_q10[i] as i32) << 14) / codebook.cb1_wght_q9[cb_base + i] as i32; - let centroid_q15 = (codebook.cb1_nlsf_q8[cb_base + i] as i32) << 7; - nlsf_q15[i] = (weighted_q15 + centroid_q15).clamp(0, 32_767) as i16; - } - - nlsf_stabilize(nlsf_q15, order); -} - -/// Stabilize NLSFs with the canonical SILK minimum-delta constraints. -/// -/// Params: mutable `nlsf_q15` vector and LPC `order` (10 or 16). -/// Returns: nothing; the slice is modified in place. -pub(super) fn nlsf_stabilize(nlsf_q15: &mut [i16], order: usize) { - let delta_min_q15 = delta_min_for_order(order); - debug_assert!(nlsf_q15.len() >= order); - - for _ in 0..MAX_LOOPS { - let mut min_diff_q15 = nlsf_q15[0] as i32 - delta_min_q15[0] as i32; - let mut split = 0usize; - - for i in 1..order { - let diff_q15 = nlsf_q15[i] as i32 - (nlsf_q15[i - 1] as i32 + delta_min_q15[i] as i32); - if diff_q15 < min_diff_q15 { - min_diff_q15 = diff_q15; - split = i; - } - } - - let tail_diff_q15 = (1 << 15) - (nlsf_q15[order - 1] as i32 + delta_min_q15[order] as i32); - if tail_diff_q15 < min_diff_q15 { - min_diff_q15 = tail_diff_q15; - split = order; - } - - if min_diff_q15 >= 0 { - return; - } - - if split == 0 { - nlsf_q15[0] = delta_min_q15[0]; - } else if split == order { - nlsf_q15[order - 1] = ((1 << 15) - delta_min_q15[order] as i32) as i16; - } else { - let mut min_center_q15 = 0i32; - for &delta in &delta_min_q15[..split] { - min_center_q15 += delta as i32; - } - min_center_q15 += (delta_min_q15[split] as i32) >> 1; - - let mut max_center_q15 = 1 << 15; - for &delta in &delta_min_q15[split + 1..=order] { - max_center_q15 -= delta as i32; - } - max_center_q15 -= (delta_min_q15[split] as i32) >> 1; - - let center_q15 = limit32( - rshift_round(nlsf_q15[split - 1] as i32 + nlsf_q15[split] as i32, 1), - min_center_q15, - max_center_q15, - ); - nlsf_q15[split - 1] = (center_q15 - ((delta_min_q15[split] as i32) >> 1)) as i16; - nlsf_q15[split] = (nlsf_q15[split - 1] as i32 + delta_min_q15[split] as i32) as i16; - } - } - - nlsf_q15[..order].sort_unstable(); - nlsf_q15[0] = nlsf_q15[0].max(delta_min_q15[0]); - for i in 1..order { - let min_allowed = nlsf_q15[i - 1].saturating_add(delta_min_q15[i]); - nlsf_q15[i] = nlsf_q15[i].max(min_allowed); - } - let max_last = ((1 << 15) - delta_min_q15[order] as i32) as i16; - nlsf_q15[order - 1] = nlsf_q15[order - 1].min(max_last); - for i in (0..order - 1).rev() { - let max_allowed = nlsf_q15[i + 1] - delta_min_q15[i + 1]; - nlsf_q15[i] = nlsf_q15[i].min(max_allowed); - } -} - -/// Unpack predictor choices from the packed `ec_sel` map. -/// -/// Params: mutable `pred_q8`, source `codebook`, and stage-1 `cb1_index`. -/// Returns: nothing; predictor coefficients are written into `pred_q8[..order]`. -fn unpack_predictors(pred_q8: &mut [u8; MAX_LPC_ORDER], codebook: &NlsfCodebook, cb1_index: usize) { - let order = codebook.order; - let base = cb1_index * order / 2; - let entries = &codebook.ec_sel[base..base + order / 2]; - for i in (0..order).step_by(2) { - let entry = entries[i / 2]; - pred_q8[i] = codebook.pred_q8[i + ((entry & 1) as usize) * (order - 1)]; - pred_q8[i + 1] = codebook.pred_q8[i + (((entry >> 4) & 1) as usize) * (order - 1) + 1]; - } -} - -/// Reconstruct predictive stage-2 residuals in Q10. -/// -/// Params: mutable `res_q10`, signed stage-2 `indices`, predictor `pred_q8`, -/// residual step `quant_step_size_q16`, and LPC `order`. -/// Returns: nothing; `res_q10[..order]` is filled in reverse predictive order. -fn residual_dequant( - res_q10: &mut [i16; MAX_LPC_ORDER], - indices: &[i8], - pred_q8: &[u8; MAX_LPC_ORDER], - quant_step_size_q16: i32, - order: usize, -) { - let mut out_q10 = 0i32; - for i in (0..order).rev() { - let pred_q10 = (out_q10 * pred_q8[i] as i32) >> 8; - out_q10 = (indices[i] as i32) << 10; - if out_q10 > 0 { - out_q10 -= NLSF_QUANT_LEVEL_ADJ_Q10; - } else if out_q10 < 0 { - out_q10 += NLSF_QUANT_LEVEL_ADJ_Q10; - } - out_q10 = pred_q10 + (((out_q10 as i64) * quant_step_size_q16 as i64) >> 16) as i32; - res_q10[i] = out_q10 as i16; - } -} - -/// Return the canonical `deltaMin_Q15` table for a given LPC order. -/// -/// Params: `order` LPC order. -/// Returns: matching delta-min table slice. -fn delta_min_for_order(order: usize) -> &'static [i16] { - match order { - 10 => tables::NLSF_CB_NB_MB.delta_min_q15, - 16 => tables::NLSF_CB_WB.delta_min_q15, - _ => panic!("unsupported LPC order {order}"), - } -} - -/// Round an arithmetic right shift like `silk_RSHIFT_ROUND`. -/// -/// Params: signed `value` and positive `shift`. -/// Returns: rounded arithmetic right-shift result. -fn rshift_round(value: i32, shift: usize) -> i32 { - if shift == 1 { - (value >> 1) + (value & 1) - } else { - ((value >> (shift - 1)) + 1) >> 1 - } -} - -/// Clamp a value between two inclusive bounds. -/// -/// Params: signed `value`, `min_value`, and `max_value`. -/// Returns: bounded value. -fn limit32(value: i32, min_value: i32, max_value: i32) -> i32 { - value.clamp(min_value, max_value) -} diff --git a/vendor/opus-decoder/src/silk/pitch.rs b/vendor/opus-decoder/src/silk/pitch.rs deleted file mode 100644 index 8180ca4e..00000000 --- a/vendor/opus-decoder/src/silk/pitch.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! SILK pitch lag decoding helpers. - -use super::tables::{ - CB_LAGS_STAGE2_10MS_NB, CB_LAGS_STAGE2_NB, CB_LAGS_STAGE3_10MS_WB, CB_LAGS_STAGE3_WB, - MAX_NB_SUBFR, PITCH_EST_MAX_LAG_MS, PITCH_EST_MIN_LAG_MS, -}; - -/// Decode absolute pitch lags for each active subframe. -/// -/// Params: absolute `lag_index`, `contour_index`, internal `fs_khz`, and active `nb_subfr`. -/// Returns: decoded pitch lag per subframe in samples. -pub(super) fn decode_pitch_lags( - lag_index: i32, - contour_index: i32, - fs_khz: i32, - nb_subfr: usize, -) -> [i32; MAX_NB_SUBFR] { - let min_lag = PITCH_EST_MIN_LAG_MS * fs_khz; - let max_lag = PITCH_EST_MAX_LAG_MS * fs_khz; - let lag = min_lag + lag_index; - let mut pitch_lags = [0i32; MAX_NB_SUBFR]; - - for (k, pitch_lag) in pitch_lags.iter_mut().enumerate().take(nb_subfr) { - let contour = contour_value(fs_khz, nb_subfr, k, contour_index as usize) as i32; - *pitch_lag = (lag + contour).clamp(min_lag, max_lag); - } - - pitch_lags -} - -/// Return one contour entry from the SILK pitch codebook. -/// -/// Params: internal `fs_khz`, active `nb_subfr`, `subframe` row, and `contour_index` column. -/// Returns: contour delta in samples. -fn contour_value(fs_khz: i32, nb_subfr: usize, subframe: usize, contour_index: usize) -> i8 { - match (fs_khz, nb_subfr) { - (8, 4) => CB_LAGS_STAGE2_NB[subframe][contour_index], - (8, 2) => CB_LAGS_STAGE2_10MS_NB[subframe][contour_index], - (_, 4) => CB_LAGS_STAGE3_WB[subframe][contour_index], - (_, 2) => CB_LAGS_STAGE3_10MS_WB[subframe][contour_index], - _ => panic!("unsupported pitch shape fs_khz={fs_khz} nb_subfr={nb_subfr}"), - } -} diff --git a/vendor/opus-decoder/src/silk/plc.rs b/vendor/opus-decoder/src/silk/plc.rs deleted file mode 100644 index 17823ece..00000000 --- a/vendor/opus-decoder/src/silk/plc.rs +++ /dev/null @@ -1,261 +0,0 @@ -//! SILK packet loss concealment helpers. -//! -//! This module stores the subset of libopus PLC state needed by the current -//! Rust decoder and synthesizes one concealed SILK internal frame at a time. - -use super::tables::{MAX_FRAME_LENGTH, MAX_LPC_ORDER, MAX_NB_SUBFR}; - -/// Metadata captured from one decoded SILK internal frame for PLC refresh. -#[derive(Debug, Clone)] -pub(super) struct SilkDecodedFrame { - /// Pitch lag from the last voiced subframe, or zero for unvoiced frames. - pub lag: i32, - /// Collapsed per-subframe pitch gains in Q16. - pub ltp_gains: [i32; MAX_NB_SUBFR], - /// Synthesized internal-rate PCM for the decoded frame. - pub output: Vec, -} - -/// Mirrors the libopus PLC state needed by the Rust SILK concealment path. -#[derive(Debug, Clone)] -pub(super) struct SilkPlcState { - /// Pitch lag in internal-rate samples from the last good voiced frame. - pub pitch_lag: i32, - /// Pitch gain per subframe in Q16. - pub pitch_gain: [i32; MAX_NB_SUBFR], - /// Saved LPC coefficients in Q12. - pub lpc_coeffs: [i32; MAX_LPC_ORDER], - /// Noise-shaping gain in Q14. - pub shape_gain: i32, - /// PLC pseudo-random seed. - pub rand_seed: i32, - /// Last synthesized internal-rate frame for pitch repetition. - pub last_frame_out: Vec, - /// Number of active subframes in one concealed internal frame. - pub nb_subfr: usize, - /// Samples per subframe at the internal SILK rate. - pub subfr_length: usize, - /// Internal sampling rate in kHz. - pub fs_khz: i32, - /// Active LPC order. - pub nb_coefs: usize, -} - -impl Default for SilkPlcState { - /// Create PLC state with stable defaults for the first concealment frame. - /// - /// Parameters: none. - /// Returns: initialized PLC state. - fn default() -> Self { - Self { - pitch_lag: 0, - pitch_gain: [0; MAX_NB_SUBFR], - lpc_coeffs: [0; MAX_LPC_ORDER], - shape_gain: 1 << 14, - rand_seed: 1, - last_frame_out: Vec::new(), - nb_subfr: 0, - subfr_length: 0, - fs_khz: 0, - nb_coefs: 0, - } - } -} - -/// Save decoded SILK state needed for future PLC synthesis. -/// -/// Parameters: mutable `plc` state, decoded `frame` metadata, saved `lpc_q12`, -/// frame layout (`nb_subfr`, `subfr_length`), `fs_khz`, and active `nb_coefs`. -/// Returns: nothing; `plc` is refreshed from the good frame. -pub(super) fn plc_update( - plc: &mut SilkPlcState, - frame: &SilkDecodedFrame, - lpc_q12: &[i32], - nb_subfr: usize, - subfr_length: usize, - fs_khz: i32, - nb_coefs: usize, -) { - plc.pitch_lag = frame.lag.max(0); - plc.pitch_gain.fill(0); - for (dst, src) in plc - .pitch_gain - .iter_mut() - .zip(frame.ltp_gains.iter()) - .take(nb_subfr.min(MAX_NB_SUBFR)) - { - *dst = (*src).clamp(0, 1 << 16); - } - - plc.lpc_coeffs.fill(0); - for (dst, src) in plc - .lpc_coeffs - .iter_mut() - .zip(lpc_q12.iter()) - .take(nb_coefs.min(MAX_LPC_ORDER)) - { - *dst = *src; - } - - plc.nb_subfr = nb_subfr; - plc.subfr_length = subfr_length; - plc.fs_khz = fs_khz; - plc.nb_coefs = nb_coefs.min(MAX_LPC_ORDER); - plc.last_frame_out.clear(); - plc.last_frame_out.extend_from_slice(&frame.output); -} - -/// Generate one concealed SILK internal frame. -/// -/// Parameters: mutable `plc` state, mutable LPC synthesis `lpc_state`, mutable -/// PCM `out`, and packet-level consecutive `loss_count`. -/// Returns: nothing; `out` contains concealed PCM and state is updated in place. -pub(super) fn plc_conceal( - plc: &mut SilkPlcState, - lpc_state: &mut [i32], - out: &mut [i16], - loss_count: u32, -) { - let frame_len = plc.subfr_length.saturating_mul(plc.nb_subfr); - let nb_coefs = plc.nb_coefs.min(MAX_LPC_ORDER).min(lpc_state.len()); - if frame_len == 0 || out.len() != frame_len || nb_coefs == 0 { - out.fill(0); - return; - } - - let attenuate_q16 = if loss_count == 0 { - 1 << 16 - } else { - ((1 << 16) * (10 - loss_count.min(9)) as i32) / 10 - }; - let base_rand_scale_q16 = (plc.fs_khz.max(1) * 3) << 10; - let noise_gain_q16 = mul_q14_q16(plc.shape_gain, base_rand_scale_q16); - let mut synth_q14 = [0i32; MAX_LPC_ORDER + MAX_FRAME_LENGTH]; - synth_q14[..nb_coefs].copy_from_slice(&lpc_state[..nb_coefs]); - - for subframe_idx in 0..plc.nb_subfr { - let gain_q16 = plc.pitch_gain[subframe_idx.min(MAX_NB_SUBFR - 1)]; - let pitch_offset = subframe_idx * plc.subfr_length; - for sample_idx in 0..plc.subfr_length { - let frame_idx = pitch_offset + sample_idx; - let pitch_sample = plc_pitch_sample(plc, frame_idx); - let mut excitation = mul_q16(pitch_sample, gain_q16); - - plc.rand_seed = plc - .rand_seed - .wrapping_mul(196_314_165) - .wrapping_add(907_633_515); - let noise = (plc.rand_seed >> 16) as i16 as i32; - excitation = excitation.wrapping_add(mul_q16(noise, noise_gain_q16)); - - let hist_idx = nb_coefs + frame_idx; - let mut lpc_pred_q10 = (nb_coefs as i32) >> 1; - for coef_idx in 0..nb_coefs { - lpc_pred_q10 = smlawb( - lpc_pred_q10, - synth_q14[hist_idx - 1 - coef_idx], - plc.lpc_coeffs[coef_idx], - ); - } - - let current_q14 = mul_q16( - add_sat32(lshift_sat32(excitation, 14), lshift_sat32(lpc_pred_q10, 4)), - attenuate_q16, - ); - synth_q14[hist_idx] = current_q14; - out[frame_idx] = sat16(rshift_round(current_q14, 14)); - } - } - - lpc_state[..nb_coefs].copy_from_slice(&synth_q14[frame_len..frame_len + nb_coefs]); - plc.last_frame_out.clear(); - plc.last_frame_out.extend_from_slice(out); - plc.shape_gain = ((plc.shape_gain as i64 * 16_220) >> 14) as i32; -} - -/// Read one pitch source sample from the saved PLC history. -/// -/// Parameters: immutable `plc` state and linear `frame_idx` inside the concealed frame. -/// Returns: previous PCM sample used for pitch repetition, or zero when unavailable. -fn plc_pitch_sample(plc: &SilkPlcState, frame_idx: usize) -> i32 { - if plc.pitch_lag <= 0 || plc.last_frame_out.is_empty() { - return 0; - } - - let lag = plc.pitch_lag as usize; - let history_len = plc.last_frame_out.len(); - let lag_idx = frame_idx.wrapping_sub(lag) % history_len; - plc.last_frame_out[lag_idx] as i32 -} - -/// Multiply a signed sample by a Q16 gain. -/// -/// Parameters: signed `value` and `gain_q16`. -/// Returns: scaled signed sample. -fn mul_q16(value: i32, gain_q16: i32) -> i32 { - ((value as i64 * gain_q16 as i64) >> 16) as i32 -} - -/// Multiply a Q14 value with a Q16 value and keep Q16 scaling. -/// -/// Parameters: Q14 `value_q14` and Q16 `value_q16`. -/// Returns: Q16-scaled product. -fn mul_q14_q16(value_q14: i32, value_q16: i32) -> i32 { - ((value_q14 as i64 * value_q16 as i64) >> 14) as i32 -} - -/// Approximate `a32 + ((b32 * (i16)c32) >> 16)`. -/// -/// Parameters: accumulator `a32`, multiplicand `b32`, and signed `c32`. -/// Returns: accumulated high-word product. -fn smlawb(a32: i32, b32: i32, c32: i32) -> i32 { - a32.wrapping_add(smulwb(b32, c32)) -} - -/// Multiply by the signed low 16-bit half of `b32` and keep high word. -/// -/// Parameters: signed `a32` and signed `b32`. -/// Returns: high-word signed product. -fn smulwb(a32: i32, b32: i32) -> i32 { - let b16 = b32 as i16 as i32; - let high = (a32 >> 16) * b16; - let low = ((a32 & 0xFFFF) * b16) >> 16; - high.wrapping_add(low) -} - -/// Saturating 32-bit addition. -/// -/// Parameters: signed `a` and `b`. -/// Returns: saturated sum. -fn add_sat32(a: i32, b: i32) -> i32 { - a.saturating_add(b) -} - -/// Left-shift with saturation. -/// -/// Parameters: signed `value` and non-negative `shift`. -/// Returns: saturated left-shifted value. -fn lshift_sat32(value: i32, shift: usize) -> i32 { - let widened = (value as i64) << shift; - widened.clamp(i64::from(i32::MIN), i64::from(i32::MAX)) as i32 -} - -/// Round a signed arithmetic right shift. -/// -/// Parameters: signed `value` and positive `shift`. -/// Returns: rounded shifted value. -fn rshift_round(value: i32, shift: usize) -> i32 { - if shift == 1 { - (value >> 1) + (value & 1) - } else { - ((value >> (shift - 1)) + 1) >> 1 - } -} - -/// Saturate a signed value to `i16`. -/// -/// Parameters: signed `value`. -/// Returns: clamped PCM sample. -fn sat16(value: i32) -> i16 { - value.clamp(i16::MIN as i32, i16::MAX as i32) as i16 -} diff --git a/vendor/opus-decoder/src/silk/resampler.rs b/vendor/opus-decoder/src/silk/resampler.rs deleted file mode 100644 index 36ccf509..00000000 --- a/vendor/opus-decoder/src/silk/resampler.rs +++ /dev/null @@ -1,157 +0,0 @@ -//! SILK decoder-side resampler for internal-rate PCM to 48 kHz. - -use crate::Error; - -use super::resampler_private; - -const RESAMPLER_MAX_BATCH_SIZE_MS: usize = 10; -const SILK_RESAMPLER_MAX_IIR_ORDER: usize = 6; -const SILK_RESAMPLER_MAX_DELAY: usize = 96; - -/// Decoder-side SILK resampler state. -#[derive(Debug, Clone)] -pub(super) struct SilkResampler { - /// Input sampling rate in Hz. - fs_in_hz: u32, - /// Output sampling rate in Hz. - fs_out_hz: u32, - /// Input rate in kHz. - fs_in_khz: usize, - /// One-block input batch size. - batch_size: usize, - /// Ratio step used by the FIR interpolator. - inv_ratio_q16: i32, - /// Decoder-mode delay compensation in samples. - input_delay: usize, - /// IIR/allpass state. - s_iir: [i32; SILK_RESAMPLER_MAX_IIR_ORDER], - /// FIR/history state. - s_fir: [i16; resampler_private::fir_history_len()], - /// Delay buffer used by the top-level dispatcher. - delay_buf: [i16; SILK_RESAMPLER_MAX_DELAY], -} - -impl SilkResampler { - /// Create a decoder-mode SILK resampler. - /// - /// Params: `fs_in_hz` internal SILK rate and `fs_out_hz` API output rate. - /// Returns: initialized resampler state for supported decoder paths. - pub(super) fn new(fs_in_hz: u32, fs_out_hz: u32) -> Result { - let input_delay = match (fs_in_hz, fs_out_hz) { - (8_000, 48_000) => 0, - (12_000, 48_000) => 4, - (16_000, 48_000) => 7, - _ => return Err(Error::NotImplemented), - }; - let fs_in_khz = (fs_in_hz / 1000) as usize; - let batch_size = fs_in_khz * RESAMPLER_MAX_BATCH_SIZE_MS; - let mut inv_ratio_q16 = ((((fs_in_hz as i64) << 15) / fs_out_hz as i64) << 2) as i32; - while smulww(inv_ratio_q16, fs_out_hz as i32) < ((fs_in_hz as i32) << 1) { - inv_ratio_q16 += 1; - } - - Ok(Self { - fs_in_hz, - fs_out_hz, - fs_in_khz, - batch_size, - inv_ratio_q16, - input_delay, - s_iir: [0; SILK_RESAMPLER_MAX_IIR_ORDER], - s_fir: [0; resampler_private::fir_history_len()], - delay_buf: [0; SILK_RESAMPLER_MAX_DELAY], - }) - } - - /// Check whether the current state matches a rate pair. - /// - /// Params: `fs_in_hz` and `fs_out_hz`. - /// Returns: true when the resampler can be reused as-is. - pub(super) fn matches(&self, fs_in_hz: u32, fs_out_hz: u32) -> bool { - self.fs_in_hz == fs_in_hz && self.fs_out_hz == fs_out_hz - } - - /// Resample internal-rate PCM to API-rate PCM. - /// - /// Params: immutable `input` PCM at internal rate and mutable `output` PCM at API rate. - /// Returns: number of output samples written. - pub(super) fn process(&mut self, input: &[i16], output: &mut [i16]) -> usize { - debug_assert!(input.len() >= self.fs_in_khz); - - let n_samples = self.fs_in_khz - self.input_delay; - self.delay_buf[self.input_delay..self.fs_in_khz].copy_from_slice(&input[..n_samples]); - - let mut written = 0usize; - let first_block = self.delay_buf[..self.fs_in_khz].to_vec(); - written += self.process_iir_fir(&first_block, &mut output[written..]); - let second_len = input.len().saturating_sub(self.fs_in_khz); - written += self.process_iir_fir( - &input[n_samples..n_samples + second_len], - &mut output[written..], - ); - - if self.input_delay > 0 { - let start = input.len() - self.input_delay; - self.delay_buf[..self.input_delay].copy_from_slice(&input[start..]); - } - written - } - - /// Run the IIR+FIR upsampling core on one contiguous input slice. - /// - /// Params: immutable `input` block and mutable `output` buffer. - /// Returns: number of output samples written. - fn process_iir_fir(&mut self, input: &[i16], output: &mut [i16]) -> usize { - if input.is_empty() { - return 0; - } - - let history = resampler_private::fir_history_len(); - let mut total_written = 0usize; - let mut in_offset = 0usize; - - while in_offset < input.len() { - let n_samples_in = (input.len() - in_offset).min(self.batch_size); - let mut buf = vec![0i16; history + (2 * n_samples_in) + history]; - buf[..history].copy_from_slice(&self.s_fir); - resampler_private::up2_hq( - &mut self.s_iir, - &mut buf[history..history + 2 * n_samples_in], - &input[in_offset..in_offset + n_samples_in], - n_samples_in, - ); - - let max_index_q16 = (n_samples_in as i32) << 17; - let written = resampler_private::iir_fir_interpol( - &mut output[total_written..], - &buf, - max_index_q16, - self.inv_ratio_q16, - ); - total_written += written; - - let src_start = 2 * n_samples_in; - self.s_fir - .copy_from_slice(&buf[src_start..src_start + history]); - in_offset += n_samples_in; - } - - total_written - } - - /// Return the number of output samples for a given input length. - /// - /// Params: input length in samples. - /// Returns: exact output sample count for the configured ratio. - pub(super) fn output_len(&self, input_len: usize) -> usize { - ((input_len as u64) * self.fs_out_hz as u64 / self.fs_in_hz as u64) as usize - } -} - -/// Approximate signed `((a32 * b32) >> 16)`. -/// -/// Params: signed `a32` and `b32`. -/// Returns: signed Q16-scaled product. -fn smulww(a32: i32, b32: i32) -> i32 { - ((a32 as i64 * b32 as i64) >> 16) as i32 -} diff --git a/vendor/opus-decoder/src/silk/resampler_private.rs b/vendor/opus-decoder/src/silk/resampler_private.rs deleted file mode 100644 index 7e38ac5b..00000000 --- a/vendor/opus-decoder/src/silk/resampler_private.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! Private SILK resampler building blocks. - -use super::resampler_rom::{ - RESAMPLER_FRAC_FIR_12, RESAMPLER_ORDER_FIR_12, RESAMPLER_UP2_HQ_0, RESAMPLER_UP2_HQ_1, -}; - -/// Upsample by 2 using the SILK high-quality allpass structure. -/// -/// Params: mutable `state_q10`, mutable `output`, immutable `input`, and active `len`. -/// Returns: nothing; `output[..2*len]` receives 2x upsampled PCM. -pub(super) fn up2_hq(state_q10: &mut [i32; 6], output: &mut [i16], input: &[i16], len: usize) { - for k in 0..len { - let in32 = (input[k] as i32) << 10; - - let mut y = in32 - state_q10[0]; - let mut x = smulwb(y, RESAMPLER_UP2_HQ_0[0] as i32); - let mut out32_1 = state_q10[0] + x; - state_q10[0] = in32 + x; - - y = out32_1 - state_q10[1]; - x = smulwb(y, RESAMPLER_UP2_HQ_0[1] as i32); - let out32_2 = state_q10[1] + x; - state_q10[1] = out32_1 + x; - - y = out32_2 - state_q10[2]; - x = smlawb(y, y, RESAMPLER_UP2_HQ_0[2] as i32); - out32_1 = state_q10[2] + x; - state_q10[2] = out32_2 + x; - output[2 * k] = sat16(rshift_round(out32_1, 10)); - - y = in32 - state_q10[3]; - x = smulwb(y, RESAMPLER_UP2_HQ_1[0] as i32); - out32_1 = state_q10[3] + x; - state_q10[3] = in32 + x; - - y = out32_1 - state_q10[4]; - x = smulwb(y, RESAMPLER_UP2_HQ_1[1] as i32); - let out32_2 = state_q10[4] + x; - state_q10[4] = out32_1 + x; - - y = out32_2 - state_q10[5]; - x = smlawb(y, y, RESAMPLER_UP2_HQ_1[2] as i32); - out32_1 = state_q10[5] + x; - state_q10[5] = out32_2 + x; - output[2 * k + 1] = sat16(rshift_round(out32_1, 10)); - } -} - -/// Interpolate a 2x-upsampled temporary buffer to the target rate. -/// -/// Params: mutable `output`, FIR/history `buffer`, maximum source `max_index_q16`, -/// and output-step `index_increment_q16`. -/// Returns: number of samples written to `output`. -pub(super) fn iir_fir_interpol( - output: &mut [i16], - buffer: &[i16], - max_index_q16: i32, - index_increment_q16: i32, -) -> usize { - let mut out_idx = 0usize; - let mut index_q16 = 0i32; - while index_q16 < max_index_q16 { - let table_index = smulwb(index_q16 & 0xFFFF, 12) as usize; - let buf_index = (index_q16 >> 16) as usize; - let buf_ptr = &buffer[buf_index..]; - - let mut res_q15 = smulbb( - buf_ptr[0] as i32, - RESAMPLER_FRAC_FIR_12[table_index][0] as i32, - ); - res_q15 = smlabb( - res_q15, - buf_ptr[1] as i32, - RESAMPLER_FRAC_FIR_12[table_index][1] as i32, - ); - res_q15 = smlabb( - res_q15, - buf_ptr[2] as i32, - RESAMPLER_FRAC_FIR_12[table_index][2] as i32, - ); - res_q15 = smlabb( - res_q15, - buf_ptr[3] as i32, - RESAMPLER_FRAC_FIR_12[table_index][3] as i32, - ); - res_q15 = smlabb( - res_q15, - buf_ptr[4] as i32, - RESAMPLER_FRAC_FIR_12[11 - table_index][3] as i32, - ); - res_q15 = smlabb( - res_q15, - buf_ptr[5] as i32, - RESAMPLER_FRAC_FIR_12[11 - table_index][2] as i32, - ); - res_q15 = smlabb( - res_q15, - buf_ptr[6] as i32, - RESAMPLER_FRAC_FIR_12[11 - table_index][1] as i32, - ); - res_q15 = smlabb( - res_q15, - buf_ptr[7] as i32, - RESAMPLER_FRAC_FIR_12[11 - table_index][0] as i32, - ); - output[out_idx] = sat16(rshift_round(res_q15, 15)); - out_idx += 1; - index_q16 += index_increment_q16; - } - out_idx -} - -/// Return FIR history length for the upsampling interpolator. -/// -/// Params: none. -/// Returns: FIR history length in samples. -pub(super) const fn fir_history_len() -> usize { - RESAMPLER_ORDER_FIR_12 -} - -/// Multiply a 32-bit value by the signed low half of another value. -/// -/// Params: signed `a32` and signed `b32`. -/// Returns: high-word product. -fn smulwb(a32: i32, b32: i32) -> i32 { - let b16 = b32 as i16 as i32; - let high = (a32 >> 16) * b16; - let low = ((a32 & 0xFFFF) * b16) >> 16; - high.wrapping_add(low) -} - -/// Multiply signed low halves and accumulate. -/// -/// Params: accumulator `a32`, signed `b32`, and signed `c32`. -/// Returns: accumulated product. -fn smlabb(a32: i32, b32: i32, c32: i32) -> i32 { - a32 + (b32 as i16 as i32) * (c32 as i16 as i32) -} - -/// Multiply signed low half and accumulate with high-word scaling. -/// -/// Params: accumulator `a32`, multiplicand `b32`, and signed `c32`. -/// Returns: accumulated high-word product. -fn smlawb(a32: i32, b32: i32, c32: i32) -> i32 { - a32.wrapping_add(smulwb(b32, c32)) -} - -/// Multiply signed low halves. -/// -/// Params: signed `a32` and `b32`. -/// Returns: signed 32-bit product. -fn smulbb(a32: i32, b32: i32) -> i32 { - (a32 as i16 as i32) * (b32 as i16 as i32) -} - -/// Saturate to `i16`. -/// -/// Params: signed `value`. -/// Returns: clamped PCM sample. -fn sat16(value: i32) -> i16 { - value.clamp(i16::MIN as i32, i16::MAX as i32) as i16 -} - -/// Round a signed arithmetic right shift. -/// -/// Params: signed `value` and positive `shift`. -/// Returns: rounded shifted value. -fn rshift_round(value: i32, shift: usize) -> i32 { - if shift == 1 { - (value >> 1) + (value & 1) - } else { - ((value >> (shift - 1)) + 1) >> 1 - } -} diff --git a/vendor/opus-decoder/src/silk/resampler_rom.rs b/vendor/opus-decoder/src/silk/resampler_rom.rs deleted file mode 100644 index e0464add..00000000 --- a/vendor/opus-decoder/src/silk/resampler_rom.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! SILK resampler ROM tables. - -/// FIR interpolation order for upsampling paths. -pub(super) const RESAMPLER_ORDER_FIR_12: usize = 8; - -/// High-quality 2x upsampler allpass coefficients, branch 0. -pub(super) const RESAMPLER_UP2_HQ_0: [i16; 3] = [1746, 14_986, -26_453]; - -/// High-quality 2x upsampler allpass coefficients, branch 1. -pub(super) const RESAMPLER_UP2_HQ_1: [i16; 3] = [6854, 25_769, -9994]; - -/// Fractional FIR interpolation table for the upsampling resampler. -pub(super) const RESAMPLER_FRAC_FIR_12: [[i16; RESAMPLER_ORDER_FIR_12 / 2]; 12] = [ - [189, -600, 617, 30_567], - [117, -159, -1070, 29_704], - [52, 221, -2392, 28_276], - [-4, 529, -3350, 26_341], - [-48, 758, -3956, 23_973], - [-80, 905, -4235, 21_254], - [-99, 972, -4222, 18_278], - [-107, 967, -3957, 15_143], - [-103, 896, -3487, 11_950], - [-91, 773, -2865, 8798], - [-71, 611, -2143, 5784], - [-46, 425, -1375, 2996], -]; diff --git a/vendor/opus-decoder/src/silk/stereo.rs b/vendor/opus-decoder/src/silk/stereo.rs deleted file mode 100644 index fb942ca8..00000000 --- a/vendor/opus-decoder/src/silk/stereo.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! SILK stereo signaling parser helpers for Phase 3a. - -use crate::entropy::EcDec; - -use super::tables; - -const STEREO_STEP_Q16: i32 = 6_554; // SILK_FIX_CONST(0.5 / 5, 16) -const STEREO_INTERP_LEN_MS: usize = 8; - -/// Decoder-side SILK stereo state. -/// -/// Params: none. -/// Returns: persistent predictor and sample history for MS->LR conversion. -#[derive(Debug, Clone, Default)] -pub(super) struct StereoState { - pub pred_prev_q13: [i16; 2], - pub s_mid: [i16; 2], - pub s_side: [i16; 2], -} - -/// Decode SILK stereo predictor signaling (`silk_stereo_decode_pred`). -/// -/// Params: `dec` is the shared Opus range decoder over SILK frame payload. -/// Returns: two MS predictor values in Q13. -pub(super) fn decode_stereo_pred(dec: &mut EcDec<'_>) -> [i32; 2] { - let mut ix = [[0i32; 3]; 2]; - let joint = dec.dec_icdf(&tables::STEREO_PRED_JOINT_ICDF, 8); - ix[0][2] = joint / 5; - ix[1][2] = joint - 5 * ix[0][2]; - - for side in &mut ix { - side[0] = dec.dec_icdf(&tables::UNIFORM3_ICDF, 8); - side[1] = dec.dec_icdf(&tables::UNIFORM5_ICDF, 8); - } - - let mut pred_q13 = [0i32; 2]; - for (n, side) in ix.iter_mut().enumerate() { - side[0] += 3 * side[2]; - let low_q13 = tables::STEREO_PRED_QUANT_Q13[side[0] as usize]; - let high_q13 = tables::STEREO_PRED_QUANT_Q13[side[0] as usize + 1]; - let step_q13 = mul_wb(high_q13 - low_q13, STEREO_STEP_Q16); - pred_q13[n] = low_q13 + step_q13 * (2 * side[1] + 1); - } - - pred_q13[0] -= pred_q13[1]; - pred_q13 -} - -/// Decode SILK "mid-only coded" stereo flag (`silk_stereo_decode_mid_only`). -/// -/// Params: `dec` is the shared Opus range decoder over SILK frame payload. -/// Returns: true when side channel is omitted for this frame. -pub(super) fn decode_mid_only(dec: &mut EcDec<'_>) -> bool { - dec.dec_icdf(&tables::STEREO_ONLY_CODE_MID_ICDF, 8) != 0 -} - -/// Multiply helper matching SILK `silk_SMULWB` behavior. -/// -/// Params: `a` and `b` are signed fixed-point values. -/// Returns: high-word product `(a*b)>>16`. -fn mul_wb(a: i32, b: i32) -> i32 { - ((a as i64 * b as i64) >> 16) as i32 -} - -/// Convert one SILK internal frame from adaptive mid/side to left/right. -/// -/// Params: mutable stereo `state`, mutable `mid` and `side` frame buffers, -/// current `pred_q13`, and internal sampling rate `fs_khz`. -/// Returns: nothing; `mid` becomes left and `side` becomes right in place. -pub(super) fn ms_to_lr( - state: &mut StereoState, - mid: &mut [i16], - side: &mut [i16], - pred_q13: [i32; 2], - fs_khz: u32, - _packet_idx: usize, -) { - let frame_length = mid.len(); - debug_assert_eq!(side.len(), frame_length); - - let mut x1 = vec![0i16; frame_length + 2]; - let mut x2 = vec![0i16; frame_length + 2]; - x1[..2].copy_from_slice(&state.s_mid); - x2[..2].copy_from_slice(&state.s_side); - x1[2..].copy_from_slice(mid); - x2[2..].copy_from_slice(side); - state - .s_mid - .copy_from_slice(&x1[frame_length..frame_length + 2]); - state - .s_side - .copy_from_slice(&x2[frame_length..frame_length + 2]); - - let mut pred0_q13 = state.pred_prev_q13[0] as i32; - let mut pred1_q13 = state.pred_prev_q13[1] as i32; - let denom_q16 = (1 << 16) / (STEREO_INTERP_LEN_MS as i32 * fs_khz as i32); - let delta0_q13 = rshift_round(smulbb(pred_q13[0] - pred0_q13, denom_q16), 16); - let delta1_q13 = rshift_round(smulbb(pred_q13[1] - pred1_q13, denom_q16), 16); - let interp_len = (STEREO_INTERP_LEN_MS * fs_khz as usize).min(frame_length); - - for n in 0..interp_len { - pred0_q13 += delta0_q13; - pred1_q13 += delta1_q13; - let mut sum = (x1[n] as i32 + x1[n + 2] as i32 + ((x1[n + 1] as i32) << 1)) << 9; - let sum_after_pred0 = smlawb((x2[n + 1] as i32) << 8, sum, pred0_q13); - let sum_after_pred1 = smlawb(sum_after_pred0, (x1[n + 1] as i32) << 11, pred1_q13); - sum = sum_after_pred1; - x2[n + 1] = sat16(rshift_round(sum, 8)); - } - - pred0_q13 = pred_q13[0]; - pred1_q13 = pred_q13[1]; - for n in interp_len..frame_length { - let mut sum = (x1[n] as i32 + x1[n + 2] as i32 + ((x1[n + 1] as i32) << 1)) << 9; - sum = smlawb((x2[n + 1] as i32) << 8, sum, pred0_q13); - sum = smlawb(sum, (x1[n + 1] as i32) << 11, pred1_q13); - x2[n + 1] = sat16(rshift_round(sum, 8)); - } - state.pred_prev_q13 = [pred_q13[0] as i16, pred_q13[1] as i16]; - - for n in 0..frame_length { - let sum = x1[n + 1] as i32 + x2[n + 1] as i32; - let diff = x1[n + 1] as i32 - x2[n + 1] as i32; - x1[n + 1] = sat16(sum); - x2[n + 1] = sat16(diff); - } - mid.copy_from_slice(&x1[1..1 + frame_length]); - side.copy_from_slice(&x2[1..1 + frame_length]); -} - -/// Multiply signed low 16-bit halves. -/// -/// Params: signed `a32` and `b32`. -/// Returns: signed 32-bit product. -fn smulbb(a32: i32, b32: i32) -> i32 { - (a32 as i16 as i32) * (b32 as i16 as i32) -} - -/// Multiply by the signed low 16-bit half of `b32` and keep high word. -/// -/// Params: signed `a32` and signed `b32`. -/// Returns: high-word signed product. -fn smulwb(a32: i32, b32: i32) -> i32 { - let b16 = b32 as i16 as i32; - let high = (a32 >> 16) * b16; - let low = ((a32 & 0xFFFF) * b16) >> 16; - high.wrapping_add(low) -} - -/// Approximate `a32 + ((b32 * (i16)c32) >> 16)`. -/// -/// Params: accumulator `a32`, multiplicand `b32`, and signed `c32`. -/// Returns: accumulated high-word product. -fn smlawb(a32: i32, b32: i32, c32: i32) -> i32 { - a32.wrapping_add(smulwb(b32, c32)) -} - -/// Saturate to `i16`. -/// -/// Params: signed `value`. -/// Returns: clamped 16-bit PCM sample. -fn sat16(value: i32) -> i16 { - value.clamp(i16::MIN as i32, i16::MAX as i32) as i16 -} - -/// Round a signed arithmetic right shift. -/// -/// Params: signed `value` and positive `shift`. -/// Returns: rounded shifted value. -fn rshift_round(value: i32, shift: usize) -> i32 { - if shift == 1 { - (value >> 1) + (value & 1) - } else { - ((value >> (shift - 1)) + 1) >> 1 - } -} diff --git a/vendor/opus-decoder/src/silk/tables.rs b/vendor/opus-decoder/src/silk/tables.rs deleted file mode 100644 index 0fd08bd2..00000000 --- a/vendor/opus-decoder/src/silk/tables.rs +++ /dev/null @@ -1,380 +0,0 @@ -//! SILK tables used by stereo signaling and NLSF/LPC decode. - -/// Maximum SILK LPC order. -pub(super) const MAX_LPC_ORDER: usize = 16; -/// Maximum number of SILK subframes. -pub(super) const MAX_NB_SUBFR: usize = 4; -/// Maximum SILK subframe length in samples. -pub(super) const MAX_SUB_FRAME_LENGTH: usize = 80; -/// Maximum SILK frame length in samples. -pub(super) const MAX_FRAME_LENGTH: usize = 320; -/// LTP filter order. -pub(super) const LTP_ORDER: usize = 5; -/// LTP memory in milliseconds. -pub(super) const LTP_MEM_LENGTH_MS: usize = 20; -/// Minimum pitch lag in milliseconds. -pub(super) const PITCH_EST_MIN_LAG_MS: i32 = 2; -/// Maximum pitch lag in milliseconds. -pub(super) const PITCH_EST_MAX_LAG_MS: i32 = 18; -/// Maximum number of stage-2 contour codebooks. -pub(super) const PE_NB_CBKS_STAGE2_EXT: usize = 11; -/// Maximum number of stage-3 contour codebooks. -pub(super) const PE_NB_CBKS_STAGE3_MAX: usize = 34; -/// Number of stage-3 contour codebooks for 10 ms. -pub(super) const PE_NB_CBKS_STAGE3_10MS: usize = 12; -/// Number of stage-2 contour codebooks for 10 ms. -pub(super) const PE_NB_CBKS_STAGE2_10MS: usize = 3; -/// Quantization level adjustment in Q10. -pub(super) const QUANT_LEVEL_ADJUST_Q10: i32 = 80; -/// Unvoiced low quantization offset in Q10. -pub(super) const OFFSET_UVL_Q10: i32 = 100; -/// Unvoiced high quantization offset in Q10. -pub(super) const OFFSET_UVH_Q10: i32 = 240; -/// Voiced low quantization offset in Q10. -pub(super) const OFFSET_VL_Q10: i32 = 32; -/// Voiced high quantization offset in Q10. -pub(super) const OFFSET_VH_Q10: i32 = 100; -/// Number of quantized gain levels. -pub(super) const N_LEVELS_QGAIN: i32 = 64; -/// Minimum gain delta index. -pub(super) const MIN_DELTA_GAIN_QUANT: i32 = -4; -/// Maximum gain delta index. -pub(super) const MAX_DELTA_GAIN_QUANT: i32 = 36; - -/// Full SILK NLSF codebook descriptor used by NLSF reconstruction. -#[derive(Debug, Clone, Copy)] -pub(super) struct NlsfCodebook { - /// LPC order for the codebook. - pub order: usize, - /// Residual quantization step size in Q16. - pub quant_step_size_q16: i32, - /// Stage-1 centroids in Q8. - pub cb1_nlsf_q8: &'static [u8], - /// Stage-1 weights in Q9. - pub cb1_wght_q9: &'static [i16], - /// Predictive residual coefficients in Q8. - pub pred_q8: &'static [u8], - /// Packed predictor/entropy selector map. - pub ec_sel: &'static [u8], - /// Minimum deltas used during stabilization in Q15. - pub delta_min_q15: &'static [i16], -} - -/// Joint iCDF for stereo predictor coarse index (`silk_stereo_pred_joint_iCDF`). -pub(super) const STEREO_PRED_JOINT_ICDF: [u8; 25] = [ - 249, 247, 246, 245, 244, 234, 210, 202, 201, 200, 197, 174, 82, 59, 56, 55, 54, 46, 22, 12, 11, - 10, 9, 7, 0, -]; - -/// Uniform 3-way iCDF (`silk_uniform3_iCDF`). -pub(super) const UNIFORM3_ICDF: [u8; 3] = [171, 85, 0]; - -/// Uniform 5-way iCDF (`silk_uniform5_iCDF`). -pub(super) const UNIFORM5_ICDF: [u8; 5] = [205, 154, 102, 51, 0]; - -/// Mid-only stereo flag iCDF (`silk_stereo_only_code_mid_iCDF`). -pub(super) const STEREO_ONLY_CODE_MID_ICDF: [u8; 2] = [64, 0]; - -/// Stereo predictor dequantization table (`silk_stereo_pred_quant_Q13`). -pub(super) const STEREO_PRED_QUANT_Q13: [i32; 16] = [ - -13_732, -10_050, -8_266, -7_526, -6_500, -5_000, -2_950, -820, 820, 2_950, 5_000, 6_500, - 7_526, 8_266, 10_050, 13_732, -]; - -/// LBRR-flag iCDF for 2 internal SILK frames (`silk_LBRR_flags_2_iCDF`). -pub(super) const LBRR_FLAGS_2_ICDF: [u8; 3] = [203, 150, 0]; - -/// LBRR-flag iCDF for 3 internal SILK frames (`silk_LBRR_flags_3_iCDF`). -pub(super) const LBRR_FLAGS_3_ICDF: [u8; 7] = [215, 195, 166, 125, 110, 82, 0]; - -/// Return LBRR iCDF table for given internal frame count. -/// -/// Params: `internal_frames` is number of 20 ms SILK-internal frames in one Opus frame. -/// Returns: table slice for 2/3-frame packets, or `None` for unsupported counts. -pub(super) fn lbrr_flags_icdf(internal_frames: usize) -> Option<&'static [u8]> { - match internal_frames { - 2 => Some(&LBRR_FLAGS_2_ICDF), - 3 => Some(&LBRR_FLAGS_3_ICDF), - _ => None, - } -} - -/// Piecewise-linear cosine approximation table in Q12. -pub(super) const LSF_COS_TAB: [i16; 129] = [ - 8192, 8190, 8182, 8170, 8152, 8130, 8104, 8072, 8034, 7994, 7946, 7896, 7840, 7778, 7714, 7644, - 7568, 7490, 7406, 7318, 7226, 7128, 7026, 6922, 6812, 6698, 6580, 6458, 6332, 6204, 6070, 5934, - 5792, 5648, 5502, 5352, 5198, 5040, 4880, 4718, 4552, 4382, 4212, 4038, 3862, 3684, 3502, 3320, - 3136, 2948, 2760, 2570, 2378, 2186, 1990, 1794, 1598, 1400, 1202, 1002, 802, 602, 402, 202, 0, - -202, -402, -602, -802, -1002, -1202, -1400, -1598, -1794, -1990, -2186, -2378, -2570, -2760, - -2948, -3136, -3320, -3502, -3684, -3862, -4038, -4212, -4382, -4552, -4718, -4880, -5040, - -5198, -5352, -5502, -5648, -5792, -5934, -6070, -6204, -6332, -6458, -6580, -6698, -6812, - -6922, -7026, -7128, -7226, -7318, -7406, -7490, -7568, -7644, -7714, -7778, -7840, -7896, - -7946, -7994, -8034, -8072, -8104, -8130, -8152, -8170, -8182, -8190, -8192, -]; - -const NLSF_CB1_NB_MB_Q8: [u8; 320] = [ - 12, 35, 60, 83, 108, 132, 157, 180, 206, 228, 15, 32, 55, 77, 101, 125, 151, 175, 201, 225, 19, - 42, 66, 89, 114, 137, 162, 184, 209, 230, 12, 25, 50, 72, 97, 120, 147, 172, 200, 223, 26, 44, - 69, 90, 114, 135, 159, 180, 205, 225, 13, 22, 53, 80, 106, 130, 156, 180, 205, 228, 15, 25, 44, - 64, 90, 115, 142, 168, 196, 222, 19, 24, 62, 82, 100, 120, 145, 168, 190, 214, 22, 31, 50, 79, - 103, 120, 151, 170, 203, 227, 21, 29, 45, 65, 106, 124, 150, 171, 196, 224, 30, 49, 75, 97, - 121, 142, 165, 186, 209, 229, 19, 25, 52, 70, 93, 116, 143, 166, 192, 219, 26, 34, 62, 75, 97, - 118, 145, 167, 194, 217, 25, 33, 56, 70, 91, 113, 143, 165, 196, 223, 21, 34, 51, 72, 97, 117, - 145, 171, 196, 222, 20, 29, 50, 67, 90, 117, 144, 168, 197, 221, 22, 31, 48, 66, 95, 117, 146, - 168, 196, 222, 24, 33, 51, 77, 116, 134, 158, 180, 200, 224, 21, 28, 70, 87, 106, 124, 149, - 170, 194, 217, 26, 33, 53, 64, 83, 117, 152, 173, 204, 225, 27, 34, 65, 95, 108, 129, 155, 174, - 210, 225, 20, 26, 72, 99, 113, 131, 154, 176, 200, 219, 34, 43, 61, 78, 93, 114, 155, 177, 205, - 229, 23, 29, 54, 97, 124, 138, 163, 179, 209, 229, 30, 38, 56, 89, 118, 129, 158, 178, 200, - 231, 21, 29, 49, 63, 85, 111, 142, 163, 193, 222, 27, 48, 77, 103, 133, 158, 179, 196, 215, - 232, 29, 47, 74, 99, 124, 151, 176, 198, 220, 237, 33, 42, 61, 76, 93, 121, 155, 174, 207, 225, - 29, 53, 87, 112, 136, 154, 170, 188, 208, 227, 24, 30, 52, 84, 131, 150, 166, 186, 203, 229, - 37, 48, 64, 84, 104, 118, 156, 177, 201, 230, -]; - -const NLSF_CB1_WGHT_NB_MB_Q9: [i16; 320] = [ - 2897, 2314, 2314, 2314, 2287, 2287, 2314, 2300, 2327, 2287, 2888, 2580, 2394, 2367, 2314, 2274, - 2274, 2274, 2274, 2194, 2487, 2340, 2340, 2314, 2314, 2314, 2340, 2340, 2367, 2354, 3216, 2766, - 2340, 2340, 2314, 2274, 2221, 2207, 2261, 2194, 2460, 2474, 2367, 2394, 2394, 2394, 2394, 2367, - 2407, 2314, 3479, 3056, 2127, 2207, 2274, 2274, 2274, 2287, 2314, 2261, 3282, 3141, 2580, 2394, - 2247, 2221, 2207, 2194, 2194, 2114, 4096, 3845, 2221, 2620, 2620, 2407, 2314, 2394, 2367, 2074, - 3178, 3244, 2367, 2221, 2553, 2434, 2340, 2314, 2167, 2221, 3338, 3488, 2726, 2194, 2261, 2460, - 2354, 2367, 2207, 2101, 2354, 2420, 2327, 2367, 2394, 2420, 2420, 2420, 2460, 2367, 3779, 3629, - 2434, 2527, 2367, 2274, 2274, 2300, 2207, 2048, 3254, 3225, 2713, 2846, 2447, 2327, 2300, 2300, - 2274, 2127, 3263, 3300, 2753, 2806, 2447, 2261, 2261, 2247, 2127, 2101, 2873, 2981, 2633, 2367, - 2407, 2354, 2194, 2247, 2247, 2114, 3225, 3197, 2633, 2580, 2274, 2181, 2247, 2221, 2221, 2141, - 3178, 3310, 2740, 2407, 2274, 2274, 2274, 2287, 2194, 2114, 3141, 3272, 2460, 2061, 2287, 2500, - 2367, 2487, 2434, 2181, 3507, 3282, 2314, 2700, 2647, 2474, 2367, 2394, 2340, 2127, 3423, 3535, - 3038, 3056, 2300, 1950, 2221, 2274, 2274, 2274, 3404, 3366, 2087, 2687, 2873, 2354, 2420, 2274, - 2474, 2540, 3760, 3488, 1950, 2660, 2897, 2527, 2394, 2367, 2460, 2261, 3028, 3272, 2740, 2888, - 2740, 2154, 2127, 2287, 2234, 2247, 3695, 3657, 2025, 1969, 2660, 2700, 2580, 2500, 2327, 2367, - 3207, 3413, 2354, 2074, 2888, 2888, 2340, 2487, 2247, 2167, 3338, 3366, 2846, 2780, 2327, 2154, - 2274, 2287, 2114, 2061, 2327, 2300, 2181, 2167, 2181, 2367, 2633, 2700, 2700, 2553, 2407, 2434, - 2221, 2261, 2221, 2221, 2340, 2420, 2607, 2700, 3038, 3244, 2806, 2888, 2474, 2074, 2300, 2314, - 2354, 2380, 2221, 2154, 2127, 2287, 2500, 2793, 2793, 2620, 2580, 2367, 3676, 3713, 2234, 1838, - 2181, 2753, 2726, 2673, 2513, 2207, 2793, 3160, 2726, 2553, 2846, 2513, 2181, 2394, 2221, 2181, -]; - -const NLSF_PRED_NB_MB_Q8: [u8; 18] = [ - 179, 138, 140, 148, 151, 149, 153, 151, 163, 116, 67, 82, 59, 92, 72, 100, 89, 92, -]; - -const NLSF_DELTA_MIN_NB_MB_Q15: [i16; 11] = [250, 3, 6, 3, 3, 3, 4, 3, 3, 3, 461]; - -const NLSF_CB1_WB_Q8: [u8; 512] = [ - 7, 23, 38, 54, 69, 85, 100, 116, 131, 147, 162, 178, 193, 208, 223, 239, 13, 25, 41, 55, 69, - 83, 98, 112, 127, 142, 157, 171, 187, 203, 220, 236, 15, 21, 34, 51, 61, 78, 92, 106, 126, 136, - 152, 167, 185, 205, 225, 240, 10, 21, 36, 50, 63, 79, 95, 110, 126, 141, 157, 173, 189, 205, - 221, 237, 17, 20, 37, 51, 59, 78, 89, 107, 123, 134, 150, 164, 184, 205, 224, 240, 10, 15, 32, - 51, 67, 81, 96, 112, 129, 142, 158, 173, 189, 204, 220, 236, 8, 21, 37, 51, 65, 79, 98, 113, - 126, 138, 155, 168, 179, 192, 209, 218, 12, 15, 34, 55, 63, 78, 87, 108, 118, 131, 148, 167, - 185, 203, 219, 236, 16, 19, 32, 36, 56, 79, 91, 108, 118, 136, 154, 171, 186, 204, 220, 237, - 11, 28, 43, 58, 74, 89, 105, 120, 135, 150, 165, 180, 196, 211, 226, 241, 6, 16, 33, 46, 60, - 75, 92, 107, 123, 137, 156, 169, 185, 199, 214, 225, 11, 19, 30, 44, 57, 74, 89, 105, 121, 135, - 152, 169, 186, 202, 218, 234, 12, 19, 29, 46, 57, 71, 88, 100, 120, 132, 148, 165, 182, 199, - 216, 233, 17, 23, 35, 46, 56, 77, 92, 106, 123, 134, 152, 167, 185, 204, 222, 237, 14, 17, 45, - 53, 63, 75, 89, 107, 115, 132, 151, 171, 188, 206, 221, 240, 9, 16, 29, 40, 56, 71, 88, 103, - 119, 137, 154, 171, 189, 205, 222, 237, 16, 19, 36, 48, 57, 76, 87, 105, 118, 132, 150, 167, - 185, 202, 218, 236, 12, 17, 29, 54, 71, 81, 94, 104, 126, 136, 149, 164, 182, 201, 221, 237, - 15, 28, 47, 62, 79, 97, 115, 129, 142, 155, 168, 180, 194, 208, 223, 238, 8, 14, 30, 45, 62, - 78, 94, 111, 127, 143, 159, 175, 192, 207, 223, 239, 17, 30, 49, 62, 79, 92, 107, 119, 132, - 145, 160, 174, 190, 204, 220, 235, 14, 19, 36, 45, 61, 76, 91, 108, 121, 138, 154, 172, 189, - 205, 222, 238, 12, 18, 31, 45, 60, 76, 91, 107, 123, 138, 154, 171, 187, 204, 221, 236, 13, 17, - 31, 43, 53, 70, 83, 103, 114, 131, 149, 167, 185, 203, 220, 237, 17, 22, 35, 42, 58, 78, 93, - 110, 125, 139, 155, 170, 188, 206, 224, 240, 8, 15, 34, 50, 67, 83, 99, 115, 131, 146, 162, - 178, 193, 209, 224, 239, 13, 16, 41, 66, 73, 86, 95, 111, 128, 137, 150, 163, 183, 206, 225, - 241, 17, 25, 37, 52, 63, 75, 92, 102, 119, 132, 144, 160, 175, 191, 212, 231, 19, 31, 49, 65, - 83, 100, 117, 133, 147, 161, 174, 187, 200, 213, 227, 242, 18, 31, 52, 68, 88, 103, 117, 126, - 138, 149, 163, 177, 192, 207, 223, 239, 16, 29, 47, 61, 76, 90, 106, 119, 133, 147, 161, 176, - 193, 209, 224, 240, 15, 21, 35, 50, 61, 73, 86, 97, 110, 119, 129, 141, 175, 198, 218, 237, -]; - -const NLSF_CB1_WGHT_WB_Q9: [i16; 512] = [ - 3657, 2925, 2925, 2925, 2925, 2925, 2925, 2925, 2925, 2925, 2925, 2925, 2963, 2963, 2925, 2846, - 3216, 3085, 2972, 3056, 3056, 3010, 3010, 3010, 2963, 2963, 3010, 2972, 2888, 2846, 2846, 2726, - 3920, 4014, 2981, 3207, 3207, 2934, 3056, 2846, 3122, 3244, 2925, 2846, 2620, 2553, 2780, 2925, - 3516, 3197, 3010, 3103, 3019, 2888, 2925, 2925, 2925, 2925, 2888, 2888, 2888, 2888, 2888, 2753, - 5054, 5054, 2934, 3573, 3385, 3056, 3085, 2793, 3160, 3160, 2972, 2846, 2513, 2540, 2753, 2888, - 4428, 4149, 2700, 2753, 2972, 3010, 2925, 2846, 2981, 3019, 2925, 2925, 2925, 2925, 2888, 2726, - 3620, 3019, 2972, 3056, 3056, 2873, 2806, 3056, 3216, 3047, 2981, 3291, 3291, 2981, 3310, 2991, - 5227, 5014, 2540, 3338, 3526, 3385, 3197, 3094, 3376, 2981, 2700, 2647, 2687, 2793, 2846, 2673, - 5081, 5174, 4615, 4428, 2460, 2897, 3047, 3207, 3169, 2687, 2740, 2888, 2846, 2793, 2846, 2700, - 3122, 2888, 2963, 2925, 2925, 2925, 2925, 2963, 2963, 2963, 2963, 2925, 2925, 2963, 2963, 2963, - 4202, 3207, 2981, 3103, 3010, 2888, 2888, 2925, 2972, 2873, 2916, 3019, 2972, 3010, 3197, 2873, - 3760, 3760, 3244, 3103, 2981, 2888, 2925, 2888, 2972, 2934, 2793, 2793, 2846, 2888, 2888, 2660, - 3854, 4014, 3207, 3122, 3244, 2934, 3047, 2963, 2963, 3085, 2846, 2793, 2793, 2793, 2793, 2580, - 3845, 4080, 3357, 3516, 3094, 2740, 3010, 2934, 3122, 3085, 2846, 2846, 2647, 2647, 2846, 2806, - 5147, 4894, 3225, 3845, 3441, 3169, 2897, 3413, 3451, 2700, 2580, 2673, 2740, 2846, 2806, 2753, - 4109, 3789, 3291, 3160, 2925, 2888, 2888, 2925, 2793, 2740, 2793, 2740, 2793, 2846, 2888, 2806, - 5081, 5054, 3047, 3545, 3244, 3056, 3085, 2944, 3103, 2897, 2740, 2740, 2740, 2846, 2793, 2620, - 4309, 4309, 2860, 2527, 3207, 3376, 3376, 3075, 3075, 3376, 3056, 2846, 2647, 2580, 2726, 2753, - 3056, 2916, 2806, 2888, 2740, 2687, 2897, 3103, 3150, 3150, 3216, 3169, 3056, 3010, 2963, 2846, - 4375, 3882, 2925, 2888, 2846, 2888, 2846, 2846, 2888, 2888, 2888, 2846, 2888, 2925, 2888, 2846, - 2981, 2916, 2916, 2981, 2981, 3056, 3122, 3216, 3150, 3056, 3010, 2972, 2972, 2972, 2925, 2740, - 4229, 4149, 3310, 3347, 2925, 2963, 2888, 2981, 2981, 2846, 2793, 2740, 2846, 2846, 2846, 2793, - 4080, 4014, 3103, 3010, 2925, 2925, 2925, 2888, 2925, 2925, 2846, 2846, 2846, 2793, 2888, 2780, - 4615, 4575, 3169, 3441, 3207, 2981, 2897, 3038, 3122, 2740, 2687, 2687, 2687, 2740, 2793, 2700, - 4149, 4269, 3789, 3657, 2726, 2780, 2888, 2888, 3010, 2972, 2925, 2846, 2687, 2687, 2793, 2888, - 4215, 3554, 2753, 2846, 2846, 2888, 2888, 2888, 2925, 2925, 2888, 2925, 2925, 2925, 2963, 2888, - 5174, 4921, 2261, 3432, 3789, 3479, 3347, 2846, 3310, 3479, 3150, 2897, 2460, 2487, 2753, 2925, - 3451, 3685, 3122, 3197, 3357, 3047, 3207, 3207, 2981, 3216, 3085, 2925, 2925, 2687, 2540, 2434, - 2981, 3010, 2793, 2793, 2740, 2793, 2846, 2972, 3056, 3103, 3150, 3150, 3150, 3103, 3010, 3010, - 2944, 2873, 2687, 2726, 2780, 3010, 3432, 3545, 3357, 3244, 3056, 3010, 2963, 2925, 2888, 2846, - 3019, 2944, 2897, 3010, 3010, 2972, 3019, 3103, 3056, 3056, 3010, 2888, 2846, 2925, 2925, 2888, - 3920, 3967, 3010, 3197, 3357, 3216, 3291, 3291, 3479, 3704, 3441, 2726, 2181, 2460, 2580, 2607, -]; - -const NLSF_PRED_WB_Q8: [u8; 30] = [ - 175, 148, 160, 176, 178, 173, 174, 164, 177, 174, 196, 182, 198, 192, 182, 68, 62, 66, 60, 72, - 117, 85, 90, 118, 136, 151, 142, 160, 142, 155, -]; - -const NLSF_DELTA_MIN_WB_Q15: [i16; 17] = - [100, 3, 40, 3, 3, 3, 5, 14, 14, 10, 11, 3, 8, 9, 7, 3, 347]; - -/// Narrowband/mediumband NLSF codebook. -pub(super) const NLSF_CB_NB_MB: NlsfCodebook = NlsfCodebook { - order: 10, - quant_step_size_q16: 11_796, - cb1_nlsf_q8: &NLSF_CB1_NB_MB_Q8, - cb1_wght_q9: &NLSF_CB1_WGHT_NB_MB_Q9, - pred_q8: &NLSF_PRED_NB_MB_Q8, - ec_sel: &super::entropy_tables::NLSF_CB2_SELECT_NB_MB, - delta_min_q15: &NLSF_DELTA_MIN_NB_MB_Q15, -}; - -/// Wideband NLSF codebook. -pub(super) const NLSF_CB_WB: NlsfCodebook = NlsfCodebook { - order: 16, - quant_step_size_q16: 9_830, - cb1_nlsf_q8: &NLSF_CB1_WB_Q8, - cb1_wght_q9: &NLSF_CB1_WGHT_WB_Q9, - pred_q8: &NLSF_PRED_WB_Q8, - ec_sel: &super::entropy_tables::NLSF_CB2_SELECT_WB, - delta_min_q15: &NLSF_DELTA_MIN_WB_Q15, -}; - -/// Quantization offsets in Q10 indexed by `[signal_type >> 1][quant_offset_type]`. -pub(super) const QUANTIZATION_OFFSETS_Q10: [[i32; 2]; 2] = [ - [OFFSET_UVL_Q10, OFFSET_UVH_Q10], - [OFFSET_VL_Q10, OFFSET_VH_Q10], -]; - -/// LTP scaling factors in Q14 indexed by `ltp_scale_index`. -pub(super) const LTP_SCALES_Q14: [i16; 3] = [15_565, 12_288, 8_192]; - -/// 20 ms pitch contour table for NB stage-2. -pub(super) const CB_LAGS_STAGE2_NB: [[i8; PE_NB_CBKS_STAGE2_EXT]; MAX_NB_SUBFR] = [ - [0, 2, -1, -1, -1, 0, 0, 1, 1, 0, 1], - [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0], - [0, -1, 2, 1, 0, 1, 1, 0, 0, -1, -1], -]; - -/// 20 ms pitch contour table for MB/WB stage-3. -pub(super) const CB_LAGS_STAGE3_WB: [[i8; PE_NB_CBKS_STAGE3_MAX]; MAX_NB_SUBFR] = [ - [ - 0, 0, 1, -1, 0, 1, -1, 0, -1, 1, -2, 2, -2, -2, 2, -3, 2, 3, -3, -4, 3, -4, 4, 4, -5, 5, - -6, -5, 6, -7, 6, 5, 8, -9, - ], - [ - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 1, -1, 0, 1, -1, -1, 1, -1, 2, 1, -1, 2, -2, -2, - 2, -2, 2, 2, 3, -3, - ], - [ - 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, -1, 1, 0, 0, 2, 1, -1, 2, -1, -1, 2, -1, 2, 2, - -1, 3, -2, -2, -2, 3, - ], - [ - 0, 1, 0, 0, 1, 0, 1, -1, 2, -1, 2, -1, 2, 3, -2, 3, -2, -2, 4, 4, -3, 5, -3, -4, 6, -4, 6, - 5, -5, 8, -6, -5, -7, 9, - ], -]; - -/// 10 ms pitch contour table for NB stage-2. -pub(super) const CB_LAGS_STAGE2_10MS_NB: [[i8; PE_NB_CBKS_STAGE2_10MS]; 2] = [[0, 1, 0], [0, 0, 1]]; - -/// 10 ms pitch contour table for MB/WB stage-3. -pub(super) const CB_LAGS_STAGE3_10MS_WB: [[i8; PE_NB_CBKS_STAGE3_10MS]; 2] = [ - [0, 0, 1, -1, 1, -1, 2, -2, 2, -2, 3, -3], - [0, 1, 0, 1, -1, 2, -1, 2, -2, 3, -2, 3], -]; - -/// LTP VQ codebook 0 in Q7. -pub(super) const LTP_VQ_0_Q7: [[i8; LTP_ORDER]; 8] = [ - [4, 6, 24, 7, 5], - [0, 0, 2, 0, 0], - [12, 28, 41, 13, -4], - [-9, 15, 42, 25, 14], - [1, -2, 62, 41, -9], - [-10, 37, 65, -4, 3], - [-6, 4, 66, 7, -8], - [16, 14, 38, -3, 33], -]; - -/// LTP VQ codebook 1 in Q7. -pub(super) const LTP_VQ_1_Q7: [[i8; LTP_ORDER]; 16] = [ - [13, 22, 39, 23, 12], - [-1, 36, 64, 27, -6], - [-7, 10, 55, 43, 17], - [1, 1, 8, 1, 1], - [6, -11, 74, 53, -9], - [-12, 55, 76, -12, 8], - [-3, 3, 93, 27, -4], - [26, 39, 59, 3, -8], - [2, 0, 77, 11, 9], - [-8, 22, 44, -6, 7], - [40, 9, 26, 3, 9], - [-7, 20, 101, -7, 4], - [3, -8, 42, 26, 0], - [-15, 33, 68, 2, 23], - [-2, 55, 46, -2, 15], - [3, -1, 21, 16, 41], -]; - -/// LTP VQ codebook 2 in Q7. -pub(super) const LTP_VQ_2_Q7: [[i8; LTP_ORDER]; 32] = [ - [-6, 27, 61, 39, 5], - [-11, 42, 88, 4, 1], - [-2, 60, 65, 6, -4], - [-1, -5, 73, 56, 1], - [-9, 19, 94, 29, -9], - [0, 12, 99, 6, 4], - [8, -19, 102, 46, -13], - [3, 2, 13, 3, 2], - [9, -21, 84, 72, -18], - [-11, 46, 104, -22, 8], - [18, 38, 48, 23, 0], - [-16, 70, 83, -21, 11], - [5, -11, 117, 22, -8], - [-6, 23, 117, -12, 3], - [3, -8, 95, 28, 4], - [-10, 15, 77, 60, -15], - [-1, 4, 124, 2, -4], - [3, 38, 84, 24, -25], - [2, 13, 42, 13, 31], - [21, -4, 56, 46, -1], - [-1, 35, 79, -13, 19], - [-7, 65, 88, -9, -14], - [20, 4, 81, 49, -29], - [20, 0, 75, 3, -17], - [5, -9, 44, 92, -8], - [1, -3, 22, 69, 31], - [-6, 95, 41, -12, 5], - [39, 67, 16, -4, 1], - [0, -6, 120, 55, -36], - [-13, 44, 122, 4, -24], - [81, 5, 11, 3, 7], - [2, 0, 9, 10, 88], -]; - -/// Select the NLSF codebook for the current internal SILK rate. -/// -/// Params: `fs_khz` is the internal SILK sampling rate in kHz. -/// Returns: NB/MB codebook for 8/12 kHz or WB codebook for 16 kHz. -pub(super) fn nlsf_codebook(fs_khz: u32) -> &'static NlsfCodebook { - if fs_khz == 16 { - &NLSF_CB_WB - } else { - &NLSF_CB_NB_MB - } -}