From: Damian Myrda Date: Tue, 20 Aug 2024 01:20:15 +0000 (-0500) Subject: batman X-Git-Url: http://git.prime8.dev/?a=commitdiff_plain;h=09ffa9730ac261ec28b359ac749b5266a675e80e;p=janitor.git batman --- 09ffa9730ac261ec28b359ac749b5266a675e80e diff --git a/README.md b/README.md new file mode 100644 index 0000000..482ad73 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Janitor + +A tool for labelling files into three categories: speech, music, and noise. diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/cli/Cargo.lock b/cli/Cargo.lock new file mode 100644 index 0000000..6cd5bee --- /dev/null +++ b/cli/Cargo.lock @@ -0,0 +1,2517 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alsa" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37fe60779335388a88c01ac6c3be40304d1e349de3ada3b15f7808bb90fa9dce" +dependencies = [ + "alsa-sys", + "bitflags 2.6.0", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-walkdir" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20235b6899dd1cb74a9afac0abf5b4a20c0e500dd6537280f4096e1b9f14da20" +dependencies = [ + "async-fs", + "futures-lite", + "thiserror", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "bytemuck" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cc" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "claxon" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" + +[[package]] +name = "cmake" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "coreaudio-rs" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +dependencies = [ + "bitflags 1.3.2", + "core-foundation-sys", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9" +dependencies = [ + "bindgen", +] + +[[package]] +name = "cpal" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" +dependencies = [ + "alsa", + "core-foundation-sys", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "oboe", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fon" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad46a0e6c9bc688823a742aa969b5c08fdc56c2a436ee00d5c6fbcb5982c55c4" +dependencies = [ + "libm", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "janitor-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-walkdir", + "byte-slice-cast", + "clap", + "fon", + "itertools 0.13.0", + "knf-rs", + "mime_guess", + "reqwest", + "rodio", + "safetensors", + "serde", + "serde_json", + "spinoff", + "tokio", + "tokio-stream", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "knf-rs" +version = "0.2.4" +dependencies = [ + "eyre", + "knf-rs-sys", +] + +[[package]] +name = "knf-rs-sys" +version = "0.2.4" +dependencies = [ + "bindgen", + "cmake", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lewton" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" +dependencies = [ + "byteorder", + "ogg", + "tinyvec", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "object" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +dependencies = [ + "memchr", +] + +[[package]] +name = "oboe" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" +dependencies = [ + "jni", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" +dependencies = [ + "cc", +] + +[[package]] +name = "ogg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rodio" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6006a627c1a38d37f3d3a85c6575418cfe34a5392d60a686d0071e1c8d427acb" +dependencies = [ + "claxon", + "cpal", + "hound", + "lewton", + "symphonia", + "thiserror", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "safetensors" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7725d4d98fa515472f43a6e2bbf956c48e06b89bb50593a040e5945160214450" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spinoff" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20aa2ed67fbb202e7b716ff8bfc6571dd9301617767380197d701c31124e88f6" +dependencies = [ + "colored", + "once_cell", + "paste", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "symphonia" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9" +dependencies = [ + "lazy_static", + "symphonia-bundle-mp3", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01c2aae70f0f1fb096b6f0ff112a930b1fb3626178fba3ae68b09dce71706d4" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-core" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-metadata" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "syn" +version = "2.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.39.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 0000000..e431616 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "janitor-cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" +tokio = { version = "1.38.0", features = ["full"] } +tokio-stream = "0.1.15" +itertools = "0.13.0" +clap = { version = "4.5.8", features = ["derive"] } +spinoff = "0.8.0" +async-walkdir = "2.0.0" +mime_guess = "2.0.5" +rodio = "0.19.0" +fon = "0.6.0" +knf-rs = { path = "./fbank/" } +byte-slice-cast = "1.2.2" +safetensors = "0.4.4" +reqwest = { version = "0.12.5", features = ["multipart", "json"] } +serde = { version = "1.0.204", features = ["derive"] } +serde_json = "1.0.122" diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 0000000..9fd6a2c --- /dev/null +++ b/cli/README.md @@ -0,0 +1,7 @@ +# Janitor CLI + +Reads, parses, and sends data to the janitor service for labelling. + +## running + +Use `cargo run -- --help` to see all the arguments. diff --git a/cli/fbank/.gitignore b/cli/fbank/.gitignore new file mode 100644 index 0000000..8647666 --- /dev/null +++ b/cli/fbank/.gitignore @@ -0,0 +1,2 @@ +target/ +build/ \ No newline at end of file diff --git a/cli/fbank/.gitmodules b/cli/fbank/.gitmodules new file mode 100644 index 0000000..59a2219 --- /dev/null +++ b/cli/fbank/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sys/knf"] + path = sys/knf + url = https://github.com/csukuangfj/kaldi-native-fbank diff --git a/cli/fbank/BUILDING.md b/cli/fbank/BUILDING.md new file mode 100644 index 0000000..aef4cdc --- /dev/null +++ b/cli/fbank/BUILDING.md @@ -0,0 +1,20 @@ +# Building + +_Prepare knf_ + +```console +git clone https://github.com/thewh1teagle/knf-rs --recursive +``` + +_Build_ + +```console +cargo build +``` + +_Build knf_ + +```console +cmake -B build . -DKALDI_NATIVE_FBANK_BUILD_PYTHON=OFF -DKALDI_NATIVE_FBANK_BUILD_TESTS=OFF +cmake --build build --config Release +``` diff --git a/cli/fbank/Cargo.lock b/cli/fbank/Cargo.lock new file mode 100644 index 0000000..a687cb9 --- /dev/null +++ b/cli/fbank/Cargo.lock @@ -0,0 +1,406 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cc" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "knf-rs" +version = "0.2.4" +dependencies = [ + "eyre", + "knf-rs-sys", +] + +[[package]] +name = "knf-rs-sys" +version = "0.2.4" +dependencies = [ + "bindgen", + "cmake", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/cli/fbank/Cargo.toml b/cli/fbank/Cargo.toml new file mode 100644 index 0000000..abe91cd --- /dev/null +++ b/cli/fbank/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "knf-rs" +version = "0.2.4" +edition = "2021" +license = "MIT" +description = "fbank features extractor without external dependencies" + +[dependencies] +knf-rs-sys = { path = "sys", version = "0.2.4", features = [] } +eyre = "0.6.12" + +[features] +default = [] diff --git a/cli/fbank/README.md b/cli/fbank/README.md new file mode 100644 index 0000000..be813fd --- /dev/null +++ b/cli/fbank/README.md @@ -0,0 +1,7 @@ +# knf-rs + +Forked from https://github.com/thewh1teagle/knf-rs + +_fbank features extractor without external dependencies_ + +Rust bindings to [kaldi-native-fbank](https://github.com/csukuangfj/kaldi-native-fbank) diff --git a/cli/fbank/src/lib.rs b/cli/fbank/src/lib.rs new file mode 100644 index 0000000..901d069 --- /dev/null +++ b/cli/fbank/src/lib.rs @@ -0,0 +1,69 @@ +use eyre::{bail, Context, Result}; + +const NUM_MEL_BINS: usize = 128; + +pub fn compute_fbank(samples: &[f32]) -> Result> { + if samples.is_empty() { + bail!("The samples array is empty. No features to compute.") + } + + let mut result = unsafe { + knf_rs_sys::ComputeFbank( + samples.as_ptr(), + samples.len().try_into().context("samples len")?, + ) + }; + + let frames = unsafe { + std::slice::from_raw_parts( + result.frames, + (result.num_frames * NUM_MEL_BINS as i32) as usize, + ) + .to_vec() + }; + + let features = unsafe { + Box::from_raw(frames.as_ptr() as *mut [[f32; NUM_MEL_BINS]; 0]) + .to_vec() + }; + + unsafe { + knf_rs_sys::DestroyFbankResult(&mut result as *mut _); + } + + Ok(features) +} + +pub fn convert_integer_to_float_audio(samples: &[i16], output: &mut [f32]) { + for (input, output) in samples.iter().zip(output.iter_mut()) { + *output = *input as f32 / 32768.0; + } +} + +#[cfg(test)] +mod tests { + use crate::compute_fbank; + use std::f32::consts::PI; + + fn generate_sine_wave(sample_rate: usize, duration: usize, frequency: f32) -> Vec { + let waveform_size = sample_rate * duration; + let mut waveform = Vec::with_capacity(waveform_size); + + for i in 0..waveform_size { + let sample = 0.5 * (2.0 * PI * frequency * i as f32 / sample_rate as f32).sin(); + waveform.push(sample); + } + waveform + } + + #[test] + fn it_works() { + let sample_rate = 16000; + let duration = 1; // 1 second + let frequency = 440.0; // A4 note + + let waveform = generate_sine_wave(sample_rate, duration, frequency); + let features = compute_fbank(&waveform); + println!("features: {:?}", features); + } +} diff --git a/cli/fbank/sys/Cargo.lock b/cli/fbank/sys/Cargo.lock new file mode 100644 index 0000000..75a16a3 --- /dev/null +++ b/cli/fbank/sys/Cargo.lock @@ -0,0 +1,382 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cc" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "knf-rs-sys" +version = "0.2.2" +dependencies = [ + "bindgen", + "cmake", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/cli/fbank/sys/Cargo.toml b/cli/fbank/sys/Cargo.toml new file mode 100644 index 0000000..6d5e6bc --- /dev/null +++ b/cli/fbank/sys/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "knf-rs-sys" +version = "0.2.4" +edition = "2021" +license = "MIT" +description = "fbank features extractor without external dependencies" + +include = [ + "knfc", + "knf/kaldi-native-fbank/csrc", + "knf/kaldi-native-fbank/CMakeLists.txt", + "knf/cmake", + "knf/CMakeLists.txt", + "knf/LICENSE", + "src/*.rs", + "build.rs", + "wrapper.hpp", +] + +[build-dependencies] +bindgen = "0.69.4" +cmake = "0.1.50" + +[features] +default = [] diff --git a/cli/fbank/sys/build.rs b/cli/fbank/sys/build.rs new file mode 100644 index 0000000..e6b0937 --- /dev/null +++ b/cli/fbank/sys/build.rs @@ -0,0 +1,110 @@ +use cmake::Config; +use std::env; +use std::path::{Path, PathBuf}; + +macro_rules! debug_log { + ($($arg:tt)*) => { + if std::env::var("BUILD_DEBUG").is_ok() { + println!("cargo:warning=[DEBUG] {}", format!($($arg)*)); + } + }; +} + +fn copy_folder(src: &Path, dst: &Path) { + std::fs::create_dir_all(dst).expect("Failed to create dst directory"); + if cfg!(unix) { + std::process::Command::new("cp") + .arg("-rf") + .arg(src) + .arg(dst.parent().expect("no parent")) + .status() + .expect("Failed to execute cp command"); + } + + if cfg!(windows) { + std::process::Command::new("robocopy.exe") + .arg("/e") + .arg(src) + .arg(dst) + .status() + .expect("Failed to execute robocopy command"); + } +} + +fn main() { + let target = env::var("TARGET").expect("no target"); + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("no out dir")); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("Failed to get CARGO_MANIFEST_DIR"); + let knf_src = Path::new(&manifest_dir).join("knf"); + let knf_dst = out_dir.join("knf"); + let knfc_src = Path::new(&manifest_dir).join("knfc"); + let knfc_dst = out_dir.join("knfc"); + let static_crt = env::var("KNF_STATIC_CRT").map(|v| v == "1").unwrap_or(false); + + let profile = if cfg!(debug_assertions) { + "Debug" + } else { + "Release" + }; + + debug_log!("TARGET: {}", target); + debug_log!("CARGO_MANIFEST_DIR: {}", manifest_dir); + debug_log!("OUT_DIR: {}", out_dir.display()); + + if !knf_dst.exists() { + debug_log!("Copy {} to {}", knf_src.display(), knf_dst.display()); + copy_folder(&knf_src, &knf_dst); + } + + if !knfc_dst.exists() { + debug_log!("Copy {} to {}", knfc_src.display(), knfc_dst.display()); + copy_folder(&knfc_src, &knfc_dst); + } + + // Bindings + let bindings = bindgen::Builder::default() + .header("wrapper.hpp") + .clang_arg(format!("-I{}", knfc_dst.display())) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Failed to generate bindings"); + + // Write the generated bindings to an output file + let bindings_path = out_dir.join("bindings.rs"); + bindings + .write_to_file(bindings_path) + .expect("Failed to write bindings"); + + println!("cargo:rerun-if-changed=./knf"); + println!("cargo:rerun-if-changed=./knfc"); + println!("cargo:rerun-if-changed=wrapper.h"); + + debug_log!("Bindings Created"); + + let mut config = Config::new(&knfc_dst); + + if cfg!(windows) { + config.static_crt(static_crt); + debug_log!("STATIC_CRT: {}", static_crt); + } + + config + .profile(profile) + .very_verbose(std::env::var("CMAKE_VERBOSE").is_ok()) // Not verbose by default + .always_configure(false) + .build(); + + println!("cargo:rustc-link-search={}", out_dir.join("lib").display()); + + // Link + if cfg!(target_os = "macos") { + println!("cargo:rustc-link-lib=c++"); + } + + if cfg!(target_os = "linux") { + println!("cargo:rustc-link-lib=stdc++"); + } + + println!("cargo:rustc-link-lib=static={}", "knfc"); + println!("cargo:rustc-link-lib=static={}", "kaldi-native-fbank-core"); +} diff --git a/cli/fbank/sys/knf/.clang-format b/cli/fbank/sys/knf/.clang-format new file mode 100644 index 0000000..c65e772 --- /dev/null +++ b/cli/fbank/sys/knf/.clang-format @@ -0,0 +1,9 @@ +--- +BasedOnStyle: Google +--- +Language: Cpp +Cpp11BracedListStyle: true +Standard: Cpp11 +DerivePointerAlignment: false +PointerAlignment: Right +--- diff --git a/cli/fbank/sys/knf/.github/scripts/filter.py b/cli/fbank/sys/knf/.github/scripts/filter.py new file mode 100644 index 0000000..7c36e64 --- /dev/null +++ b/cli/fbank/sys/knf/.github/scripts/filter.py @@ -0,0 +1,85 @@ +# This file is from +# https://github.com/acts-project/acts/blob/main/CI/iwyu/filter.py +import sys +import yaml +import re +import argparse +from collections import namedtuple + + +Config = namedtuple( + "Config", ["remove_lines", "replace_lines", "ignore_files"], defaults=[[], [], []] +) + + +class State: + skip_file: bool = False + + +def parse_config(config: Config): + remove_lines = [] + for s in config["remove_lines"]: + remove_lines.append(re.compile(s)) + + replace_lines = [] + for s in config["replace_lines"]: + s, r = list(s.items())[0] + replace_lines.append((re.compile(s), r)) + + ignore_files = [] + for s in config["ignore_files"]: + ignore_files.append(re.compile(s)) + + return Config( + remove_lines=remove_lines, + replace_lines=replace_lines, + ignore_files=ignore_files, + ) + + +def filter(line: str, config: Config, state: State): + if state.skip_file: + if line.endswith("---\n"): + state.skip_file = False + return None + + if line.endswith(" should add these lines:\n"): + for s in config.ignore_files: + if s.search(line): + state.skip_file = True + return None + + for s in config.remove_lines: + if s.search(line): + return None + + for s, r in config.replace_lines: + if s.search(line): + return s.sub(r, line) + + return line + + +parser = argparse.ArgumentParser() +parser.add_argument("config") +parser.add_argument("input") +parser.add_argument("output") +args = parser.parse_args() + +with open(args.config, "r") as config_file: + try: + config = yaml.safe_load(config_file) + config = parse_config(config) + except yaml.YAMLError as exc: + print(exc) + sys.exit(1) + +with open(args.input, "r") as input_file, open(args.output, "w") as output_file: + state = State() + + for line in input_file: + filtered_line = filter(line, config, state) + if filtered_line is not None: + output_file.write(filtered_line) + +sys.exit(0) diff --git a/cli/fbank/sys/knf/.github/scripts/filter.yaml b/cli/fbank/sys/knf/.github/scripts/filter.yaml new file mode 100644 index 0000000..23578cb --- /dev/null +++ b/cli/fbank/sys/knf/.github/scripts/filter.yaml @@ -0,0 +1,23 @@ +# This file is from +# https://github.com/acts-project/acts/blob/main/CI/iwyu/filter.yaml +remove_lines: + # ignore pybind11 + - "^(- )?#include " + - "^#include " + - "^#include " + - "^#include " + +replace_lines: + - "^#include ": "#include " + - "^#include ": "#include " + - "^#include ": "#include " + - "^#include ": "#include " + - "^#include ": "#include " + - "^#include ": "#include " + - "^#include ": "#include " + +ignore_files: + - ".*_deps/" diff --git a/cli/fbank/sys/knf/.github/workflows/build-wheels-aarch64.yaml b/cli/fbank/sys/knf/.github/workflows/build-wheels-aarch64.yaml new file mode 100644 index 0000000..1db0f2e --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/build-wheels-aarch64.yaml @@ -0,0 +1,61 @@ +name: build-wheels-aarch64 + +on: + push: + branches: + - wheel + tags: + - '*' + +concurrency: + group: build-wheels-aarch64-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_wheels_aarch64: + name: ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + python-version: ["cp37", "cp38", "cp39", "cp310", "cp311", "cp312"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: all + + # see https://cibuildwheel.readthedocs.io/en/stable/changelog/ + # for a list of versions + - name: Build wheels + uses: pypa/cibuildwheel@v2.16.5 + env: + CIBW_BUILD: "${{ matrix.python-version}}-* " + CIBW_SKIP: "cp27-* cp35-* cp36-* *-win32 pp* *-musllinux* *-manylinux_i686" + CIBW_BUILD_VERBOSITY: 3 + CIBW_ARCHS_LINUX: aarch64 + + - name: Display wheels + shell: bash + run: | + ls -lh ./wheelhouse/ + + - uses: actions/upload-artifact@v4 + with: + name: wheel-${{ matrix.python-version }} + path: ./wheelhouse/*.whl + + - name: Publish wheels to PyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + shell: bash + run: | + python3 -m pip install --upgrade pip + python3 -m pip install wheel twine setuptools + + twine upload ./wheelhouse/*.whl diff --git a/cli/fbank/sys/knf/.github/workflows/build-wheels-macos-arm64.yaml b/cli/fbank/sys/knf/.github/workflows/build-wheels-macos-arm64.yaml new file mode 100644 index 0000000..46c76a9 --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/build-wheels-macos-arm64.yaml @@ -0,0 +1,73 @@ +name: build-wheels-macos-arm64 + +on: + push: + branches: + - wheel + tags: + - '*' + workflow_dispatch: + +concurrency: + group: build-wheels-macos-arm64-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_wheels_macos_arm64: + name: ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest] + python-version: ["cp37", "cp38", "cp39", "cp310", "cp311", "cp312"] + + steps: + - uses: actions/checkout@v4 + + # see https://cibuildwheel.readthedocs.io/en/stable/changelog/ + # for a list of versions + - name: Build wheels + if: matrix.python-version == 'cp37' + uses: pypa/cibuildwheel@v2.11.4 + env: + CIBW_BUILD: "${{ matrix.python-version}}-* " + CIBW_ENVIRONMENT: KALDI_NATIVE_FBANK_CMAKE_ARGS="-DCMAKE_OSX_ARCHITECTURES='arm64'" + CIBW_ARCHS: "arm64" + CIBW_BUILD_VERBOSITY: 3 + + - name: Build wheels + if: matrix.python-version != 'cp37' + uses: pypa/cibuildwheel@v2.15.0 + env: + CIBW_BUILD: "${{ matrix.python-version}}-* " + CIBW_ENVIRONMENT: KALDI_NATIVE_FBANK_CMAKE_ARGS="-DCMAKE_OSX_ARCHITECTURES='arm64'" + CIBW_ARCHS: "arm64" + CIBW_BUILD_VERBOSITY: 3 + + - name: Display wheels + shell: bash + run: | + ls -lh ./wheelhouse/ + + - uses: actions/upload-artifact@v4 + with: + name: wheel-${{ matrix.python-version }} + path: ./wheelhouse/*.whl + + - name: Publish wheels to PyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + shell: bash + run: | + opts='--break-system-packages' + v=${{ matrix.python-version }} + if [[ $v == cp37 || $v == cp38 || $v == cp39 ]]; then + opts='' + fi + + python3 -m pip install $opts --upgrade pip + python3 -m pip install $opts wheel twine setuptools + + twine upload ./wheelhouse/*.whl diff --git a/cli/fbank/sys/knf/.github/workflows/build-wheels-macos-x64.yaml b/cli/fbank/sys/knf/.github/workflows/build-wheels-macos-x64.yaml new file mode 100644 index 0000000..a58dad3 --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/build-wheels-macos-x64.yaml @@ -0,0 +1,73 @@ +name: build-wheels-macos-x64 + +on: + push: + branches: + - wheel + tags: + - '*' + workflow_dispatch: + +concurrency: + group: build-wheels-macos-x64-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_wheels_x64: + name: ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest] + python-version: ["cp37", "cp38", "cp39", "cp310", "cp311", "cp312"] + + steps: + - uses: actions/checkout@v2 + + # see https://cibuildwheel.readthedocs.io/en/stable/changelog/ + # for a list of versions + - name: Build wheels + if: matrix.python-version == 'cp37' + uses: pypa/cibuildwheel@v2.11.4 + env: + CIBW_BUILD: "${{ matrix.python-version}}-* " + CIBW_ENVIRONMENT: KALDI_NATIVE_FBANK_CMAKE_ARGS="-DCMAKE_OSX_ARCHITECTURES='x86_64'" + CIBW_ARCHS: "x86_64" + CIBW_BUILD_VERBOSITY: 3 + + - name: Build wheels + if: matrix.python-version != 'cp37' + uses: pypa/cibuildwheel@v2.15.0 + env: + CIBW_BUILD: "${{ matrix.python-version}}-* " + CIBW_ENVIRONMENT: KALDI_NATIVE_FBANK_CMAKE_ARGS="-DCMAKE_OSX_ARCHITECTURES='x86_64'" + CIBW_ARCHS: "x86_64" + CIBW_BUILD_VERBOSITY: 3 + + - name: Display wheels + shell: bash + run: | + ls -lh ./wheelhouse/ + + - uses: actions/upload-artifact@v4 + with: + name: wheel-${{ matrix.python-version }} + path: ./wheelhouse/*.whl + + - name: Publish wheels to PyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + shell: bash + run: | + opts='--break-system-packages' + v=${{ matrix.python-version }} + if [[ $v == cp37 || $v == cp38 || $v == cp39 ]]; then + opts='' + fi + + python3 -m pip install $opts --upgrade pip + python3 -m pip install $opts wheel twine setuptools + + twine upload ./wheelhouse/*.whl diff --git a/cli/fbank/sys/knf/.github/workflows/build-wheels-win32.yaml b/cli/fbank/sys/knf/.github/workflows/build-wheels-win32.yaml new file mode 100644 index 0000000..030294d --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/build-wheels-win32.yaml @@ -0,0 +1,57 @@ +name: build-wheels-win32 + +on: + push: + branches: + - wheel + tags: + - '*' + workflow_dispatch: + +concurrency: + group: build-wheels-win32-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_wheels: + name: ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-latest] + python-version: ["cp37", "cp38", "cp39", "cp310", "cp311", "cp312"] + + steps: + - uses: actions/checkout@v4 + + # see https://cibuildwheel.readthedocs.io/en/stable/changelog/ + # for a list of versions + - name: Build wheels + uses: pypa/cibuildwheel@v2.16.5 + env: + CIBW_BUILD: "${{ matrix.python-version}}-* " + CIBW_ENVIRONMENT: KALDI_NATIVE_FBANK_CMAKE_ARGS="-A Win32" + CIBW_SKIP: "*-win_amd64" + CIBW_BUILD_VERBOSITY: 3 + + - name: Display wheels + shell: bash + run: | + ls -lh ./wheelhouse/ + + - uses: actions/upload-artifact@v4 + with: + name: wheel-${{ matrix.python-version }} + path: ./wheelhouse/*.whl + + - name: Publish wheels to PyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + shell: bash + run: | + python3 -m pip install --upgrade pip + python3 -m pip install wheel twine setuptools + + twine upload ./wheelhouse/*.whl diff --git a/cli/fbank/sys/knf/.github/workflows/build-wheels-win64.yaml b/cli/fbank/sys/knf/.github/workflows/build-wheels-win64.yaml new file mode 100644 index 0000000..029345c --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/build-wheels-win64.yaml @@ -0,0 +1,56 @@ +name: build-wheels-win64 + +on: + push: + branches: + - wheel + tags: + - '*' + workflow_dispatch: + +concurrency: + group: build-wheels-win64-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_wheels: + name: ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-latest] + python-version: ["cp37", "cp38", "cp39", "cp310", "cp311", "cp312"] + + steps: + - uses: actions/checkout@v4 + + # see https://cibuildwheel.readthedocs.io/en/stable/changelog/ + # for a list of versions + - name: Build wheels + uses: pypa/cibuildwheel@v2.16.5 + env: + CIBW_BUILD: "${{ matrix.python-version}}-* " + CIBW_SKIP: "cp27-* cp35-* *-win32 pp* *-musllinux*" + CIBW_BUILD_VERBOSITY: 3 + + - name: Display wheels + shell: bash + run: | + ls -lh ./wheelhouse/ + + - uses: actions/upload-artifact@v4 + with: + name: wheel-${{ matrix.python-version }} + path: ./wheelhouse/*.whl + + - name: Publish wheels to PyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + shell: bash + run: | + python3 -m pip install --upgrade pip + python3 -m pip install wheel twine setuptools + + twine upload ./wheelhouse/*.whl diff --git a/cli/fbank/sys/knf/.github/workflows/build-wheels.yaml b/cli/fbank/sys/knf/.github/workflows/build-wheels.yaml new file mode 100644 index 0000000..37844c0 --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/build-wheels.yaml @@ -0,0 +1,71 @@ +name: build-wheels + +on: + push: + branches: + - wheel + tags: + - '*' + workflow_dispatch: + +concurrency: + group: build-wheels-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_wheels: + name: ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + python-version: ["cp37", "cp38", "cp39", "cp310", "cp311", "cp312"] + + steps: + - uses: actions/checkout@v4 + + # see https://cibuildwheel.readthedocs.io/en/stable/changelog/ + # for a list of versions + - name: Build wheels + uses: pypa/cibuildwheel@v2.16.5 + env: + CIBW_BUILD: "${{ matrix.python-version}}-* " + CIBW_SKIP: "cp27-* cp35-* cp36-* *-win32 pp* *-musllinux* *-manylinux_i686" + CIBW_BUILD_VERBOSITY: 3 + + - name: Display wheels + shell: bash + run: | + ls -lh ./wheelhouse/ + + - uses: actions/upload-artifact@v4 + with: + name: wheel-${{ matrix.python-version }} + path: ./wheelhouse/*.whl + + - name: Publish wheels to PyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + shell: bash + run: | + python3 -m pip install --upgrade pip + python3 -m pip install wheel twine setuptools + + twine upload ./wheelhouse/*.whl + + - name: Build sdist + if: ${{ matrix.python-version == 'cp38' }} + shell: bash + run: | + python3 setup.py sdist + ls -l dist/* + + - name: Publish sdist to PyPI + if: ${{ matrix.python-version == 'cp38' }} + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + twine upload dist/*.tar.gz diff --git a/cli/fbank/sys/knf/.github/workflows/iwyu.yaml b/cli/fbank/sys/knf/.github/workflows/iwyu.yaml new file mode 100644 index 0000000..6cc3d4c --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/iwyu.yaml @@ -0,0 +1,171 @@ +name: iwyu + +on: + push: + branches: + - master + - iwyu-2 + pull_request: + branches: + - master + + workflow_dispatch: + +concurrency: + group: iwyu-${{ github.ref }} + cancel-in-progress: true + +# References +# https://github.com/official-stockfish/Stockfish/blob/master/.github/workflows/iwyu.yml +# https://github.com/acts-project/acts/actions/runs/8588671882/workflow +# https://github.com/acts-project/acts/tree/main/CI/iwyu + +jobs: + iwyu: + if: github.repository_owner == 'csukuangfj' || github.repository_owner == 'k2-fsa' + permissions: + contents: write # for stefanzweifel/git-auto-commit-action to push code in repo + name: Include what you use + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download required linux packages + shell: bash + run: | + echo "GITHUB_WORKSPACE: $GITHUB_WORKSPACE" + sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main' + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo apt update + sudo apt install -y libclang-17-dev clang-17 libc++-17-dev + + - name: Display llvm + shell: bash + run: | + ls -lh /usr/lib/*llvm* + + ls -lh /usr/bin/*clang* + + - name: cache-iwyu + id: cache-iwyu + uses: actions/cache@v4 + with: + path: /tmp/iwyu-install + key: iwyu-install-2024-04-13 + + - name: Get IWYU source + if: steps.cache-iwyu.outputs.cache-hit != 'true' + uses: actions/checkout@v4 + with: + repository: include-what-you-use/include-what-you-use + ref: clang_17 + path: iwyu + + - name: Build IWYU + if: steps.cache-iwyu.outputs.cache-hit != 'true' + shell: bash + run: | + mkdir iwyu-build iwyu-install + cmake -B iwyu-build -S iwyu -DCMAKE_PREFIX_PATH=/usr/lib/llvm-17 -DCMAKE_INSTALL_PREFIX=/tmp/iwyu-install + cmake --build iwyu-build --target install + + ls -lh /tmp/iwyu-install + tree /tmp/iwyu-install + + /tmp/iwyu-install/bin/include-what-you-use --version + + - name: setup path + shell: bash + run: | + echo "/tmp/iwyu-install/bin" >> "$GITHUB_PATH" + + - name: display include what you use version + shell: bash + run: | + include-what-you-use --version + which include-what-you-use + + include-what-you-use --help + + - uses: actions/upload-artifact@v4 + with: + name: iwyu-install + path: /tmpiwyu-install/* + + - name: Check + shell: bash + run: | + mkdir build + cd build + + cmake \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + .. + + - name: Run iwyu_tool.py + shell: bash + run: | + python3 /tmp/iwyu-install/bin/iwyu_tool.py -p build/ -j2 | tee iwyu-output.txt + + - name: Filter IWYU output + shell: bash + run: | + python3 .github/scripts/filter.py .github/scripts/filter.yaml iwyu-output.txt iwyu-filtered.txt + + - name: Show IWYU output + shell: bash + run: | + cat iwyu-output.txt + + - name: Show filtered IWYU output + shell: bash + run: | + cat iwyu-filtered.txt + + - name: Upload IWYU output + uses: actions/upload-artifact@v4 + with: + name: iwyu + path: | + iwyu-output.txt + iwyu-filtered.txt + + - name: Apply iwyu + shell: bash + run: | + git status + python3 /tmp/iwyu-install/bin/fix_includes.py < iwyu-filtered.txt + + rm -rf iwyu* + + - name: Show diff + shell: bash + run: | + git status + + git diff + git diff > a.diff + # Download a.diff + # Run + # git apply a.diff + + - name: Upload IWYU output + uses: actions/upload-artifact@v4 + with: + name: diff + path: | + a.diff + + # https://github.com/stefanzweifel/git-auto-commit-action + - uses: stefanzweifel/git-auto-commit-action@v5 + if: false + with: + file_pattern: '*.h *.cc' + commit_message: "apply iwyu changes" diff --git a/cli/fbank/sys/knf/.github/workflows/linux-macos.yaml b/cli/fbank/sys/knf/.github/workflows/linux-macos.yaml new file mode 100644 index 0000000..acde044 --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/linux-macos.yaml @@ -0,0 +1,76 @@ +name: linux-macos + +on: + push: + branches: + - master + pull_request: + branches: + - master + +concurrency: + group: linux-macos-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + linux_macos: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.8" + + - name: Install Python dependencies + shell: bash + run: | + python3 -m pip install --upgrade pip numpy + python3 -m pip install wheel twine setuptools + if [[ ${{ matrix.os }} == "ubuntu" ]]; then + python3 -m pip install torch==1.13.0+cpu -f https://download.pytorch.org/whl/torch_stable.html + else + python3 -m pip install torch==1.13.0 -f https://download.pytorch.org/whl/torch_stable.html + fi + + - name: Configure Cmake + shell: bash + run: | + mkdir build + cd build + cmake -D CMAKE_BUILD_TYPE=Release .. + + - name: Build kaldi-native-fbank for ubuntu/macos + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') + run: | + cd build + make -j2 + ctest --output-on-failure + + - name: Run tests for ubuntu/macos + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') + run: | + cd build + ctest --output-on-failure + + - name: Build Python + shell: bash + run: | + python3 -m pip install --verbose . + + - name: Display Python package version + shell: bash + run: | + python3 -c "import kaldi_native_fbank; print(kaldi_native_fbank.__version__)" + python3 -c "import kaldi_native_fbank; print(kaldi_native_fbank.__file__)" diff --git a/cli/fbank/sys/knf/.github/workflows/test-wheel.yaml b/cli/fbank/sys/knf/.github/workflows/test-wheel.yaml new file mode 100644 index 0000000..f16be22 --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/test-wheel.yaml @@ -0,0 +1,57 @@ +name: test-wheel + +on: + push: + branches: + - test-wheel + + workflow_dispatch: + + schedule: + # minute (0-59) + # hour (0-23) + # day of the month (1-31) + # month (1-12) + # day of the week (0-6) + # nightly build at 23:50 UTC time every day + - cron: "50 23 * * *" + +concurrency: + group: test-wheel-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + + +jobs: + test_wheel: + runs-on: ${{ matrix.os }} + name: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest, ubuntu-latest] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install kaldi-native-fbank + shell: bash + run: | + python3 -m pip install --upgrade pip numpy + python3 -m pip install kaldi-native-fbank + + - name: Display version + shell: bash + run: | + python3 -c "import kaldi_native_fbank; print(kaldi_native_fbank.__file__)" + python3 -c "import kaldi_native_fbank; print(kaldi_native_fbank.__version__)" diff --git a/cli/fbank/sys/knf/.github/workflows/windows-x64.yaml b/cli/fbank/sys/knf/.github/workflows/windows-x64.yaml new file mode 100644 index 0000000..ba25626 --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/windows-x64.yaml @@ -0,0 +1,81 @@ +name: windows-x64 + +on: + push: + branches: + - master + pull_request: + branches: + - master + +concurrency: + group: windows-x64-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + windows_x64: + name: Test on Win64 + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [windows-latest] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Configure CMake + shell: bash + run: | + mkdir build + cd build + cmake -D CMAKE_BUILD_TYPE=Release .. + + + - name: Build kaldi-native-fbank for windows + shell: bash + run: | + cd build + cmake --build . --target ALL_BUILD --config Release + cat CMakeCache.txt + + - name: Run tests for windows + shell: bash + run: | + cd build + ctest --verbose -C Release --output-on-failure -E py + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.8" + + - name: Install Python dependencies + shell: bash + run: | + python3 -m pip install --upgrade pip pytest + python3 -m pip install wheel twine setuptools + + - name: Build Python + shell: bash + run: | + python3 -m pip install --verbose . + + - name: Display Python package version + shell: bash + run: | + python3 -c "import kaldi_native_fbank; print(kaldi_native_fbank.__version__)" + python3 -c "import kaldi_native_fbank; print(kaldi_native_fbank.__file__)" + + - name: Run Python tests + shell: bash + run: | + cd kaldi-native-fbank/python/tests + python3 ./test_fbank_options.py + python3 ./test_frame_extraction_options.py + python3 ./test_mel_bank_options.py diff --git a/cli/fbank/sys/knf/.github/workflows/windows-x86.yaml b/cli/fbank/sys/knf/.github/workflows/windows-x86.yaml new file mode 100644 index 0000000..2808702 --- /dev/null +++ b/cli/fbank/sys/knf/.github/workflows/windows-x86.yaml @@ -0,0 +1,67 @@ +name: windows-x86 + +on: + push: + branches: + - master + pull_request: + branches: + - master + +concurrency: + group: windows-x86-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + windows_x86: + runs-on: ${{ matrix.os }} + name: ${{ matrix.vs-version }} + strategy: + fail-fast: false + matrix: + include: + - vs-version: vs2015 + toolset-version: v140 + os: windows-2019 + + - vs-version: vs2017 + toolset-version: v141 + os: windows-2019 + + - vs-version: vs2019 + toolset-version: v142 + os: windows-2022 + + - vs-version: vs2022 + toolset-version: v143 + os: windows-2022 + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Configure CMake + shell: bash + run: | + mkdir build + cd build + cmake -T ${{ matrix.toolset-version}},host=x64 -A Win32 -D CMAKE_BUILD_TYPE=Release -DKALDI_NATIVE_FBANK_BUILD_PYTHON=OFF .. + cat CMakeCache.txt + + + - name: Build kaldi-native-fbank for windows + shell: bash + run: | + cd build + cmake --build . --target ALL_BUILD --config Release + + - name: Run tests for windows + shell: bash + run: | + cd build + + ctest --verbose -C Release --output-on-failure -E py diff --git a/cli/fbank/sys/knf/.gitignore b/cli/fbank/sys/knf/.gitignore new file mode 100644 index 0000000..ce74a7e --- /dev/null +++ b/cli/fbank/sys/knf/.gitignore @@ -0,0 +1,8 @@ +build +dist +*.egg-info +**/__pycache__ +build-arm-linux-gnueabihf + +# vim +*.sw? diff --git a/cli/fbank/sys/knf/CMakeLists.txt b/cli/fbank/sys/knf/CMakeLists.txt new file mode 100644 index 0000000..8d289be --- /dev/null +++ b/cli/fbank/sys/knf/CMakeLists.txt @@ -0,0 +1,131 @@ +if("x${CMAKE_SOURCE_DIR}" STREQUAL "x${CMAKE_BINARY_DIR}") + message(FATAL_ERROR "\ +In-source build is not a good practice. +Please use: + mkdir build + cd build + cmake .. +to build this project" + ) +endif() + +if(CMAKE_TOOLCHAIN_FILE) + set(_BUILD_PYTHON OFF) + set(_BUILD_TESTS OFF) +else() + set(_BUILD_PYTHON ON) + set(_BUILD_TESTS ON) +endif() + +if(POLICY CMP0057) + cmake_policy(SET CMP0057 NEW) +endif() + +cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + +project(kaldi-native-fbank CXX C) + +set(KALDI_NATIVE_FBANK_VERSION "1.20.0") + +# Disable warning about +# +# "The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is +# not set. +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + cmake_policy(SET CMP0135 NEW) +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +set(CMAKE_SKIP_BUILD_RPATH FALSE) +set(BUILD_RPATH_USE_ORIGIN TRUE) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + +if(NOT APPLE) + set(kaldi_native_fbank_rpath_origin "$ORIGIN") +else() + set(kaldi_native_fbank_rpath_origin "@loader_path") +endif() + +set(CMAKE_INSTALL_RPATH ${kaldi_native_fbank_rpath_origin}) +set(CMAKE_BUILD_RPATH ${kaldi_native_fbank_rpath_origin}) + +set(CMAKE_CXX_STANDARD 14 CACHE STRING "The C++ version to be used.") + +if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON) +endif() + +# See +# https://stackoverflow.com/questions/33062728/cmake-link-shared-library-on-windows +if(BUILD_SHARED_LIBS AND MSVC) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() +message(STATUS "CMAKE_EXPORT_COMPILE_COMMANDS: ${CMAKE_EXPORT_COMPILE_COMMANDS}") + +message(STATUS "BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}") + +option(KALDI_NATIVE_FBANK_BUILD_TESTS "Whether to build tests or not" ${_BUILD_TESTS}) +option(KALDI_NATIVE_FBANK_BUILD_PYTHON "Whether to build Python extension" ${_BUILD_PYTHON}) +option(KALDI_NATIVE_FBANK_ENABLE_CHECK "Whether to build with log" OFF) + +message(STATUS "KALDI_NATIVE_FBANK_BUILD_TESTS: ${KALDI_NATIVE_FBANK_BUILD_TESTS}") +message(STATUS "KALDI_NATIVE_FBANK_BUILD_PYTHON: ${KALDI_NATIVE_FBANK_BUILD_PYTHON}") +message(STATUS "KALDI_NATIVE_FBANK_ENABLE_CHECK: ${KALDI_NATIVE_FBANK_ENABLE_CHECK}") + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +message(STATUS "KALDI_NATIVE_FBANK_ENABLE_CHECK: ${KALDI_NATIVE_FBANK_ENABLE_CHECK}") + +if(WIN32) + add_definitions(-DNOMINMAX) # Otherwise, std::max() and std::min() won't work +endif() + +if(KALDI_NATIVE_FBANK_BUILD_PYTHON) + include(pybind11) +endif() + +if(KALDI_NATIVE_FBANK_BUILD_TESTS) + enable_testing() + include(googletest) +endif() + +if(NOT CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install") +endif() + +message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") +message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") + +include(CheckIncludeFileCXX) +check_include_file_cxx(cxxabi.h KNF_HAVE_CXXABI_H) +check_include_file_cxx(execinfo.h KNF_HAVE_EXECINFO_H) + +include_directories(${CMAKE_SOURCE_DIR}) + +if(WIN32 AND MSVC) + # disable various warnings for MSVC + # 4244: '=': conversion from 'double' to 'float', possible loss of data + # 4267: 'return': conversion from 'size_t' to 'int32_t', possible loss of data + # 4624: destructor was implicitly defined as deleted because a base class destructor is inaccessible or deleted + set(disabled_warnings + /wd4244 + /wd4267 + /wd4624 + ) + message(STATUS "Disabled warnings: ${disabled_warnings}") + foreach(w IN LISTS disabled_warnings) + string(APPEND CMAKE_CXX_FLAGS " ${w} ") + endforeach() +endif() + +add_subdirectory(kaldi-native-fbank) diff --git a/cli/fbank/sys/knf/LICENSE b/cli/fbank/sys/knf/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/cli/fbank/sys/knf/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/cli/fbank/sys/knf/MANIFEST.in b/cli/fbank/sys/knf/MANIFEST.in new file mode 100644 index 0000000..ae2b482 --- /dev/null +++ b/cli/fbank/sys/knf/MANIFEST.in @@ -0,0 +1,5 @@ +include LICENSE +include README.md +include CMakeLists.txt +recursive-include kaldi-native-fbank *.* +recursive-include cmake *.* diff --git a/cli/fbank/sys/knf/README.md b/cli/fbank/sys/knf/README.md new file mode 100644 index 0000000..c806ab6 --- /dev/null +++ b/cli/fbank/sys/knf/README.md @@ -0,0 +1,109 @@ +# Introduction + +Kaldi-compatible online fbank feature extractor without external dependencies. + +Tested on the following architectures and operating systems: + + - Linux + - macOS + - Windows + - Android + - x86 + - arm + - aarch64 + +# Usage + +See the following CMake-based speech recognition (i.e., text-to-speech) projects +for its usage: + +- + - Specifically, please have a look at +- + +They use `kaldi-native-fbank` to compute fbank features for **real-time** +speech recognition. + +# Python APIs + +First, please install `kaldi-native-fbank` by + +```bash +git clone https://github.com/csukuangfj/kaldi-native-fbank +cd kaldi-native-fbank +python3 setup.py install +``` + +or use + +```bash +pip install kaldi-native-fbank +``` + +To check that you have installed `kaldi-native-fbank` successfully, please use + +``` +python3 -c "import kaldi_native_fbank; print(kaldi_native_fbank.__version__)" +``` + +which should print the version you have installed. + +Please refer to + + - + - + +for usages. + +For easier reference, we post the above file below: + +```python3 +#!/usr/bin/env python3 + +import sys + +try: + import kaldifeat +except: + print("Please install kaldifeat first") + sys.exit(0) + +import kaldi_native_fbank as knf +import torch + + +def main(): + sampling_rate = 16000 + samples = torch.randn(16000 * 10) + + opts = kaldifeat.FbankOptions() + opts.frame_opts.dither = 0 + opts.mel_opts.num_bins = 80 + opts.frame_opts.snip_edges = False + opts.mel_opts.debug_mel = False + + online_fbank = kaldifeat.OnlineFbank(opts) + + online_fbank.accept_waveform(sampling_rate, samples) + + opts = knf.FbankOptions() + opts.frame_opts.dither = 0 + opts.mel_opts.num_bins = 80 + opts.frame_opts.snip_edges = False + opts.mel_opts.debug_mel = False + + fbank = knf.OnlineFbank(opts) + fbank.accept_waveform(sampling_rate, samples.tolist()) + + assert online_fbank.num_frames_ready == fbank.num_frames_ready + for i in range(fbank.num_frames_ready): + f1 = online_fbank.get_frame(i) + f2 = torch.from_numpy(fbank.get_frame(i)) + assert torch.allclose(f1, f2, atol=1e-3), (i, (f1 - f2).abs().max()) + + +if __name__ == "__main__": + torch.manual_seed(20220825) + main() + print("success") +``` diff --git a/cli/fbank/sys/knf/build-arm-linux-gnueabihf.sh b/cli/fbank/sys/knf/build-arm-linux-gnueabihf.sh new file mode 100755 index 0000000..e8b9e25 --- /dev/null +++ b/cli/fbank/sys/knf/build-arm-linux-gnueabihf.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -x + +mkdir -p build-arm-linux-gnueabihf +cd build-arm-linux-gnueabihf +cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/arm-linux-gnueabihf.toolchain.cmake .. +make VERBOSE=1 -j4 +make install diff --git a/cli/fbank/sys/knf/cmake/Modules/FetchContent.cmake b/cli/fbank/sys/knf/cmake/Modules/FetchContent.cmake new file mode 100644 index 0000000..98cdf6c --- /dev/null +++ b/cli/fbank/sys/knf/cmake/Modules/FetchContent.cmake @@ -0,0 +1,916 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FetchContent +------------------ + +.. only:: html + + .. contents:: + +Overview +^^^^^^^^ + +This module enables populating content at configure time via any method +supported by the :module:`ExternalProject` module. Whereas +:command:`ExternalProject_Add` downloads at build time, the +``FetchContent`` module makes content available immediately, allowing the +configure step to use the content in commands like :command:`add_subdirectory`, +:command:`include` or :command:`file` operations. + +Content population details would normally be defined separately from the +command that performs the actual population. Projects should also +check whether the content has already been populated somewhere else in the +project hierarchy. Typical usage would look something like this: + +.. code-block:: cmake + + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.0 + ) + + FetchContent_GetProperties(googletest) + if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) + endif() + +When using the above pattern with a hierarchical project arrangement, +projects at higher levels in the hierarchy are able to define or override +the population details of content specified anywhere lower in the project +hierarchy. The ability to detect whether content has already been +populated ensures that even if multiple child projects want certain content +to be available, the first one to populate it wins. The other child project +can simply make use of the already available content instead of repeating +the population for itself. See the +:ref:`Examples ` section which demonstrates +this scenario. + +The ``FetchContent`` module also supports defining and populating +content in a single call, with no check for whether the content has been +populated elsewhere in the project already. This is a more low level +operation and would not normally be the way the module is used, but it is +sometimes useful as part of implementing some higher level feature or to +populate some content in CMake's script mode. + + +Declaring Content Details +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. command:: FetchContent_Declare + + .. code-block:: cmake + + FetchContent_Declare( ...) + + The ``FetchContent_Declare()`` function records the options that describe + how to populate the specified content, but if such details have already + been recorded earlier in this project (regardless of where in the project + hierarchy), this and all later calls for the same content ```` are + ignored. This "first to record, wins" approach is what allows hierarchical + projects to have parent projects override content details of child projects. + + The content ```` can be any string without spaces, but good practice + would be to use only letters, numbers and underscores. The name will be + treated case-insensitively and it should be obvious for the content it + represents, often being the name of the child project or the value given + to its top level :command:`project` command (if it is a CMake project). + For well-known public projects, the name should generally be the official + name of the project. Choosing an unusual name makes it unlikely that other + projects needing that same content will use the same name, leading to + the content being populated multiple times. + + The ```` can be any of the download or update/patch options + that the :command:`ExternalProject_Add` command understands. The configure, + build, install and test steps are explicitly disabled and therefore options + related to them will be ignored. In most cases, ```` will + just be a couple of options defining the download method and method-specific + details like a commit tag or archive hash. For example: + + .. code-block:: cmake + + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.0 + ) + + FetchContent_Declare( + myCompanyIcons + URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz + URL_HASH 5588a7b18261c20068beabfb4f530b87 + ) + + FetchContent_Declare( + myCompanyCertificates + SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs + SVN_REVISION -r12345 + ) + +Populating The Content +^^^^^^^^^^^^^^^^^^^^^^ + +.. command:: FetchContent_Populate + + .. code-block:: cmake + + FetchContent_Populate( ) + + In most cases, the only argument given to ``FetchContent_Populate()`` is the + ````. When used this way, the command assumes the content details have + been recorded by an earlier call to :command:`FetchContent_Declare`. The + details are stored in a global property, so they are unaffected by things + like variable or directory scope. Therefore, it doesn't matter where in the + project the details were previously declared, as long as they have been + declared before the call to ``FetchContent_Populate()``. Those saved details + are then used to construct a call to :command:`ExternalProject_Add` in a + private sub-build to perform the content population immediately. The + implementation of ``ExternalProject_Add()`` ensures that if the content has + already been populated in a previous CMake run, that content will be reused + rather than repopulating them again. For the common case where population + involves downloading content, the cost of the download is only paid once. + + An internal global property records when a particular content population + request has been processed. If ``FetchContent_Populate()`` is called more + than once for the same content name within a configure run, the second call + will halt with an error. Projects can and should check whether content + population has already been processed with the + :command:`FetchContent_GetProperties` command before calling + ``FetchContent_Populate()``. + + ``FetchContent_Populate()`` will set three variables in the scope of the + caller; ``_POPULATED``, ``_SOURCE_DIR`` and + ``_BINARY_DIR``, where ```` is the lowercased ````. + ``_POPULATED`` will always be set to ``True`` by the call. + ``_SOURCE_DIR`` is the location where the + content can be found upon return (it will have already been populated), while + ``_BINARY_DIR`` is a directory intended for use as a corresponding + build directory. The main use case for the two directory variables is to + call :command:`add_subdirectory` immediately after population, i.e.: + + .. code-block:: cmake + + FetchContent_Populate(FooBar ...) + add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR}) + + The values of the three variables can also be retrieved from anywhere in the + project hierarchy using the :command:`FetchContent_GetProperties` command. + + A number of cache variables influence the behavior of all content population + performed using details saved from a :command:`FetchContent_Declare` call: + + ``FETCHCONTENT_BASE_DIR`` + In most cases, the saved details do not specify any options relating to the + directories to use for the internal sub-build, final source and build areas. + It is generally best to leave these decisions up to the ``FetchContent`` + module to handle on the project's behalf. The ``FETCHCONTENT_BASE_DIR`` + cache variable controls the point under which all content population + directories are collected, but in most cases developers would not need to + change this. The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if + developers change this value, they should aim to keep the path short and + just below the top level of the build tree to avoid running into path + length problems on Windows. + + ``FETCHCONTENT_QUIET`` + The logging output during population can be quite verbose, making the + configure stage quite noisy. This cache option (``ON`` by default) hides + all population output unless an error is encountered. If experiencing + problems with hung downloads, temporarily switching this option off may + help diagnose which content population is causing the issue. + + ``FETCHCONTENT_FULLY_DISCONNECTED`` + When this option is enabled, no attempt is made to download or update + any content. It is assumed that all content has already been populated in + a previous run or the source directories have been pointed at existing + contents the developer has provided manually (using options described + further below). When the developer knows that no changes have been made to + any content details, turning this option ``ON`` can significantly speed up + the configure stage. It is ``OFF`` by default. + + ``FETCHCONTENT_UPDATES_DISCONNECTED`` + This is a less severe download/update control compared to + ``FETCHCONTENT_FULLY_DISCONNECTED``. Instead of bypassing all download and + update logic, the ``FETCHCONTENT_UPDATES_DISCONNECTED`` only disables the + update stage. Therefore, if content has not been downloaded previously, + it will still be downloaded when this option is enabled. This can speed up + the configure stage, but not as much as + ``FETCHCONTENT_FULLY_DISCONNECTED``. It is ``OFF`` by default. + + In addition to the above cache variables, the following cache variables are + also defined for each content name (```` is the uppercased value of + ````): + + ``FETCHCONTENT_SOURCE_DIR_`` + If this is set, no download or update steps are performed for the specified + content and the ``_SOURCE_DIR`` variable returned to the caller is + pointed at this location. This gives developers a way to have a separate + checkout of the content that they can modify freely without interference + from the build. The build simply uses that existing source, but it still + defines ``_BINARY_DIR`` to point inside its own build area. + Developers are strongly encouraged to use this mechanism rather than + editing the sources populated in the default location, as changes to + sources in the default location can be lost when content population details + are changed by the project. + + ``FETCHCONTENT_UPDATES_DISCONNECTED_`` + This is the per-content equivalent of + ``FETCHCONTENT_UPDATES_DISCONNECTED``. If the global option or this option + is ``ON``, then updates will be disabled for the named content. + Disabling updates for individual content can be useful for content whose + details rarely change, while still leaving other frequently changing + content with updates enabled. + + + The ``FetchContent_Populate()`` command also supports a syntax allowing the + content details to be specified directly rather than using any saved + details. This is more low-level and use of this form is generally to be + avoided in favour of using saved content details as outlined above. + Nevertheless, in certain situations it can be useful to invoke the content + population as an isolated operation (typically as part of implementing some + other higher level feature or when using CMake in script mode): + + .. code-block:: cmake + + FetchContent_Populate( + [QUIET] + [SUBBUILD_DIR ] + [SOURCE_DIR ] + [BINARY_DIR ] + ... + ) + + This form has a number of key differences to that where only ```` is + provided: + + - All required population details are assumed to have been provided directly + in the call to ``FetchContent_Populate()``. Any saved details for + ```` are ignored. + - No check is made for whether content for ```` has already been + populated. + - No global property is set to record that the population has occurred. + - No global properties record the source or binary directories used for the + populated content. + - The ``FETCHCONTENT_FULLY_DISCONNECTED`` and + ``FETCHCONTENT_UPDATES_DISCONNECTED`` cache variables are ignored. + + The ``_SOURCE_DIR`` and ``_BINARY_DIR`` variables are still + returned to the caller, but since these locations are not stored as global + properties when this form is used, they are only available to the calling + scope and below rather than the entire project hierarchy. No + ``_POPULATED`` variable is set in the caller's scope with this form. + + The supported options for ``FetchContent_Populate()`` are the same as those + for :command:`FetchContent_Declare()`. Those few options shown just + above are either specific to ``FetchContent_Populate()`` or their behavior is + slightly modified from how :command:`ExternalProject_Add` treats them. + + ``QUIET`` + The ``QUIET`` option can be given to hide the output associated with + populating the specified content. If the population fails, the output will + be shown regardless of whether this option was given or not so that the + cause of the failure can be diagnosed. The global ``FETCHCONTENT_QUIET`` + cache variable has no effect on ``FetchContent_Populate()`` calls where the + content details are provided directly. + + ``SUBBUILD_DIR`` + The ``SUBBUILD_DIR`` argument can be provided to change the location of the + sub-build created to perform the population. The default value is + ``${CMAKE_CURRENT_BINARY_DIR}/-subbuild`` and it would be unusual + to need to override this default. If a relative path is specified, it will + be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`. + + ``SOURCE_DIR``, ``BINARY_DIR`` + The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by + :command:`ExternalProject_Add`, but different default values are used by + ``FetchContent_Populate()``. ``SOURCE_DIR`` defaults to + ``${CMAKE_CURRENT_BINARY_DIR}/-src`` and ``BINARY_DIR`` defaults to + ``${CMAKE_CURRENT_BINARY_DIR}/-build``. If a relative path is + specified, it will be interpreted as relative to + :variable:`CMAKE_CURRENT_BINARY_DIR`. + + In addition to the above explicit options, any other unrecognized options are + passed through unmodified to :command:`ExternalProject_Add` to perform the + download, patch and update steps. The following options are explicitly + prohibited (they are disabled by the ``FetchContent_Populate()`` command): + + - ``CONFIGURE_COMMAND`` + - ``BUILD_COMMAND`` + - ``INSTALL_COMMAND`` + - ``TEST_COMMAND`` + + If using ``FetchContent_Populate()`` within CMake's script mode, be aware + that the implementation sets up a sub-build which therefore requires a CMake + generator and build tool to be available. If these cannot be found by + default, then the :variable:`CMAKE_GENERATOR` and/or + :variable:`CMAKE_MAKE_PROGRAM` variables will need to be set appropriately + on the command line invoking the script. + + +Retrieve Population Properties +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. command:: FetchContent_GetProperties + + When using saved content details, a call to :command:`FetchContent_Populate` + records information in global properties which can be queried at any time. + This information includes the source and binary directories associated with + the content and also whether or not the content population has been processed + during the current configure run. + + .. code-block:: cmake + + FetchContent_GetProperties( + [SOURCE_DIR ] + [BINARY_DIR ] + [POPULATED ] + ) + + The ``SOURCE_DIR``, ``BINARY_DIR`` and ``POPULATED`` options can be used to + specify which properties should be retrieved. Each option accepts a value + which is the name of the variable in which to store that property. Most of + the time though, only ```` is given, in which case the call will then + set the same variables as a call to + :command:`FetchContent_Populate(name) `. This allows + the following canonical pattern to be used, which ensures that the relevant + variables will always be defined regardless of whether or not the population + has been performed elsewhere in the project already: + + .. code-block:: cmake + + FetchContent_GetProperties(foobar) + if(NOT foobar_POPULATED) + FetchContent_Populate(foobar) + + # Set any custom variables, etc. here, then + # populate the content as part of this build + + add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR}) + endif() + + The above pattern allows other parts of the overall project hierarchy to + re-use the same content and ensure that it is only populated once. + + +.. _`fetch-content-examples`: + +Examples +^^^^^^^^ + +Consider a project hierarchy where ``projA`` is the top level project and it +depends on projects ``projB`` and ``projC``. Both ``projB`` and ``projC`` +can be built standalone and they also both depend on another project +``projD``. For simplicity, this example will assume that all four projects +are available on a company git server. The ``CMakeLists.txt`` of each project +might have sections like the following: + +*projA*: + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + projB + GIT_REPOSITORY git@mycompany.com/git/projB.git + GIT_TAG 4a89dc7e24ff212a7b5167bef7ab079d + ) + FetchContent_Declare( + projC + GIT_REPOSITORY git@mycompany.com/git/projC.git + GIT_TAG 4ad4016bd1d8d5412d135cf8ceea1bb9 + ) + FetchContent_Declare( + projD + GIT_REPOSITORY git@mycompany.com/git/projD.git + GIT_TAG origin/integrationBranch + ) + + FetchContent_GetProperties(projB) + if(NOT projb_POPULATED) + FetchContent_Populate(projB) + add_subdirectory(${projb_SOURCE_DIR} ${projb_BINARY_DIR}) + endif() + + FetchContent_GetProperties(projC) + if(NOT projc_POPULATED) + FetchContent_Populate(projC) + add_subdirectory(${projc_SOURCE_DIR} ${projc_BINARY_DIR}) + endif() + +*projB*: + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + projD + GIT_REPOSITORY git@mycompany.com/git/projD.git + GIT_TAG 20b415f9034bbd2a2e8216e9a5c9e632 + ) + + FetchContent_GetProperties(projD) + if(NOT projd_POPULATED) + FetchContent_Populate(projD) + add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR}) + endif() + + +*projC*: + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + projD + GIT_REPOSITORY git@mycompany.com/git/projD.git + GIT_TAG 7d9a17ad2c962aa13e2fbb8043fb6b8a + ) + + FetchContent_GetProperties(projD) + if(NOT projd_POPULATED) + FetchContent_Populate(projD) + add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR}) + endif() + +A few key points should be noted in the above: + +- ``projB`` and ``projC`` define different content details for ``projD``, + but ``projA`` also defines a set of content details for ``projD`` and + because ``projA`` will define them first, the details from ``projB`` and + ``projC`` will not be used. The override details defined by ``projA`` + are not required to match either of those from ``projB`` or ``projC``, but + it is up to the higher level project to ensure that the details it does + define still make sense for the child projects. +- While ``projA`` defined content details for ``projD``, it did not need + to explicitly call ``FetchContent_Populate(projD)`` itself. Instead, it + leaves that to a child project to do (in this case it will be ``projB`` + since it is added to the build ahead of ``projC``). If ``projA`` needed to + customize how the ``projD`` content was brought into the build as well + (e.g. define some CMake variables before calling + :command:`add_subdirectory` after populating), it would do the call to + ``FetchContent_Populate()``, etc. just as it did for the ``projB`` and + ``projC`` content. For higher level projects, it is usually enough to + just define the override content details and leave the actual population + to the child projects. This saves repeating the same thing at each level + of the project hierarchy unnecessarily. +- Even though ``projA`` is the top level project in this example, it still + checks whether ``projB`` and ``projC`` have already been populated before + going ahead to do those populations. This makes ``projA`` able to be more + easily incorporated as a child of some other higher level project in the + future if required. Always protect a call to + :command:`FetchContent_Populate` with a check to + :command:`FetchContent_GetProperties`, even in what may be considered a top + level project at the time. + + +The following example demonstrates how one might download and unpack a +firmware tarball using CMake's :manual:`script mode `. The call to +:command:`FetchContent_Populate` specifies all the content details and the +unpacked firmware will be placed in a ``firmware`` directory below the +current working directory. + +*getFirmware.cmake*: + +.. code-block:: cmake + + # NOTE: Intended to be run in script mode with cmake -P + include(FetchContent) + FetchContent_Populate( + firmware + URL https://mycompany.com/assets/firmware-1.23-arm.tar.gz + URL_HASH MD5=68247684da89b608d466253762b0ff11 + SOURCE_DIR firmware + ) + +#]=======================================================================] + + +set(__FetchContent_privateDir "${CMAKE_CURRENT_LIST_DIR}/FetchContent") + +#======================================================================= +# Recording and retrieving content details for later population +#======================================================================= + +# Internal use, projects must not call this directly. It is +# intended for use by FetchContent_Declare() only. +# +# Sets a content-specific global property (not meant for use +# outside of functions defined here in this file) which can later +# be retrieved using __FetchContent_getSavedDetails() with just the +# same content name. If there is already a value stored in the +# property, it is left unchanged and this call has no effect. +# This allows parent projects to define the content details, +# overriding anything a child project may try to set (properties +# are not cached between runs, so the first thing to set it in a +# build will be in control). +function(__FetchContent_declareDetails contentName) + + string(TOLOWER ${contentName} contentNameLower) + set(propertyName "_FetchContent_${contentNameLower}_savedDetails") + get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED) + if(NOT alreadyDefined) + define_property(GLOBAL PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} ${ARGN}) + endif() + +endfunction() + + +# Internal use, projects must not call this directly. It is +# intended for use by the FetchContent_Declare() function. +# +# Retrieves details saved for the specified content in an +# earlier call to __FetchContent_declareDetails(). +function(__FetchContent_getSavedDetails contentName outVar) + + string(TOLOWER ${contentName} contentNameLower) + set(propertyName "_FetchContent_${contentNameLower}_savedDetails") + get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED) + if(NOT alreadyDefined) + message(FATAL_ERROR "No content details recorded for ${contentName}") + endif() + get_property(propertyValue GLOBAL PROPERTY ${propertyName}) + set(${outVar} "${propertyValue}" PARENT_SCOPE) + +endfunction() + + +# Saves population details of the content, sets defaults for the +# SOURCE_DIR and BUILD_DIR. +function(FetchContent_Declare contentName) + + set(options "") + set(oneValueArgs SVN_REPOSITORY) + set(multiValueArgs "") + + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + unset(srcDirSuffix) + unset(svnRepoArgs) + if(ARG_SVN_REPOSITORY) + # Add a hash of the svn repository URL to the source dir. This works + # around the problem where if the URL changes, the download would + # fail because it tries to checkout/update rather than switch the + # old URL to the new one. We limit the hash to the first 7 characters + # so that the source path doesn't get overly long (which can be a + # problem on windows due to path length limits). + string(SHA1 urlSHA ${ARG_SVN_REPOSITORY}) + string(SUBSTRING ${urlSHA} 0 7 urlSHA) + set(srcDirSuffix "-${urlSHA}") + set(svnRepoArgs SVN_REPOSITORY ${ARG_SVN_REPOSITORY}) + endif() + + string(TOLOWER ${contentName} contentNameLower) + __FetchContent_declareDetails( + ${contentNameLower} + SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src${srcDirSuffix}" + BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build" + ${svnRepoArgs} + # List these last so they can override things we set above + ${ARG_UNPARSED_ARGUMENTS} + ) + +endfunction() + + +#======================================================================= +# Set/get whether the specified content has been populated yet. +# The setter also records the source and binary dirs used. +#======================================================================= + +# Internal use, projects must not call this directly. It is +# intended for use by the FetchContent_Populate() function to +# record when FetchContent_Populate() is called for a particular +# content name. +function(__FetchContent_setPopulated contentName sourceDir binaryDir) + + string(TOLOWER ${contentName} contentNameLower) + set(prefix "_FetchContent_${contentNameLower}") + + set(propertyName "${prefix}_sourceDir") + define_property(GLOBAL PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} ${sourceDir}) + + set(propertyName "${prefix}_binaryDir") + define_property(GLOBAL PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} ${binaryDir}) + + set(propertyName "${prefix}_populated") + define_property(GLOBAL PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} True) + +endfunction() + + +# Set variables in the calling scope for any of the retrievable +# properties. If no specific properties are requested, variables +# will be set for all retrievable properties. +# +# This function is intended to also be used by projects as the canonical +# way to detect whether they should call FetchContent_Populate() +# and pull the populated source into the build with add_subdirectory(), +# if they are using the populated content in that way. +function(FetchContent_GetProperties contentName) + + string(TOLOWER ${contentName} contentNameLower) + + set(options "") + set(oneValueArgs SOURCE_DIR BINARY_DIR POPULATED) + set(multiValueArgs "") + + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT ARG_SOURCE_DIR AND + NOT ARG_BINARY_DIR AND + NOT ARG_POPULATED) + # No specific properties requested, provide them all + set(ARG_SOURCE_DIR ${contentNameLower}_SOURCE_DIR) + set(ARG_BINARY_DIR ${contentNameLower}_BINARY_DIR) + set(ARG_POPULATED ${contentNameLower}_POPULATED) + endif() + + set(prefix "_FetchContent_${contentNameLower}") + + if(ARG_SOURCE_DIR) + set(propertyName "${prefix}_sourceDir") + get_property(value GLOBAL PROPERTY ${propertyName}) + if(value) + set(${ARG_SOURCE_DIR} ${value} PARENT_SCOPE) + endif() + endif() + + if(ARG_BINARY_DIR) + set(propertyName "${prefix}_binaryDir") + get_property(value GLOBAL PROPERTY ${propertyName}) + if(value) + set(${ARG_BINARY_DIR} ${value} PARENT_SCOPE) + endif() + endif() + + if(ARG_POPULATED) + set(propertyName "${prefix}_populated") + get_property(value GLOBAL PROPERTY ${propertyName} DEFINED) + set(${ARG_POPULATED} ${value} PARENT_SCOPE) + endif() + +endfunction() + + +#======================================================================= +# Performing the population +#======================================================================= + +# The value of contentName will always have been lowercased by the caller. +# All other arguments are assumed to be options that are understood by +# ExternalProject_Add(), except for QUIET and SUBBUILD_DIR. +function(__FetchContent_directPopulate contentName) + + set(options + QUIET + ) + set(oneValueArgs + SUBBUILD_DIR + SOURCE_DIR + BINARY_DIR + # Prevent the following from being passed through + CONFIGURE_COMMAND + BUILD_COMMAND + INSTALL_COMMAND + TEST_COMMAND + ) + set(multiValueArgs "") + + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT ARG_SUBBUILD_DIR) + message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set") + elseif(NOT IS_ABSOLUTE "${ARG_SUBBUILD_DIR}") + set(ARG_SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBBUILD_DIR}") + endif() + + if(NOT ARG_SOURCE_DIR) + message(FATAL_ERROR "Internal error: SOURCE_DIR not set") + elseif(NOT IS_ABSOLUTE "${ARG_SOURCE_DIR}") + set(ARG_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SOURCE_DIR}") + endif() + + if(NOT ARG_BINARY_DIR) + message(FATAL_ERROR "Internal error: BINARY_DIR not set") + elseif(NOT IS_ABSOLUTE "${ARG_BINARY_DIR}") + set(ARG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_BINARY_DIR}") + endif() + + # Ensure the caller can know where to find the source and build directories + # with some convenient variables. Doing this here ensures the caller sees + # the correct result in the case where the default values are overridden by + # the content details set by the project. + set(${contentName}_SOURCE_DIR "${ARG_SOURCE_DIR}" PARENT_SCOPE) + set(${contentName}_BINARY_DIR "${ARG_BINARY_DIR}" PARENT_SCOPE) + + # The unparsed arguments may contain spaces, so build up ARG_EXTRA + # in such a way that it correctly substitutes into the generated + # CMakeLists.txt file with each argument quoted. + unset(ARG_EXTRA) + foreach(arg IN LISTS ARG_UNPARSED_ARGUMENTS) + set(ARG_EXTRA "${ARG_EXTRA} \"${arg}\"") + endforeach() + + # Hide output if requested, but save it to a variable in case there's an + # error so we can show the output upon failure. When not quiet, don't + # capture the output to a variable because the user may want to see the + # output as it happens (e.g. progress during long downloads). Combine both + # stdout and stderr in the one capture variable so the output stays in order. + if (ARG_QUIET) + set(outputOptions + OUTPUT_VARIABLE capturedOutput + ERROR_VARIABLE capturedOutput + ) + else() + set(capturedOutput) + set(outputOptions) + message(STATUS "Populating ${contentName}") + endif() + + if(CMAKE_GENERATOR) + set(generatorOpts "-G${CMAKE_GENERATOR}") + if(CMAKE_GENERATOR_PLATFORM) + list(APPEND generatorOpts "-A${CMAKE_GENERATOR_PLATFORM}") + endif() + if(CMAKE_GENERATOR_TOOLSET) + list(APPEND generatorOpts "-T${CMAKE_GENERATOR_TOOLSET}") + endif() + + if(CMAKE_MAKE_PROGRAM) + list(APPEND generatorOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}") + endif() + + else() + # Likely we've been invoked via CMake's script mode where no + # generator is set (and hence CMAKE_MAKE_PROGRAM could not be + # trusted even if provided). We will have to rely on being + # able to find the default generator and build tool. + unset(generatorOpts) + endif() + + # Create and build a separate CMake project to carry out the population. + # If we've already previously done these steps, they will not cause + # anything to be updated, so extra rebuilds of the project won't occur. + # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project + # has this set to something not findable on the PATH. + configure_file("${__FetchContent_privateDir}/CMakeLists.cmake.in" + "${ARG_SUBBUILD_DIR}/CMakeLists.txt") + execute_process( + COMMAND ${CMAKE_COMMAND} ${generatorOpts} . + RESULT_VARIABLE result + ${outputOptions} + WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" + ) + if(result) + if(capturedOutput) + message("${capturedOutput}") + endif() + message(FATAL_ERROR "CMake step for ${contentName} failed: ${result}") + endif() + execute_process( + COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + ${outputOptions} + WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" + ) + if(result) + if(capturedOutput) + message("${capturedOutput}") + endif() + message(FATAL_ERROR "Build step for ${contentName} failed: ${result}") + endif() + +endfunction() + + +option(FETCHCONTENT_FULLY_DISCONNECTED "Disables all attempts to download or update content and assumes source dirs already exist") +option(FETCHCONTENT_UPDATES_DISCONNECTED "Enables UPDATE_DISCONNECTED behavior for all content population") +option(FETCHCONTENT_QUIET "Enables QUIET option for all content population" ON) +set(FETCHCONTENT_BASE_DIR "${CMAKE_BINARY_DIR}/_deps" CACHE PATH "Directory under which to collect all populated content") + +# Populate the specified content using details stored from +# an earlier call to FetchContent_Declare(). +function(FetchContent_Populate contentName) + + if(NOT contentName) + message(FATAL_ERROR "Empty contentName not allowed for FetchContent_Populate()") + endif() + + string(TOLOWER ${contentName} contentNameLower) + + if(ARGN) + # This is the direct population form with details fully specified + # as part of the call, so we already have everything we need + __FetchContent_directPopulate( + ${contentNameLower} + SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-subbuild" + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-build" + ${ARGN} # Could override any of the above ..._DIR variables + ) + + # Pass source and binary dir variables back to the caller + set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE) + set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE) + + # Don't set global properties, or record that we did this population, since + # this was a direct call outside of the normal declared details form. + # We only want to save values in the global properties for content that + # honours the hierarchical details mechanism so that projects are not + # robbed of the ability to override details set in nested projects. + return() + endif() + + # No details provided, so assume they were saved from an earlier call + # to FetchContent_Declare(). Do a check that we haven't already + # populated this content before in case the caller forgot to check. + FetchContent_GetProperties(${contentName}) + if(${contentNameLower}_POPULATED) + message(FATAL_ERROR "Content ${contentName} already populated in ${${contentNameLower}_SOURCE_DIR}") + endif() + + string(TOUPPER ${contentName} contentNameUpper) + set(FETCHCONTENT_SOURCE_DIR_${contentNameUpper} + "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}" + CACHE PATH "When not empty, overrides where to find pre-populated content for ${contentName}") + + if(FETCHCONTENT_SOURCE_DIR_${contentNameUpper}) + # The source directory has been explicitly provided in the cache, + # so no population is required + set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}") + set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") + + elseif(FETCHCONTENT_FULLY_DISCONNECTED) + # Bypass population and assume source is already there from a previous run + set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src") + set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") + + else() + # Support both a global "disconnect all updates" and a per-content + # update test (either one being set disables updates for this content). + option(FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper} + "Enables UPDATE_DISCONNECTED behavior just for population of ${contentName}") + if(FETCHCONTENT_UPDATES_DISCONNECTED OR + FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper}) + set(disconnectUpdates True) + else() + set(disconnectUpdates False) + endif() + + if(FETCHCONTENT_QUIET) + set(quietFlag QUIET) + else() + unset(quietFlag) + endif() + + __FetchContent_getSavedDetails(${contentName} contentDetails) + if("${contentDetails}" STREQUAL "") + message(FATAL_ERROR "No details have been set for content: ${contentName}") + endif() + + __FetchContent_directPopulate( + ${contentNameLower} + ${quietFlag} + UPDATE_DISCONNECTED ${disconnectUpdates} + SUBBUILD_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-subbuild" + SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src" + BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build" + # Put the saved details last so they can override any of the + # the options we set above (this can include SOURCE_DIR or + # BUILD_DIR) + ${contentDetails} + ) + endif() + + __FetchContent_setPopulated( + ${contentName} + ${${contentNameLower}_SOURCE_DIR} + ${${contentNameLower}_BINARY_DIR} + ) + + # Pass variables back to the caller. The variables passed back here + # must match what FetchContent_GetProperties() sets when it is called + # with just the content name. + set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE) + set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE) + set(${contentNameLower}_POPULATED True PARENT_SCOPE) + +endfunction() diff --git a/cli/fbank/sys/knf/cmake/Modules/FetchContent/CMakeLists.cmake.in b/cli/fbank/sys/knf/cmake/Modules/FetchContent/CMakeLists.cmake.in new file mode 100644 index 0000000..9a7a771 --- /dev/null +++ b/cli/fbank/sys/knf/cmake/Modules/FetchContent/CMakeLists.cmake.in @@ -0,0 +1,21 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION ${CMAKE_VERSION}) + +# We name the project and the target for the ExternalProject_Add() call +# to something that will highlight to the user what we are working on if +# something goes wrong and an error message is produced. + +project(${contentName}-populate NONE) + +include(ExternalProject) +ExternalProject_Add(${contentName}-populate + ${ARG_EXTRA} + SOURCE_DIR "${ARG_SOURCE_DIR}" + BINARY_DIR "${ARG_BINARY_DIR}" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/cli/fbank/sys/knf/cmake/Modules/README.md b/cli/fbank/sys/knf/cmake/Modules/README.md new file mode 100644 index 0000000..c8d275f --- /dev/null +++ b/cli/fbank/sys/knf/cmake/Modules/README.md @@ -0,0 +1,5 @@ + +## FetchContent + +`FetchContent.cmake` and `FetchContent/CMakeLists.cmake.in` +are copied from `cmake/3.11.0/share/cmake-3.11/Modules`. diff --git a/cli/fbank/sys/knf/cmake/__init__.py b/cli/fbank/sys/knf/cmake/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/fbank/sys/knf/cmake/cmake_extension.py b/cli/fbank/sys/knf/cmake/cmake_extension.py new file mode 100644 index 0000000..f3ca874 --- /dev/null +++ b/cli/fbank/sys/knf/cmake/cmake_extension.py @@ -0,0 +1,120 @@ +# Copyright (c) 2021 Xiaomi Corporation (author: Fangjun Kuang) + +import glob +import os +import platform +import shutil +import sys +from pathlib import Path + +import setuptools +from setuptools.command.build_ext import build_ext + + +def is_for_pypi(): + ans = os.environ.get("KALDI_NATIVE_FBANK_IS_FOR_PYPI", None) + return ans is not None + + +def is_macos(): + return platform.system() == "Darwin" + + +def is_windows(): + return platform.system() == "Windows" + + +try: + from wheel.bdist_wheel import bdist_wheel as _bdist_wheel + + class bdist_wheel(_bdist_wheel): + def finalize_options(self): + _bdist_wheel.finalize_options(self) + # In this case, the generated wheel has a name in the form + # kaldifeat-xxx-pyxx-none-any.whl + if is_for_pypi() and not is_macos(): + self.root_is_pure = True + else: + # The generated wheel has a name ending with + # -linux_x86_64.whl + self.root_is_pure = False + + +except ImportError: + bdist_wheel = None + + +def cmake_extension(name, *args, **kwargs) -> setuptools.Extension: + kwargs["language"] = "c++" + sources = [] + return setuptools.Extension(name, sources, *args, **kwargs) + + +class BuildExtension(build_ext): + def build_extension(self, ext: setuptools.extension.Extension): + # build/temp.linux-x86_64-3.8 + os.makedirs(self.build_temp, exist_ok=True) + + # build/lib.linux-x86_64-3.8 + os.makedirs(self.build_lib, exist_ok=True) + + install_dir = Path(self.build_lib).resolve() / "kaldi_native_fbank" + + kaldi_native_fbank_dir = Path(__file__).parent.parent.resolve() + + cmake_args = os.environ.get("KALDI_NATIVE_FBANK_CMAKE_ARGS", "") + make_args = os.environ.get("KALDI_NATIVE_FBANK_MAKE_ARGS", "") + system_make_args = os.environ.get("MAKEFLAGS", "") + + if cmake_args == "": + cmake_args = "-DCMAKE_BUILD_TYPE=Release" + + extra_cmake_args = f" -DCMAKE_INSTALL_PREFIX={install_dir} " + extra_cmake_args += " -DKALDI_NATIVE_FBANK_BUILD_TESTS=OFF " + + if "PYTHON_EXECUTABLE" not in cmake_args: + print(f"Setting PYTHON_EXECUTABLE to {sys.executable}") + cmake_args += f" -DPYTHON_EXECUTABLE={sys.executable}" + + cmake_args += extra_cmake_args + + if is_windows(): + build_cmd = f""" + cmake {cmake_args} -B {self.build_temp} -S {kaldi_native_fbank_dir} + cmake --build {self.build_temp} --target install --config Release -- -m + """ + print(f"build command is:\n{build_cmd}") + ret = os.system( + f"cmake {cmake_args} -B {self.build_temp} -S {kaldi_native_fbank_dir}" + ) + if ret != 0: + raise Exception("Failed to configure kaldi_native_fbank") + + ret = os.system( + f"cmake --build {self.build_temp} --target install --config Release -- -m" + ) + if ret != 0: + raise Exception("Failed to install kaldi_native_fbank") + else: + if make_args == "" and system_make_args == "": + print("For fast compilation, run:") + print( + 'export KALDI_NATIVE_FBANK_MAKE_ARGS="-j"; python setup.py install' + ) + + build_cmd = f""" + cd {self.build_temp} + + cmake {cmake_args} {kaldi_native_fbank_dir} + + make {make_args} install + """ + print(f"build command is:\n{build_cmd}") + + ret = os.system(build_cmd) + if ret != 0: + raise Exception( + "\nBuild kaldi-native-fbank failed. Please check the error message.\n" + "You can ask for help by creating an issue on GitHub.\n" + "\nClick:\n\thttps://github.com/csukuangfj/kaldi-native-fbank/issues/new\n" # noqa + ) diff --git a/cli/fbank/sys/knf/cmake/googletest.cmake b/cli/fbank/sys/knf/cmake/googletest.cmake new file mode 100644 index 0000000..d7816c2 --- /dev/null +++ b/cli/fbank/sys/knf/cmake/googletest.cmake @@ -0,0 +1,79 @@ +function(download_googltest) + if(CMAKE_VERSION VERSION_LESS 3.11) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) + endif() + + include(FetchContent) + + set(googletest_URL "https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz") + set(googletest_URL2 "https://huggingface.co/csukuangfj/k2-cmake-deps/resolve/main/googletest-1.13.0.tar.gz") + set(googletest_HASH "SHA256=ad7fdba11ea011c1d925b3289cf4af2c66a352e18d4c7264392fead75e919363") + + # If you don't have access to the Internet, + # please pre-download googletest + set(possible_file_locations + $ENV{HOME}/Downloads/googletest-1.13.0.tar.gz + ${PROJECT_SOURCE_DIR}/googletest-1.13.0.tar.gz + ${PROJECT_BINARY_DIR}/googletest-1.13.0.tar.gz + /tmp/googletest-1.13.0.tar.gz + /star-fj/fangjun/download/github/googletest-1.13.0.tar.gz + ) + + foreach(f IN LISTS possible_file_locations) + if(EXISTS ${f}) + set(googletest_URL "${f}") + file(TO_CMAKE_PATH "${googletest_URL}" googletest_URL) + set(googletest_URL2) + break() + endif() + endforeach() + + set(BUILD_GMOCK ON CACHE BOOL "" FORCE) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + set(gtest_disable_pthreads ON CACHE BOOL "" FORCE) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + FetchContent_Declare(googletest + URL + ${googletest_URL} + ${googletest_URL2} + URL_HASH ${googletest_HASH} + ) + + FetchContent_GetProperties(googletest) + if(NOT googletest_POPULATED) + message(STATUS "Downloading googletest from ${googletest_URL}") + FetchContent_Populate(googletest) + endif() + message(STATUS "googletest is downloaded to ${googletest_SOURCE_DIR}") + message(STATUS "googletest's binary dir is ${googletest_BINARY_DIR}") + + if(APPLE) + set(CMAKE_MACOSX_RPATH ON) # to solve the following warning on macOS + endif() + #[==[ + -- Generating done + Policy CMP0042 is not set: MACOSX_RPATH is enabled by default. Run "cmake + --help-policy CMP0042" for policy details. Use the cmake_policy command to + set the policy and suppress this warning. + + MACOSX_RPATH is not specified for the following targets: + + gmock + gmock_main + gtest + gtest_main + + This warning is for project developers. Use -Wno-dev to suppress it. + ]==] + + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL) + + target_include_directories(gtest + INTERFACE + ${googletest_SOURCE_DIR}/googletest/include + ${googletest_SOURCE_DIR}/googlemock/include + ) +endfunction() + +download_googltest() diff --git a/cli/fbank/sys/knf/cmake/pybind11.cmake b/cli/fbank/sys/knf/cmake/pybind11.cmake new file mode 100644 index 0000000..c708b5f --- /dev/null +++ b/cli/fbank/sys/knf/cmake/pybind11.cmake @@ -0,0 +1,47 @@ +function(download_pybind11) + if(CMAKE_VERSION VERSION_LESS 3.11) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) + endif() + + include(FetchContent) + + set(pybind11_URL "https://github.com/pybind/pybind11/archive/refs/tags/v2.10.2.tar.gz") + set(pybind11_URL2 "https://huggingface.co/csukuangfj/sherpa-onnx-cmake-deps/resolve/main/pybind11-2.10.2.tar.gz") + set(pybind11_HASH "SHA256=93bd1e625e43e03028a3ea7389bba5d3f9f2596abc074b068e70f4ef9b1314ae") + + # If you don't have access to the Internet, + # please pre-download pybind11 + set(possible_file_locations + $ENV{HOME}/Downloads/pybind11-2.10.2.tar.gz + ${PROJECT_SOURCE_DIR}/pybind11-2.10.2.tar.gz + ${PROJECT_BINARY_DIR}/pybind11-2.10.2.tar.gz + /tmp/pybind11-2.10.2.tar.gz + /star-fj/fangjun/download/github/pybind11-2.10.2.tar.gz + ) + + foreach(f IN LISTS possible_file_locations) + if(EXISTS ${f}) + set(pybind11_URL "${f}") + file(TO_CMAKE_PATH "${pybind11_URL}" pybind11_URL) + set(pybind11_URL2) + break() + endif() + endforeach() + + FetchContent_Declare(pybind11 + URL + ${pybind11_URL} + ${pybind11_URL2} + URL_HASH ${pybind11_HASH} + ) + + FetchContent_GetProperties(pybind11) + if(NOT pybind11_POPULATED) + message(STATUS "Downloading pybind11 from ${pybind11_URL}") + FetchContent_Populate(pybind11) + endif() + message(STATUS "pybind11 is downloaded to ${pybind11_SOURCE_DIR}") + add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR} EXCLUDE_FROM_ALL) +endfunction() + +download_pybind11() diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/CMakeLists.txt b/cli/fbank/sys/knf/kaldi-native-fbank/CMakeLists.txt new file mode 100644 index 0000000..2037626 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/CMakeLists.txt @@ -0,0 +1,8 @@ +add_subdirectory(csrc) + +if(KALDI_NATIVE_FBANK_BUILD_PYTHON) + message(STATUS "Building Python") + add_subdirectory(python) +else() + message(STATUS "Disable building Python") +endif() diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/CMakeLists.txt b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/CMakeLists.txt new file mode 100644 index 0000000..69b7da4 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/CMakeLists.txt @@ -0,0 +1,101 @@ + +include_directories(${PROJECT_SOURCE_DIR}) +set(sources + feature-fbank.cc + feature-functions.cc + feature-mfcc.cc + feature-window.cc + fftsg.cc + kaldi-math.cc + mel-computations.cc + online-feature.cc + rfft.cc + whisper-feature.cc +) + +if(KALDI_NATIVE_FBANK_ENABLE_CHECK) + list(APPEND sources log.cc) +endif() + +add_library(kaldi-native-fbank-core ${sources}) +if(KALDI_NATIVE_FBANK_ENABLE_CHECK) + target_compile_definitions(kaldi-native-fbank-core PUBLIC KNF_ENABLE_CHECK=1) + + if(KNF_HAVE_EXECINFO_H) + target_compile_definitions(kaldi-native-fbank-core PRIVATE KNF_HAVE_EXECINFO_H=1) + endif() + + if(KNF_HAVE_CXXABI_H) + target_compile_definitions(kaldi-native-fbank-core PRIVATE KNF_HAVE_CXXABI_H=1) + endif() +endif() + +# We are using std::call_once() in log.h,which requires us to link with -pthread +if(NOT WIN32 AND KALDI_NATIVE_FBANK_ENABLE_CHECK) + target_link_libraries(kaldi-native-fbank-core -pthread) +endif() + +if(KALDI_NATIVE_FBANK_BUILD_TESTS) + add_executable(test-online-fbank test-online-fbank.cc) + target_link_libraries(test-online-fbank kaldi-native-fbank-core) +endif() + +function(kaldi_native_fbank_add_test source) + get_filename_component(name ${source} NAME_WE) + add_executable(${name} "${source}") + target_link_libraries(${name} + PRIVATE + kaldi-native-fbank-core + gtest + gtest_main + ) + + add_test(NAME "Test.${name}" + COMMAND + $ + ) +endfunction() + +# please sort the source files alphabetically +set(test_srcs + # test-online-feature.cc + test-log.cc + test-rfft.cc +) + +if(KALDI_NATIVE_FBANK_BUILD_TESTS) + foreach(source IN LISTS test_srcs) + kaldi_native_fbank_add_test(${source}) + endforeach() +endif() + +install(TARGETS kaldi-native-fbank-core + DESTINATION lib +) +if(KALDI_NATIVE_FBANK_BUILD_PYTHON AND WIN32) + install(TARGETS kaldi-native-fbank-core + DESTINATION .. + ) +endif() + +if(KALDI_NATIVE_FBANK_BUILD_TESTS) + install(TARGETS test-online-fbank + DESTINATION bin + ) +endif() + +file(MAKE_DIRECTORY + DESTINATION + ${PROJECT_BINARY_DIR}/include/kaldi-native-fbank/csrc +) +file(GLOB_RECURSE all_headers *.h) + +file(COPY + ${all_headers} + DESTINATION + ${PROJECT_BINARY_DIR}/include/kaldi-native-fbank/csrc +) + +install(FILES ${all_headers} + DESTINATION include/kaldi-native-fbank/csrc +) diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/CPPLINT.cfg b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/CPPLINT.cfg new file mode 100644 index 0000000..4c1a8fb --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/CPPLINT.cfg @@ -0,0 +1 @@ +exclude_files=fftsg.cc diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-fbank.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-fbank.cc new file mode 100644 index 0000000..b73aa80 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-fbank.cc @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is copied/modified from kaldi/src/feat/feature-fbank.cc +// +#include "kaldi-native-fbank/csrc/feature-fbank.h" + +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-functions.h" +#include "kaldi-native-fbank/csrc/kaldi-math.h" +#include "kaldi-native-fbank/csrc/log.h" + +namespace knf { + +std::ostream &operator<<(std::ostream &os, const FbankOptions &opts) { + os << opts.ToString(); + return os; +} + +FbankComputer::FbankComputer(const FbankOptions &opts) + : opts_(opts), rfft_(opts.frame_opts.PaddedWindowSize()) { + if (opts.energy_floor > 0.0f) { + log_energy_floor_ = logf(opts.energy_floor); + } + + // We'll definitely need the filterbanks info for VTLN warping factor 1.0. + // [note: this call caches it.] + GetMelBanks(1.0f); +} + +FbankComputer::~FbankComputer() { + for (auto iter = mel_banks_.begin(); iter != mel_banks_.end(); ++iter) + delete iter->second; +} + +const MelBanks *FbankComputer::GetMelBanks(float vtln_warp) { + MelBanks *this_mel_banks = nullptr; + + // std::map::iterator iter = mel_banks_.find(vtln_warp); + auto iter = mel_banks_.find(vtln_warp); + if (iter == mel_banks_.end()) { + this_mel_banks = new MelBanks(opts_.mel_opts, opts_.frame_opts, vtln_warp); + mel_banks_[vtln_warp] = this_mel_banks; + } else { + this_mel_banks = iter->second; + } + return this_mel_banks; +} + +void FbankComputer::Compute(float signal_raw_log_energy, float vtln_warp, + std::vector *signal_frame, float *feature) { + const MelBanks &mel_banks = *(GetMelBanks(vtln_warp)); + + KNF_CHECK_EQ(signal_frame->size(), opts_.frame_opts.PaddedWindowSize()); + + // Compute energy after window function (not the raw one). + if (opts_.use_energy && !opts_.raw_energy) { + signal_raw_log_energy = std::log( + std::max(InnerProduct(signal_frame->data(), signal_frame->data(), + signal_frame->size()), + std::numeric_limits::epsilon())); + } + rfft_.Compute(signal_frame->data()); // signal_frame is modified in-place + ComputePowerSpectrum(signal_frame); + + // Use magnitude instead of power if requested. + if (!opts_.use_power) { + Sqrt(signal_frame->data(), signal_frame->size() / 2 + 1); + } + + int32_t mel_offset = ((opts_.use_energy && !opts_.htk_compat) ? 1 : 0); + + // Its length is opts_.mel_opts.num_bins + float *mel_energies = feature + mel_offset; + + // Sum with mel filter banks over the power spectrum + mel_banks.Compute(signal_frame->data(), mel_energies); + + if (opts_.use_log_fbank) { + // Avoid log of zero (which should be prevented anyway by dithering). + for (int32_t i = 0; i != opts_.mel_opts.num_bins; ++i) { + auto t = std::max(mel_energies[i], std::numeric_limits::epsilon()); + mel_energies[i] = std::log(t); + } + } + + // Copy energy as first value (or the last, if htk_compat == true). + if (opts_.use_energy) { + if (opts_.energy_floor > 0.0 && signal_raw_log_energy < log_energy_floor_) { + signal_raw_log_energy = log_energy_floor_; + } + int32_t energy_index = opts_.htk_compat ? opts_.mel_opts.num_bins : 0; + feature[energy_index] = signal_raw_log_energy; + } +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-fbank.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-fbank.h new file mode 100644 index 0000000..84005e0 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-fbank.h @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is copied/modified from kaldi/src/feat/feature-fbank.h + +#ifndef KALDI_NATIVE_FBANK_CSRC_FEATURE_FBANK_H_ +#define KALDI_NATIVE_FBANK_CSRC_FEATURE_FBANK_H_ + +#include +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" +#include "kaldi-native-fbank/csrc/rfft.h" + +namespace knf { + +struct FbankOptions { + FrameExtractionOptions frame_opts; + MelBanksOptions mel_opts; + // append an extra dimension with energy to the filter banks + bool use_energy = false; + float energy_floor = 0.0f; // active iff use_energy==true + + // If true, compute log_energy before preemphasis and windowing + // If false, compute log_energy after preemphasis ans windowing + bool raw_energy = true; // active iff use_energy==true + + // If true, put energy last (if using energy) + // If false, put energy first + bool htk_compat = false; // active iff use_energy==true + + // if true (default), produce log-filterbank, else linear + bool use_log_fbank = true; + + // if true (default), use power in filterbank + // analysis, else magnitude. + bool use_power = true; + + FbankOptions() { mel_opts.num_bins = 23; } + + std::string ToString() const { + std::ostringstream os; + os << "frame_opts: \n"; + os << frame_opts << "\n"; + os << "\n"; + + os << "mel_opts: \n"; + os << mel_opts << "\n"; + + os << "use_energy: " << use_energy << "\n"; + os << "energy_floor: " << energy_floor << "\n"; + os << "raw_energy: " << raw_energy << "\n"; + os << "htk_compat: " << htk_compat << "\n"; + os << "use_log_fbank: " << use_log_fbank << "\n"; + os << "use_power: " << use_power << "\n"; + return os.str(); + } +}; + +std::ostream &operator<<(std::ostream &os, const FbankOptions &opts); + +class FbankComputer { + public: + using Options = FbankOptions; + + explicit FbankComputer(const FbankOptions &opts); + ~FbankComputer(); + + int32_t Dim() const { + return opts_.mel_opts.num_bins + (opts_.use_energy ? 1 : 0); + } + + // if true, compute log_energy_pre_window but after dithering and dc removal + bool NeedRawLogEnergy() const { return opts_.use_energy && opts_.raw_energy; } + + const FrameExtractionOptions &GetFrameOptions() const { + return opts_.frame_opts; + } + + const FbankOptions &GetOptions() const { return opts_; } + + /** + Function that computes one frame of features from + one frame of signal. + + @param [in] signal_raw_log_energy The log-energy of the frame of the signal + prior to windowing and pre-emphasis, or + log(numeric_limits::min()), whichever is greater. Must be + ignored by this function if this class returns false from + this->NeedsRawLogEnergy(). + @param [in] vtln_warp The VTLN warping factor that the user wants + to be applied when computing features for this utterance. Will + normally be 1.0, meaning no warping is to be done. The value will + be ignored for feature types that don't support VLTN, such as + spectrogram features. + @param [in] signal_frame One frame of the signal, + as extracted using the function ExtractWindow() using the options + returned by this->GetFrameOptions(). The function will use the + vector as a workspace, which is why it's a non-const pointer. + @param [out] feature Pointer to a vector of size this->Dim(), to which + the computed feature will be written. It should be pre-allocated. + */ + void Compute(float signal_raw_log_energy, float vtln_warp, + std::vector *signal_frame, float *feature); + + private: + const MelBanks *GetMelBanks(float vtln_warp); + + FbankOptions opts_; + float log_energy_floor_; + std::map mel_banks_; // float is VTLN coefficient. + Rfft rfft_; +}; + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_CSRC_FEATURE_FBANK_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-functions.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-functions.cc new file mode 100644 index 0000000..00ae4c7 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-functions.cc @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is copied/modified from kaldi/src/feat/feature-functions.cc + +#include "kaldi-native-fbank/csrc/feature-functions.h" + +#include +#include + +namespace knf { + +void ComputePowerSpectrum(std::vector *complex_fft) { + int32_t dim = complex_fft->size(); + + // now we have in complex_fft, first half of complex spectrum + // it's stored as [real0, realN/2, real1, im1, real2, im2, ...] + + float *p = complex_fft->data(); + int32_t half_dim = dim / 2; + float first_energy = p[0] * p[0]; + float last_energy = p[1] * p[1]; // handle this special case + + for (int32_t i = 1; i < half_dim; ++i) { + float real = p[i * 2]; + float im = p[i * 2 + 1]; + p[i] = real * real + im * im; + } + p[0] = first_energy; + p[half_dim] = last_energy; // Will actually never be used, and anyway + // if the signal has been bandlimited sensibly this should be zero. +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-functions.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-functions.h new file mode 100644 index 0000000..b221622 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-functions.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is copied/modified from kaldi/src/feat/feature-functions.h +#ifndef KALDI_NATIVE_FBANK_CSRC_FEATURE_FUNCTIONS_H_ +#define KALDI_NATIVE_FBANK_CSRC_FEATURE_FUNCTIONS_H_ + +#include +namespace knf { + +// ComputePowerSpectrum converts a complex FFT (as produced by the FFT +// functions in csrc/rfft.h), and converts it into +// a power spectrum. If the complex FFT is a vector of size n (representing +// half of the complex FFT of a real signal of size n, as described there), +// this function computes in the first (n/2) + 1 elements of it, the +// energies of the fft bins from zero to the Nyquist frequency. Contents of the +// remaining (n/2) - 1 elements are undefined at output. + +void ComputePowerSpectrum(std::vector *complex_fft); + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_CSRC_FEATURE_FUNCTIONS_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-mfcc.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-mfcc.cc new file mode 100644 index 0000000..6b75285 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-mfcc.cc @@ -0,0 +1,174 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is copied/modified from kaldi/src/feat/feature-mfcc.cc +// +#include "kaldi-native-fbank/csrc/feature-mfcc.h" + +#include +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-functions.h" +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/kaldi-math.h" +#include "kaldi-native-fbank/csrc/log.h" + +namespace knf { + +static std::vector ComputeDctMatrix(int32_t num_rows, int32_t num_cols) { + // this function is copied from + // https://github.com/kaldi-asr/kaldi/blob/master/src/matrix/matrix-functions.cc#L592 + + std::vector ans(num_rows * num_cols); + float *p = ans.data(); + + float normalizer = std::sqrt(1.0 / num_cols); // normalizer for X_0 + + for (int32_t i = 0; i != num_cols; ++i) { + p[i] = normalizer; + } + + normalizer = std::sqrt(2.0 / num_cols); // normalizer for other elements + + for (int32_t k = 1; k != num_rows; ++k) { + for (int32_t n = 0; n != num_cols; ++n) { + *(p + k * num_cols + n) = + normalizer * + std::cos(static_cast(M_PI) / num_cols * (n + 0.5) * k); + } + } + + return ans; +} + +std::ostream &operator<<(std::ostream &os, const MfccOptions &opts) { + os << opts.ToString(); + return os; +} + +MfccComputer::MfccComputer(const MfccOptions &opts) + : opts_(opts), + rfft_(opts.frame_opts.PaddedWindowSize()), + mel_energies_(opts.mel_opts.num_bins) { + if (opts.energy_floor > 0.0f) { + log_energy_floor_ = logf(opts.energy_floor); + } + + // We'll definitely need the filterbanks info for VTLN warping factor 1.0. + // [note: this call caches it.] + GetMelBanks(1.0f); + + int32_t num_bins = opts.mel_opts.num_bins; + + KNF_CHECK_LE(opts.num_ceps, num_bins) + << "num-ceps cannot be larger than num-mel-bins." + << " It should be smaller or equal. You provided num-ceps: " + << opts.num_ceps << " and num-mel-bins: " << num_bins; + + dct_matrix_ = ComputeDctMatrix(opts.num_ceps, num_bins); + + if (opts.cepstral_lifter != 0.0) { + lifter_coeffs_ = std::vector(opts.num_ceps); + // torch::empty({1, opts.num_ceps}, torch::kFloat32); + ComputeLifterCoeffs(opts.cepstral_lifter, &lifter_coeffs_); + } +} + +MfccComputer::~MfccComputer() { + for (auto iter = mel_banks_.begin(); iter != mel_banks_.end(); ++iter) + delete iter->second; +} + +const MelBanks *MfccComputer::GetMelBanks(float vtln_warp) { + MelBanks *this_mel_banks = nullptr; + + // std::map::iterator iter = mel_banks_.find(vtln_warp); + auto iter = mel_banks_.find(vtln_warp); + if (iter == mel_banks_.end()) { + this_mel_banks = new MelBanks(opts_.mel_opts, opts_.frame_opts, vtln_warp); + mel_banks_[vtln_warp] = this_mel_banks; + } else { + this_mel_banks = iter->second; + } + return this_mel_banks; +} + +void MfccComputer::Compute(float signal_raw_log_energy, float vtln_warp, + std::vector *signal_frame, float *feature) { + const MelBanks &mel_banks = *(GetMelBanks(vtln_warp)); + + KNF_CHECK_EQ(signal_frame->size(), opts_.frame_opts.PaddedWindowSize()); + + // Compute energy after window function (not the raw one). + if (opts_.use_energy && !opts_.raw_energy) { + signal_raw_log_energy = std::log( + std::max(InnerProduct(signal_frame->data(), signal_frame->data(), + signal_frame->size()), + std::numeric_limits::epsilon())); + } + rfft_.Compute(signal_frame->data()); // signal_frame is modified in-place + ComputePowerSpectrum(signal_frame); + + // Sum with mel filter banks over the power spectrum + mel_banks.Compute(signal_frame->data(), mel_energies_.data()); + + // Avoid log of zero (which should be prevented anyway by dithering). + for (int32_t i = 0; i != opts_.mel_opts.num_bins; ++i) { + auto t = std::max(mel_energies_[i], std::numeric_limits::epsilon()); + mel_energies_[i] = std::log(t); + } + + // feature = dct_matrix_ * mel_energies [which now have log] + for (int32_t i = 0; i != opts_.num_ceps; ++i) { + feature[i] = InnerProduct(dct_matrix_.data() + i * opts_.mel_opts.num_bins, + mel_energies_.data(), opts_.mel_opts.num_bins); + } + + if (opts_.cepstral_lifter != 0.0) { + for (int32_t i = 0; i != opts_.num_ceps; ++i) { + feature[i] *= lifter_coeffs_[i]; + } + } + + if (opts_.use_energy) { + if (opts_.energy_floor > 0.0 && signal_raw_log_energy < log_energy_floor_) { + signal_raw_log_energy = log_energy_floor_; + } + feature[0] = signal_raw_log_energy; + } + + if (opts_.htk_compat) { + float energy = feature[0]; + + for (int32_t i = 0; i < opts_.num_ceps - 1; ++i) { + feature[i] = feature[i + 1]; + } + + if (!opts_.use_energy) { + energy *= M_SQRT2; // scale on C0 (actually removing a scale + } + // we previously added that's part of one common definition of + // the cosine transform.) + feature[opts_.num_ceps - 1] = energy; + } +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-mfcc.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-mfcc.h new file mode 100644 index 0000000..32dc772 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-mfcc.h @@ -0,0 +1,151 @@ +/** + * Copyright 2009-2011 Karel Vesely; Petr Motlicek; Saarland University + * 2014-2016 Johns Hopkins University (author: Daniel Povey) + * Copyright 2024 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is copied/modified from kaldi/src/feat/feature-mfcc.h + +#ifndef KALDI_NATIVE_FBANK_CSRC_FEATURE_MFCC_H_ +#define KALDI_NATIVE_FBANK_CSRC_FEATURE_MFCC_H_ + +#include +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" +#include "kaldi-native-fbank/csrc/rfft.h" + +namespace knf { + +/// MfccOptions contains basic options for computing MFCC features. +// (this class is copied from kaldi) +struct MfccOptions { + FrameExtractionOptions frame_opts; + MelBanksOptions mel_opts; + + // Number of cepstra in MFCC computation (including C0) + int32_t num_ceps = 13; + + // Use energy (not C0) in MFCC computation + bool use_energy = true; + + // Floor on energy (absolute, not relative) in MFCC + // computation. Only makes a difference if use_energy=true; + // only necessary if dither=0.0. + // Suggested values: 0.1 or 1.0 + float energy_floor = 0.0; + + // If true, compute energy before preemphasis and windowing + bool raw_energy = true; + + // Constant that controls scaling of MFCCs + float cepstral_lifter = 22.0; + + // If true, put energy or C0 last and use a factor of + // sqrt(2) on C0. + // Warning: not sufficient to get HTK compatible features + // (need to change other parameters) + bool htk_compat = false; + + MfccOptions() { mel_opts.num_bins = 23; } + + std::string ToString() const { + std::ostringstream os; + os << "MfccOptions("; + os << "frame_opts=" << frame_opts.ToString() << ", "; + os << "mel_opts=" << mel_opts.ToString() << ", "; + + os << "num_ceps=" << num_ceps << ", "; + os << "use_energy=" << (use_energy ? "True" : "False") << ", "; + os << "energy_floor=" << energy_floor << ", "; + os << "raw_energy=" << (raw_energy ? "True" : "False") << ", "; + os << "cepstral_lifter=" << cepstral_lifter << ", "; + os << "htk_compat=" << (htk_compat ? "True" : "False") << ")"; + + return os.str(); + } +}; + +std::ostream &operator<<(std::ostream &os, const MfccOptions &opts); + +class MfccComputer { + public: + using Options = MfccOptions; + + explicit MfccComputer(const MfccOptions &opts); + ~MfccComputer(); + + int32_t Dim() const { return opts_.num_ceps; } + + // if true, compute log_energy_pre_window but after dithering and dc removal + bool NeedRawLogEnergy() const { return opts_.use_energy && opts_.raw_energy; } + + const FrameExtractionOptions &GetFrameOptions() const { + return opts_.frame_opts; + } + + const MfccOptions &GetOptions() const { return opts_; } + + /** + Function that computes one frame of features from + one frame of signal. + + @param [in] signal_raw_log_energy The log-energy of the frame of the signal + prior to windowing and pre-emphasis, or + log(numeric_limits::min()), whichever is greater. Must be + ignored by this function if this class returns false from + this->NeedsRawLogEnergy(). + @param [in] vtln_warp The VTLN warping factor that the user wants + to be applied when computing features for this utterance. Will + normally be 1.0, meaning no warping is to be done. The value will + be ignored for feature types that don't support VLTN, such as + spectrogram features. + @param [in] signal_frame One frame of the signal, + as extracted using the function ExtractWindow() using the options + returned by this->GetFrameOptions(). The function will use the + vector as a workspace, which is why it's a non-const pointer. + @param [out] feature Pointer to a vector of size this->Dim(), to which + the computed feature will be written. It should be pre-allocated. + */ + void Compute(float signal_raw_log_energy, float vtln_warp, + std::vector *signal_frame, float *feature); + + private: + const MelBanks *GetMelBanks(float vtln_warp); + + MfccOptions opts_; + float log_energy_floor_; + std::map mel_banks_; // float is VTLN coefficient. + Rfft rfft_; + + // temp buffer of size num_mel_bins = opts.mel_opts.num_bins + std::vector mel_energies_; + + // opts_.num_ceps + std::vector lifter_coeffs_; + + // [num_ceps][num_mel_bins] + std::vector dct_matrix_; +}; + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_CSRC_FEATURE_MFCC_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-window.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-window.cc new file mode 100644 index 0000000..c5d606f --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-window.cc @@ -0,0 +1,257 @@ +// kaldi-native-fbank/csrc/feature-window.cc +// +// Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + +// This file is copied/modified from kaldi/src/feat/feature-window.cc + +#include "kaldi-native-fbank/csrc/feature-window.h" + +#include +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/kaldi-math.h" + +namespace knf { + +std::ostream &operator<<(std::ostream &os, const FrameExtractionOptions &opts) { + os << opts.ToString(); + return os; +} + +FeatureWindowFunction::FeatureWindowFunction(const FrameExtractionOptions &opts) + : window_(opts.WindowSize()) { + int32_t frame_length = opts.WindowSize(); + KNF_CHECK_GT(frame_length, 0); + + float *window_data = window_.data(); + + double a = M_2PI / (frame_length - 1); + if (opts.window_type == "hann") { + // see https://pytorch.org/docs/stable/generated/torch.hann_window.html + // We assume periodic is true + a = M_2PI / frame_length; + } + + for (int32_t i = 0; i < frame_length; i++) { + double i_fl = static_cast(i); + if (opts.window_type == "hanning") { + window_data[i] = 0.5 - 0.5 * cos(a * i_fl); + } else if (opts.window_type == "sine") { + // when you are checking ws wikipedia, please + // note that 0.5 * a = M_PI/(frame_length-1) + window_data[i] = sin(0.5 * a * i_fl); + } else if (opts.window_type == "hamming") { + window_data[i] = 0.54 - 0.46 * cos(a * i_fl); + } else if (opts.window_type == "hann") { + window_data[i] = 0.50 - 0.50 * cos(a * i_fl); + } else if (opts.window_type == "povey") { + // like hamming but goes to zero at edges. + window_data[i] = pow(0.5 - 0.5 * cos(a * i_fl), 0.85); + } else if (opts.window_type == "rectangular") { + window_data[i] = 1.0; + } else if (opts.window_type == "blackman") { + window_data[i] = opts.blackman_coeff - 0.5 * cos(a * i_fl) + + (0.5 - opts.blackman_coeff) * cos(2 * a * i_fl); + } else { + KNF_LOG(FATAL) << "Invalid window type " << opts.window_type; + } + } +} + +void FeatureWindowFunction::Apply(float *wave) const { + int32_t window_size = window_.size(); + const float *p = window_.data(); + for (int32_t k = 0; k != window_size; ++k) { + wave[k] *= p[k]; + } +} + +int64_t FirstSampleOfFrame(int32_t frame, const FrameExtractionOptions &opts) { + int64_t frame_shift = opts.WindowShift(); + if (opts.snip_edges) { + return frame * frame_shift; + } else { + int64_t midpoint_of_frame = frame_shift * frame + frame_shift / 2, + beginning_of_frame = midpoint_of_frame - opts.WindowSize() / 2; + return beginning_of_frame; + } +} + +int32_t NumFrames(int64_t num_samples, const FrameExtractionOptions &opts, + bool flush /*= true*/) { + int64_t frame_shift = opts.WindowShift(); + int64_t frame_length = opts.WindowSize(); + if (opts.snip_edges) { + // with --snip-edges=true (the default), we use a HTK-like approach to + // determining the number of frames-- all frames have to fit completely into + // the waveform, and the first frame begins at sample zero. + if (num_samples < frame_length) + return 0; + else + return (1 + ((num_samples - frame_length) / frame_shift)); + // You can understand the expression above as follows: 'num_samples - + // frame_length' is how much room we have to shift the frame within the + // waveform; 'frame_shift' is how much we shift it each time; and the ratio + // is how many times we can shift it (integer arithmetic rounds down). + } else { + // if --snip-edges=false, the number of frames is determined by rounding the + // (file-length / frame-shift) to the nearest integer. The point of this + // formula is to make the number of frames an obvious and predictable + // function of the frame shift and signal length, which makes many + // segmentation-related questions simpler. + // + // Because integer division in C++ rounds toward zero, we add (half the + // frame-shift minus epsilon) before dividing, to have the effect of + // rounding towards the closest integer. + int32_t num_frames = (num_samples + (frame_shift / 2)) / frame_shift; + + if (flush) return num_frames; + + // note: 'end' always means the last plus one, i.e. one past the last. + int64_t end_sample_of_last_frame = + FirstSampleOfFrame(num_frames - 1, opts) + frame_length; + + // the following code is optimized more for clarity than efficiency. + // If flush == false, we can't output frames that extend past the end + // of the signal. + while (num_frames > 0 && end_sample_of_last_frame > num_samples) { + num_frames--; + end_sample_of_last_frame -= frame_shift; + } + return num_frames; + } +} + +void ExtractWindow(int64_t sample_offset, const std::vector &wave, + int32_t f, const FrameExtractionOptions &opts, + const FeatureWindowFunction &window_function, + std::vector *window, + float *log_energy_pre_window /*= nullptr*/) { + KNF_CHECK(sample_offset >= 0 && wave.size() != 0); + + int32_t frame_length = opts.WindowSize(); + int32_t frame_length_padded = opts.PaddedWindowSize(); + + int64_t num_samples = sample_offset + wave.size(); + int64_t start_sample = FirstSampleOfFrame(f, opts); + int64_t end_sample = start_sample + frame_length; + + if (opts.snip_edges) { + KNF_CHECK(start_sample >= sample_offset && end_sample <= num_samples); + } else { + KNF_CHECK(sample_offset == 0 || start_sample >= sample_offset); + } + + if (window->size() != frame_length_padded) { + window->resize(frame_length_padded); + } + + // wave_start and wave_end are start and end indexes into 'wave', for the + // piece of wave that we're trying to extract. + int32_t wave_start = int32_t(start_sample - sample_offset); + int32_t wave_end = wave_start + frame_length; + + if (wave_start >= 0 && wave_end <= wave.size()) { + // the normal case-- no edge effects to consider. + std::copy(wave.begin() + wave_start, + wave.begin() + wave_start + frame_length, window->data()); + } else { + // Deal with any end effects by reflection, if needed. This code will only + // be reached for about two frames per utterance, so we don't concern + // ourselves excessively with efficiency. + int32_t wave_dim = wave.size(); + for (int32_t s = 0; s < frame_length; ++s) { + int32_t s_in_wave = s + wave_start; + while (s_in_wave < 0 || s_in_wave >= wave_dim) { + // reflect around the beginning or end of the wave. + // e.g. -1 -> 0, -2 -> 1. + // dim -> dim - 1, dim + 1 -> dim - 2. + // the code supports repeated reflections, although this + // would only be needed in pathological cases. + if (s_in_wave < 0) + s_in_wave = -s_in_wave - 1; + else + s_in_wave = 2 * wave_dim - 1 - s_in_wave; + } + (*window)[s] = wave[s_in_wave]; + } + } + + ProcessWindow(opts, window_function, window->data(), log_energy_pre_window); +} + +static void RemoveDcOffset(float *d, int32_t n) { + float sum = 0; + for (int32_t i = 0; i != n; ++i) { + sum += d[i]; + } + + float mean = sum / n; + + for (int32_t i = 0; i != n; ++i) { + d[i] -= mean; + } +} + +float InnerProduct(const float *a, const float *b, int32_t n) { + float sum = 0; + for (int32_t i = 0; i != n; ++i) { + sum += a[i] * b[i]; + } + return sum; +} + +void Dither(float *d, int32_t n, float dither_value) { + if (dither_value == 0.0) { + return; + } + + RandomState rstate; + for (int32_t i = 0; i < n; ++i) { + d[i] += RandGauss(&rstate) * dither_value; + } +} + +static void Preemphasize(float *d, int32_t n, float preemph_coeff) { + if (preemph_coeff == 0.0) { + return; + } + + KNF_CHECK(preemph_coeff >= 0.0 && preemph_coeff <= 1.0); + + for (int32_t i = n - 1; i > 0; --i) { + d[i] -= preemph_coeff * d[i - 1]; + } + d[0] -= preemph_coeff * d[0]; +} + +void ProcessWindow(const FrameExtractionOptions &opts, + const FeatureWindowFunction &window_function, float *window, + float *log_energy_pre_window /*= nullptr*/) { + int32_t frame_length = opts.WindowSize(); + + if (opts.dither != 0.0) { + Dither(window, frame_length, opts.dither); + } + + if (opts.remove_dc_offset) { + RemoveDcOffset(window, frame_length); + } + + if (log_energy_pre_window != NULL) { + float energy = std::max(InnerProduct(window, window, frame_length), + std::numeric_limits::epsilon()); + *log_energy_pre_window = std::log(energy); + } + + if (opts.preemph_coeff != 0.0) { + Preemphasize(window, frame_length, opts.preemph_coeff); + } + + window_function.Apply(window); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-window.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-window.h new file mode 100644 index 0000000..88b509e --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/feature-window.h @@ -0,0 +1,178 @@ +// kaldi-native-fbank/csrc/feature-window.h +// +// Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + +// This file is copied/modified from kaldi/src/feat/feature-window.h + +#ifndef KALDI_NATIVE_FBANK_CSRC_FEATURE_WINDOW_H_ +#define KALDI_NATIVE_FBANK_CSRC_FEATURE_WINDOW_H_ + +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/log.h" + +namespace knf { + +inline int32_t RoundUpToNearestPowerOfTwo(int32_t n) { + // copied from kaldi/src/base/kaldi-math.cc + KNF_CHECK_GT(n, 0); + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + return n + 1; +} + +struct FrameExtractionOptions { + float samp_freq = 16000; + float frame_shift_ms = 10.0f; // in milliseconds. + float frame_length_ms = 25.0f; // in milliseconds. + + float dither = 0.00003f; // Amount of dithering, 0.0 means no dither. + // Value 0.00003f is equivalent to 1.0 in kaldi. + + float preemph_coeff = 0.97f; // Preemphasis coefficient. + bool remove_dc_offset = true; // Subtract mean of wave before FFT. + std::string window_type = "povey"; // e.g. Hamming window + // May be "hamming", "rectangular", "povey", "hanning", "hann", "sine", + // "blackman". + // "povey" is a window I made to be similar to Hamming but to go to zero at + // the edges, it's pow((0.5 - 0.5*cos(n/N*2*pi)), 0.85) I just don't think the + // Hamming window makes sense as a windowing function. + bool round_to_power_of_two = true; + float blackman_coeff = 0.42f; + bool snip_edges = true; + // bool allow_downsample = false; + // bool allow_upsample = false; + + int32_t WindowShift() const { + return static_cast(samp_freq * 0.001f * frame_shift_ms); + } + int32_t WindowSize() const { + return static_cast(samp_freq * 0.001f * frame_length_ms); + } + int32_t PaddedWindowSize() const { + return (round_to_power_of_two ? RoundUpToNearestPowerOfTwo(WindowSize()) + : WindowSize()); + } + std::string ToString() const { + std::ostringstream os; +#define KNF_PRINT(x) os << #x << ": " << x << "\n" + KNF_PRINT(samp_freq); + KNF_PRINT(frame_shift_ms); + KNF_PRINT(frame_length_ms); + KNF_PRINT(dither); + KNF_PRINT(preemph_coeff); + KNF_PRINT(remove_dc_offset); + KNF_PRINT(window_type); + KNF_PRINT(round_to_power_of_two); + KNF_PRINT(blackman_coeff); + KNF_PRINT(snip_edges); + // KNF_PRINT(allow_downsample); + // KNF_PRINT(allow_upsample); +#undef KNF_PRINT + return os.str(); + } +}; + +std::ostream &operator<<(std::ostream &os, const FrameExtractionOptions &opts); + +class FeatureWindowFunction { + public: + FeatureWindowFunction() = default; + explicit FeatureWindowFunction(const FrameExtractionOptions &opts); + /** + * @param wave Pointer to a 1-D array of shape [window_size]. + * It is modified in-place: wave[i] = wave[i] * window_[i]. + * @param + */ + void Apply(float *wave) const; + const std::vector &GetWindow() const { return window_; } + + private: + std::vector window_; // of size opts.WindowSize() +}; + +int64_t FirstSampleOfFrame(int32_t frame, const FrameExtractionOptions &opts); + +/** + This function returns the number of frames that we can extract from a wave + file with the given number of samples in it (assumed to have the same + sampling rate as specified in 'opts'). + + @param [in] num_samples The number of samples in the wave file. + @param [in] opts The frame-extraction options class + + @param [in] flush True if we are asserting that this number of samples + is 'all there is', false if we expecting more data to possibly come in. This + only makes a difference to the answer + if opts.snip_edges== false. For offline feature extraction you always want + flush == true. In an online-decoding context, once you know (or decide) that + no more data is coming in, you'd call it with flush == true at the end to + flush out any remaining data. +*/ +int32_t NumFrames(int64_t num_samples, const FrameExtractionOptions &opts, + bool flush = true); + +/* + ExtractWindow() extracts a windowed frame of waveform (possibly with a + power-of-two, padded size, depending on the config), including all the + processing done by ProcessWindow(). + + @param [in] sample_offset If 'wave' is not the entire waveform, but + part of it to the left has been discarded, then the + number of samples prior to 'wave' that we have + already discarded. Set this to zero if you are + processing the entire waveform in one piece, or + if you get 'no matching function' compilation + errors when updating the code. + @param [in] wave The waveform + @param [in] f The frame index to be extracted, with + 0 <= f < NumFrames(sample_offset + wave.Dim(), opts, true) + @param [in] opts The options class to be used + @param [in] window_function The windowing function, as derived from the + options class. + @param [out] window The windowed, possibly-padded waveform to be + extracted. Will be resized as needed. + @param [out] log_energy_pre_window If non-NULL, the log-energy of + the signal prior to pre-emphasis and multiplying by + the windowing function will be written to here. +*/ +void ExtractWindow(int64_t sample_offset, const std::vector &wave, + int32_t f, const FrameExtractionOptions &opts, + const FeatureWindowFunction &window_function, + std::vector *window, + float *log_energy_pre_window = nullptr); + +/** + This function does all the windowing steps after actually + extracting the windowed signal: depending on the + configuration, it does dithering, dc offset removal, + preemphasis, and multiplication by the windowing function. + @param [in] opts The options class to be used + @param [in] window_function The windowing function-- should have + been initialized using 'opts'. + @param [in,out] window A vector of size opts.WindowSize(). Note: + it will typically be a sub-vector of a larger vector of size + opts.PaddedWindowSize(), with the remaining samples zero, + as the FFT code is more efficient if it operates on data with + power-of-two size. + @param [out] log_energy_pre_window If non-NULL, then after dithering and + DC offset removal, this function will write to this pointer the log of + the total energy (i.e. sum-squared) of the frame. + */ +void ProcessWindow(const FrameExtractionOptions &opts, + const FeatureWindowFunction &window_function, float *window, + float *log_energy_pre_window = nullptr); + +// Compute the inner product of two vectors +float InnerProduct(const float *a, const float *b, int32_t n); + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_CSRC_FEATURE_WINDOW_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/fftsg.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/fftsg.cc new file mode 100644 index 0000000..158045b --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/fftsg.cc @@ -0,0 +1,2883 @@ +/* This file is copied from + * + * https://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + * + * Copyright Takuya OOURA, 1996-2001 + * + * You may use, copy, modify and distribute this code for any + * purpose (include commercial use) and without fee. Please refer to + * this package when you modify this code. + */ +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :split-radix + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, double *, int *, double *); + void rdft(int, int, double *, int *, double *); + void ddct(int, int, double *, int *, double *); + void ddst(int, int, double *, int *, double *); + void dfct(int, double *, double *, int *, double *); + void dfst(int, double *, double *, int *, double *); +macro definitions + USE_CDFT_PTHREADS : default=not defined + CDFT_THREADS_BEGIN_N : must be >= 512, default=8192 + CDFT_4THREADS_BEGIN_N : must be >= 512, default=65536 + USE_CDFT_WINTHREADS : default=not defined + CDFT_THREADS_BEGIN_N : must be >= 512, default=32768 + CDFT_4THREADS_BEGIN_N : must be >= 512, default=524288 + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (double *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + + output data + a[2*k] = R[k], 0<=k + input data + a[2*j] = R[j], 0<=j= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + output data + a[k] = C[k], 0<=k= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + + input data + a[j] = A[j], 0 + output data + a[k] = S[k], 0= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (double *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (double *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + output data + a[k] = S[k], 0= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + +#include + +#ifdef USE_CDFT_PTHREADS +#define USE_CDFT_THREADS +#ifndef CDFT_THREADS_BEGIN_N +#define CDFT_THREADS_BEGIN_N 8192 +#endif +#ifndef CDFT_4THREADS_BEGIN_N +#define CDFT_4THREADS_BEGIN_N 65536 +#endif +#include +#include +#include +#define cdft_thread_t pthread_t +#define cdft_thread_create(thp,func,argp) { \ + if (pthread_create(thp, NULL, func, (void *) argp) != 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#define cdft_thread_wait(th) { \ + if (pthread_join(th, NULL) != 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#endif /* USE_CDFT_PTHREADS */ + + +#ifdef USE_CDFT_WINTHREADS +#define USE_CDFT_THREADS +#ifndef CDFT_THREADS_BEGIN_N +#define CDFT_THREADS_BEGIN_N 32768 +#endif +#ifndef CDFT_4THREADS_BEGIN_N +#define CDFT_4THREADS_BEGIN_N 524288 +#endif +#include +#include +#include +#define cdft_thread_t HANDLE +#define cdft_thread_create(thp,func,argp) { \ + DWORD thid; \ + *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, (LPVOID) argp, 0, &thid); \ + if (*(thp) == 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#define cdft_thread_wait(th) { \ + WaitForSingleObject(th, INFINITE); \ + CloseHandle(th); \ +} +#endif /* USE_CDFT_WINTHREADS */ + + +namespace knf { + +static void makeipt(int nw, int *ip) +{ + int j, l, m, m2, p, q; + + ip[2] = 0; + ip[3] = 16; + m = 2; + for (l = nw; l > 32; l >>= 2) { + m2 = m << 1; + q = m2 << 3; + for (j = m; j < m2; j++) { + p = ip[j] << 2; + ip[m + j] = p; + ip[m2 + j] = p + q; + } + m = m2; + } +} + +static void makewt(int nw, int *ip, double *w) +{ + int j, nwh, nw0, nw1; + double delta, wn4r, wk1r, wk1i, wk3r, wk3i; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atan(1.0) / nwh; + wn4r = cos(delta * nwh); + w[0] = 1; + w[1] = wn4r; + if (nwh == 4) { + w[2] = cos(delta * 2); + w[3] = sin(delta * 2); + } else if (nwh > 4) { + makeipt(nw, ip); + w[2] = 0.5 / cos(delta * 2); + w[3] = 0.5 / cos(delta * 6); + for (j = 4; j < nwh; j += 4) { + w[j] = cos(delta * j); + w[j + 1] = sin(delta * j); + w[j + 2] = cos(3 * delta * j); + w[j + 3] = -sin(3 * delta * j); + } + } + nw0 = 0; + while (nwh > 2) { + nw1 = nw0 + nwh; + nwh >>= 1; + w[nw1] = 1; + w[nw1 + 1] = wn4r; + if (nwh == 4) { + wk1r = w[nw0 + 4]; + wk1i = w[nw0 + 5]; + w[nw1 + 2] = wk1r; + w[nw1 + 3] = wk1i; + } else if (nwh > 4) { + wk1r = w[nw0 + 4]; + wk3r = w[nw0 + 6]; + w[nw1 + 2] = 0.5 / wk1r; + w[nw1 + 3] = 0.5 / wk3r; + for (j = 4; j < nwh; j += 4) { + wk1r = w[nw0 + 2 * j]; + wk1i = w[nw0 + 2 * j + 1]; + wk3r = w[nw0 + 2 * j + 2]; + wk3i = w[nw0 + 2 * j + 3]; + w[nw1 + j] = wk1r; + w[nw1 + j + 1] = wk1i; + w[nw1 + j + 2] = wk3r; + w[nw1 + j + 3] = wk3i; + } + } + nw0 = nw1; + } + } +} + +static void makect(int nc, int *ip, double *c) +{ + int j, nch; + double delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atan(1.0) / nch; + c[0] = cos(delta * nch); + c[nch] = 0.5 * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5 * cos(delta * j); + c[nc - j] = 0.5 * sin(delta * j); + } + } +} + +/* -------- child routines -------- */ + +static void bitrv2(int n, int *ip, double *a) +{ + int j, j1, k, k1, l, m, nh, nm; + double xr, xi, yr, yi; + + m = 1; + for (l = n >> 2; l > 8; l >>= 2) { + m <<= 1; + } + nh = n >> 1; + nm = 4 * m; + if (l == 8) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + 2 * ip[m + k]; + k1 = 4 * k + 2 * ip[m + j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + 2 * ip[m + k]; + j1 = k1 + 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= 2; + k1 -= nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh + 2; + k1 += nh + 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh - nm; + k1 += 2 * nm - 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } else { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + ip[m + k]; + k1 = 4 * k + ip[m + j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + ip[m + k]; + j1 = k1 + 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } +} + +static void bitrv2conj(int n, int *ip, double *a) +{ + int j, j1, k, k1, l, m, nh, nm; + double xr, xi, yr, yi; + + m = 1; + for (l = n >> 2; l > 8; l >>= 2) { + m <<= 1; + } + nh = n >> 1; + nm = 4 * m; + if (l == 8) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + 2 * ip[m + k]; + k1 = 4 * k + 2 * ip[m + j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + 2 * ip[m + k]; + j1 = k1 + 2; + k1 += nh; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= 2; + k1 -= nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh + 2; + k1 += nh + 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh - nm; + k1 += 2 * nm - 2; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + } + } else { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + ip[m + k]; + k1 = 4 * k + ip[m + j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + ip[m + k]; + j1 = k1 + 2; + k1 += nh; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + j1 += nm; + k1 += nm; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + } + } +} + +static void bitrv216(double *a) +{ + double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, + x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x7r = a[14]; + x7i = a[15]; + x8r = a[16]; + x8i = a[17]; + x10r = a[20]; + x10i = a[21]; + x11r = a[22]; + x11i = a[23]; + x12r = a[24]; + x12i = a[25]; + x13r = a[26]; + x13i = a[27]; + x14r = a[28]; + x14i = a[29]; + a[2] = x8r; + a[3] = x8i; + a[4] = x4r; + a[5] = x4i; + a[6] = x12r; + a[7] = x12i; + a[8] = x2r; + a[9] = x2i; + a[10] = x10r; + a[11] = x10i; + a[14] = x14r; + a[15] = x14i; + a[16] = x1r; + a[17] = x1i; + a[20] = x5r; + a[21] = x5i; + a[22] = x13r; + a[23] = x13i; + a[24] = x3r; + a[25] = x3i; + a[26] = x11r; + a[27] = x11i; + a[28] = x7r; + a[29] = x7i; +} + +static void bitrv216neg(double *a) +{ + double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, + x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, + x13r, x13i, x14r, x14i, x15r, x15i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x6r = a[12]; + x6i = a[13]; + x7r = a[14]; + x7i = a[15]; + x8r = a[16]; + x8i = a[17]; + x9r = a[18]; + x9i = a[19]; + x10r = a[20]; + x10i = a[21]; + x11r = a[22]; + x11i = a[23]; + x12r = a[24]; + x12i = a[25]; + x13r = a[26]; + x13i = a[27]; + x14r = a[28]; + x14i = a[29]; + x15r = a[30]; + x15i = a[31]; + a[2] = x15r; + a[3] = x15i; + a[4] = x7r; + a[5] = x7i; + a[6] = x11r; + a[7] = x11i; + a[8] = x3r; + a[9] = x3i; + a[10] = x13r; + a[11] = x13i; + a[12] = x5r; + a[13] = x5i; + a[14] = x9r; + a[15] = x9i; + a[16] = x1r; + a[17] = x1i; + a[18] = x14r; + a[19] = x14i; + a[20] = x6r; + a[21] = x6i; + a[22] = x10r; + a[23] = x10i; + a[24] = x2r; + a[25] = x2i; + a[26] = x12r; + a[27] = x12i; + a[28] = x4r; + a[29] = x4i; + a[30] = x8r; + a[31] = x8i; +} + +static void bitrv208(double *a) +{ + double x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i; + + x1r = a[2]; + x1i = a[3]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x6r = a[12]; + x6i = a[13]; + a[2] = x4r; + a[3] = x4i; + a[6] = x6r; + a[7] = x6i; + a[8] = x1r; + a[9] = x1i; + a[12] = x3r; + a[13] = x3i; +} + +static void bitrv208neg(double *a) +{ + double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x6r, x6i, x7r, x7i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x6r = a[12]; + x6i = a[13]; + x7r = a[14]; + x7i = a[15]; + a[2] = x7r; + a[3] = x7i; + a[4] = x3r; + a[5] = x3i; + a[6] = x5r; + a[7] = x5i; + a[8] = x1r; + a[9] = x1i; + a[10] = x6r; + a[11] = x6i; + a[12] = x2r; + a[13] = x2i; + a[14] = x4r; + a[15] = x4i; +} + +static void cftf1st(int n, double *a, double *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, + wd1r, wd1i, wd3r, wd3i; + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = a[1] + a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = a[1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j2] = x1r - x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + csc1 = w[2]; + csc3 = w[3]; + wd1r = 1; + wd1i = 0; + wd3r = 1; + wd3i = 0; + k = 0; + for (j = 2; j < mh - 2; j += 4) { + k += 4; + wk1r = csc1 * (wd1r + w[k]); + wk1i = csc1 * (wd1i + w[k + 1]); + wk3r = csc3 * (wd3r + w[k + 2]); + wk3i = csc3 * (wd3i + w[k + 3]); + wd1r = w[k]; + wd1i = w[k + 1]; + wd3r = w[k + 2]; + wd3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = a[j + 1] + a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = a[j + 1] - a[j2 + 1]; + y0r = a[j + 2] + a[j2 + 2]; + y0i = a[j + 3] + a[j2 + 3]; + y1r = a[j + 2] - a[j2 + 2]; + y1i = a[j + 3] - a[j2 + 3]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 + 2] + a[j3 + 2]; + y2i = a[j1 + 3] + a[j3 + 3]; + y3r = a[j1 + 2] - a[j3 + 2]; + y3i = a[j1 + 3] - a[j3 + 3]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j + 2] = y0r + y2r; + a[j + 3] = y0i + y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j1 + 2] = y0r - y2r; + a[j1 + 3] = y0i - y2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = y1r - y3i; + x0i = y1i + y3r; + a[j2 + 2] = wd1r * x0r - wd1i * x0i; + a[j2 + 3] = wd1r * x0i + wd1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + x0r = y1r + y3i; + x0i = y1i - y3r; + a[j3 + 2] = wd3r * x0r + wd3i * x0i; + a[j3 + 3] = wd3r * x0i - wd3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + y0r = a[j0 - 2] + a[j2 - 2]; + y0i = a[j0 - 1] + a[j2 - 1]; + y1r = a[j0 - 2] - a[j2 - 2]; + y1i = a[j0 - 1] - a[j2 - 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 - 2] + a[j3 - 2]; + y2i = a[j1 - 1] + a[j3 - 1]; + y3r = a[j1 - 2] - a[j3 - 2]; + y3i = a[j1 - 1] - a[j3 - 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j0 - 2] = y0r + y2r; + a[j0 - 1] = y0i + y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j1 - 2] = y0r - y2r; + a[j1 - 1] = y0i - y2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = y1r - y3i; + x0i = y1i + y3r; + a[j2 - 2] = wd1i * x0r - wd1r * x0i; + a[j2 - 1] = wd1i * x0i + wd1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + x0r = y1r + y3i; + x0i = y1i - y3r; + a[j3 - 2] = wd3i * x0r + wd3r * x0i; + a[j3 - 1] = wd3i * x0i - wd3r * x0r; + } + wk1r = csc1 * (wd1r + wn4r); + wk1i = csc1 * (wd1i + wn4r); + wk3r = csc3 * (wd3r - wn4r); + wk3i = csc3 * (wd3i - wn4r); + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0 - 2] + a[j2 - 2]; + x0i = a[j0 - 1] + a[j2 - 1]; + x1r = a[j0 - 2] - a[j2 - 2]; + x1i = a[j0 - 1] - a[j2 - 1]; + x2r = a[j1 - 2] + a[j3 - 2]; + x2i = a[j1 - 1] + a[j3 - 1]; + x3r = a[j1 - 2] - a[j3 - 2]; + x3i = a[j1 - 1] - a[j3 - 1]; + a[j0 - 2] = x0r + x2r; + a[j0 - 1] = x0i + x2i; + a[j1 - 2] = x0r - x2r; + a[j1 - 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2 - 2] = wk1r * x0r - wk1i * x0i; + a[j2 - 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3 - 2] = wk3r * x0r + wk3i * x0i; + a[j3 - 1] = wk3r * x0i - wk3i * x0r; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); + x0r = a[j0 + 2] + a[j2 + 2]; + x0i = a[j0 + 3] + a[j2 + 3]; + x1r = a[j0 + 2] - a[j2 + 2]; + x1i = a[j0 + 3] - a[j2 + 3]; + x2r = a[j1 + 2] + a[j3 + 2]; + x2i = a[j1 + 3] + a[j3 + 3]; + x3r = a[j1 + 2] - a[j3 + 2]; + x3i = a[j1 + 3] - a[j3 + 3]; + a[j0 + 2] = x0r + x2r; + a[j0 + 3] = x0i + x2i; + a[j1 + 2] = x0r - x2r; + a[j1 + 3] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2 + 2] = wk1i * x0r - wk1r * x0i; + a[j2 + 3] = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3 + 2] = wk3i * x0r + wk3r * x0i; + a[j3 + 3] = wk3i * x0i - wk3r * x0r; +} + +static void cftb1st(int n, double *a, double *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, + wd1r, wd1i, wd3r, wd3i; + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = -a[1] - a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = -a[1] + a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i - x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j2] = x1r + x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r - x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + csc1 = w[2]; + csc3 = w[3]; + wd1r = 1; + wd1i = 0; + wd3r = 1; + wd3i = 0; + k = 0; + for (j = 2; j < mh - 2; j += 4) { + k += 4; + wk1r = csc1 * (wd1r + w[k]); + wk1i = csc1 * (wd1i + w[k + 1]); + wk3r = csc3 * (wd3r + w[k + 2]); + wk3i = csc3 * (wd3i + w[k + 3]); + wd1r = w[k]; + wd1i = w[k + 1]; + wd3r = w[k + 2]; + wd3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = -a[j + 1] - a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = -a[j + 1] + a[j2 + 1]; + y0r = a[j + 2] + a[j2 + 2]; + y0i = -a[j + 3] - a[j2 + 3]; + y1r = a[j + 2] - a[j2 + 2]; + y1i = -a[j + 3] + a[j2 + 3]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 + 2] + a[j3 + 2]; + y2i = a[j1 + 3] + a[j3 + 3]; + y3r = a[j1 + 2] - a[j3 + 2]; + y3i = a[j1 + 3] - a[j3 + 3]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j + 2] = y0r + y2r; + a[j + 3] = y0i - y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j1 + 2] = y0r - y2r; + a[j1 + 3] = y0i + y2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = y1r + y3i; + x0i = y1i + y3r; + a[j2 + 2] = wd1r * x0r - wd1i * x0i; + a[j2 + 3] = wd1r * x0i + wd1i * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + x0r = y1r - y3i; + x0i = y1i - y3r; + a[j3 + 2] = wd3r * x0r + wd3i * x0i; + a[j3 + 3] = wd3r * x0i - wd3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = -a[j0 + 1] - a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = -a[j0 + 1] + a[j2 + 1]; + y0r = a[j0 - 2] + a[j2 - 2]; + y0i = -a[j0 - 1] - a[j2 - 1]; + y1r = a[j0 - 2] - a[j2 - 2]; + y1i = -a[j0 - 1] + a[j2 - 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 - 2] + a[j3 - 2]; + y2i = a[j1 - 1] + a[j3 - 1]; + y3r = a[j1 - 2] - a[j3 - 2]; + y3i = a[j1 - 1] - a[j3 - 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i - x2i; + a[j0 - 2] = y0r + y2r; + a[j0 - 1] = y0i - y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j1 - 2] = y0r - y2r; + a[j1 - 1] = y0i + y2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = y1r + y3i; + x0i = y1i + y3r; + a[j2 - 2] = wd1i * x0r - wd1r * x0i; + a[j2 - 1] = wd1i * x0i + wd1r * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + x0r = y1r - y3i; + x0i = y1i - y3r; + a[j3 - 2] = wd3i * x0r + wd3r * x0i; + a[j3 - 1] = wd3i * x0i - wd3r * x0r; + } + wk1r = csc1 * (wd1r + wn4r); + wk1i = csc1 * (wd1i + wn4r); + wk3r = csc3 * (wd3r - wn4r); + wk3i = csc3 * (wd3i - wn4r); + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0 - 2] + a[j2 - 2]; + x0i = -a[j0 - 1] - a[j2 - 1]; + x1r = a[j0 - 2] - a[j2 - 2]; + x1i = -a[j0 - 1] + a[j2 - 1]; + x2r = a[j1 - 2] + a[j3 - 2]; + x2i = a[j1 - 1] + a[j3 - 1]; + x3r = a[j1 - 2] - a[j3 - 2]; + x3i = a[j1 - 1] - a[j3 - 1]; + a[j0 - 2] = x0r + x2r; + a[j0 - 1] = x0i - x2i; + a[j1 - 2] = x0r - x2r; + a[j1 - 1] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2 - 2] = wk1r * x0r - wk1i * x0i; + a[j2 - 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3 - 2] = wk3r * x0r + wk3i * x0i; + a[j3 - 1] = wk3r * x0i - wk3i * x0r; + x0r = a[j0] + a[j2]; + x0i = -a[j0 + 1] - a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = -a[j0 + 1] + a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i - x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); + x0r = a[j0 + 2] + a[j2 + 2]; + x0i = -a[j0 + 3] - a[j2 + 3]; + x1r = a[j0 + 2] - a[j2 + 2]; + x1i = -a[j0 + 3] + a[j2 + 3]; + x2r = a[j1 + 2] + a[j3 + 2]; + x2i = a[j1 + 3] + a[j3 + 3]; + x3r = a[j1 + 2] - a[j3 + 2]; + x3i = a[j1 + 3] - a[j3 + 3]; + a[j0 + 2] = x0r + x2r; + a[j0 + 3] = x0i - x2i; + a[j1 + 2] = x0r - x2r; + a[j1 + 3] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2 + 2] = wk1i * x0r - wk1r * x0i; + a[j2 + 3] = wk1i * x0i + wk1r * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3 + 2] = wk3i * x0r + wk3r * x0i; + a[j3 + 3] = wk3i * x0i - wk3r * x0r; +} + +static void cftmdl1(int n, double *a, double *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + double wn4r, wk1r, wk1i, wk3r, wk3i; + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = a[1] + a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = a[1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j2] = x1r - x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + k = 0; + for (j = 2; j < mh; j += 2) { + k += 4; + wk1r = w[k]; + wk1i = w[k + 1]; + wk3r = w[k + 2]; + wk3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = a[j + 1] + a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = a[j + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + } + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); +} + +static void cftmdl2(int n, double *a, double *w) +{ + int j, j0, j1, j2, j3, k, kr, m, mh; + double wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i; + + mh = n >> 3; + m = 2 * mh; + wn4r = w[1]; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] - a[j2 + 1]; + x0i = a[1] + a[j2]; + x1r = a[0] + a[j2 + 1]; + x1i = a[1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wn4r * (x2r - x2i); + y0i = wn4r * (x2i + x2r); + a[0] = x0r + y0r; + a[1] = x0i + y0i; + a[j1] = x0r - y0r; + a[j1 + 1] = x0i - y0i; + y0r = wn4r * (x3r - x3i); + y0i = wn4r * (x3i + x3r); + a[j2] = x1r - y0i; + a[j2 + 1] = x1i + y0r; + a[j3] = x1r + y0i; + a[j3 + 1] = x1i - y0r; + k = 0; + kr = 2 * m; + for (j = 2; j < mh; j += 2) { + k += 4; + wk1r = w[k]; + wk1i = w[k + 1]; + wk3r = w[k + 2]; + wk3i = w[k + 3]; + kr -= 4; + wd1i = w[kr]; + wd1r = w[kr + 1]; + wd3i = w[kr + 2]; + wd3r = w[kr + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] - a[j2 + 1]; + x0i = a[j + 1] + a[j2]; + x1r = a[j] + a[j2 + 1]; + x1i = a[j + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wk1r * x0r - wk1i * x0i; + y0i = wk1r * x0i + wk1i * x0r; + y2r = wd1r * x2r - wd1i * x2i; + y2i = wd1r * x2i + wd1i * x2r; + a[j] = y0r + y2r; + a[j + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wk3r * x1r + wk3i * x1i; + y0i = wk3r * x1i - wk3i * x1r; + y2r = wd3r * x3r + wd3i * x3i; + y2i = wd3r * x3i - wd3i * x3r; + a[j2] = y0r + y2r; + a[j2 + 1] = y0i + y2i; + a[j3] = y0r - y2r; + a[j3 + 1] = y0i - y2i; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] - a[j2 + 1]; + x0i = a[j0 + 1] + a[j2]; + x1r = a[j0] + a[j2 + 1]; + x1i = a[j0 + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wd1i * x0r - wd1r * x0i; + y0i = wd1i * x0i + wd1r * x0r; + y2r = wk1i * x2r - wk1r * x2i; + y2i = wk1i * x2i + wk1r * x2r; + a[j0] = y0r + y2r; + a[j0 + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wd3i * x1r + wd3r * x1i; + y0i = wd3i * x1i - wd3r * x1r; + y2r = wk3i * x3r + wk3r * x3i; + y2i = wk3i * x3i - wk3r * x3r; + a[j2] = y0r + y2r; + a[j2 + 1] = y0i + y2i; + a[j3] = y0r - y2r; + a[j3 + 1] = y0i - y2i; + } + wk1r = w[m]; + wk1i = w[m + 1]; + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] - a[j2 + 1]; + x0i = a[j0 + 1] + a[j2]; + x1r = a[j0] + a[j2 + 1]; + x1i = a[j0 + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wk1r * x0r - wk1i * x0i; + y0i = wk1r * x0i + wk1i * x0r; + y2r = wk1i * x2r - wk1r * x2i; + y2i = wk1i * x2i + wk1r * x2r; + a[j0] = y0r + y2r; + a[j0 + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wk1i * x1r - wk1r * x1i; + y0i = wk1i * x1i + wk1r * x1r; + y2r = wk1r * x3r - wk1i * x3i; + y2i = wk1r * x3i + wk1i * x3r; + a[j2] = y0r - y2r; + a[j2 + 1] = y0i - y2i; + a[j3] = y0r + y2r; + a[j3 + 1] = y0i + y2i; +} + +static int cfttree(int n, int j, int k, double *a, int nw, double *w) +{ + int i, isplt, m; + + if ((k & 3) != 0) { + isplt = k & 1; + if (isplt != 0) { + cftmdl1(n, &a[j - n], &w[nw - (n >> 1)]); + } else { + cftmdl2(n, &a[j - n], &w[nw - n]); + } + } else { + m = n; + for (i = k; (i & 3) == 0; i >>= 2) { + m <<= 2; + } + isplt = i & 1; + if (isplt != 0) { + while (m > 128) { + cftmdl1(m, &a[j - m], &w[nw - (m >> 1)]); + m >>= 2; + } + } else { + while (m > 128) { + cftmdl2(m, &a[j - m], &w[nw - m]); + m >>= 2; + } + } + } + return isplt; +} + +static void cftf161(double *a, double *w) +{ + double wn4r, wk1r, wk1i, + x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, + y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, + y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; + + wn4r = w[1]; + wk1r = w[2]; + wk1i = w[3]; + x0r = a[0] + a[16]; + x0i = a[1] + a[17]; + x1r = a[0] - a[16]; + x1i = a[1] - a[17]; + x2r = a[8] + a[24]; + x2i = a[9] + a[25]; + x3r = a[8] - a[24]; + x3i = a[9] - a[25]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y4r = x0r - x2r; + y4i = x0i - x2i; + y8r = x1r - x3i; + y8i = x1i + x3r; + y12r = x1r + x3i; + y12i = x1i - x3r; + x0r = a[2] + a[18]; + x0i = a[3] + a[19]; + x1r = a[2] - a[18]; + x1i = a[3] - a[19]; + x2r = a[10] + a[26]; + x2i = a[11] + a[27]; + x3r = a[10] - a[26]; + x3i = a[11] - a[27]; + y1r = x0r + x2r; + y1i = x0i + x2i; + y5r = x0r - x2r; + y5i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y9r = wk1r * x0r - wk1i * x0i; + y9i = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + y13r = wk1i * x0r - wk1r * x0i; + y13i = wk1i * x0i + wk1r * x0r; + x0r = a[4] + a[20]; + x0i = a[5] + a[21]; + x1r = a[4] - a[20]; + x1i = a[5] - a[21]; + x2r = a[12] + a[28]; + x2i = a[13] + a[29]; + x3r = a[12] - a[28]; + x3i = a[13] - a[29]; + y2r = x0r + x2r; + y2i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y10r = wn4r * (x0r - x0i); + y10i = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + y14r = wn4r * (x0r + x0i); + y14i = wn4r * (x0i - x0r); + x0r = a[6] + a[22]; + x0i = a[7] + a[23]; + x1r = a[6] - a[22]; + x1i = a[7] - a[23]; + x2r = a[14] + a[30]; + x2i = a[15] + a[31]; + x3r = a[14] - a[30]; + x3i = a[15] - a[31]; + y3r = x0r + x2r; + y3i = x0i + x2i; + y7r = x0r - x2r; + y7i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y11r = wk1i * x0r - wk1r * x0i; + y11i = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + y15r = wk1r * x0r - wk1i * x0i; + y15i = wk1r * x0i + wk1i * x0r; + x0r = y12r - y14r; + x0i = y12i - y14i; + x1r = y12r + y14r; + x1i = y12i + y14i; + x2r = y13r - y15r; + x2i = y13i - y15i; + x3r = y13r + y15r; + x3i = y13i + y15i; + a[24] = x0r + x2r; + a[25] = x0i + x2i; + a[26] = x0r - x2r; + a[27] = x0i - x2i; + a[28] = x1r - x3i; + a[29] = x1i + x3r; + a[30] = x1r + x3i; + a[31] = x1i - x3r; + x0r = y8r + y10r; + x0i = y8i + y10i; + x1r = y8r - y10r; + x1i = y8i - y10i; + x2r = y9r + y11r; + x2i = y9i + y11i; + x3r = y9r - y11r; + x3i = y9i - y11i; + a[16] = x0r + x2r; + a[17] = x0i + x2i; + a[18] = x0r - x2r; + a[19] = x0i - x2i; + a[20] = x1r - x3i; + a[21] = x1i + x3r; + a[22] = x1r + x3i; + a[23] = x1i - x3r; + x0r = y5r - y7i; + x0i = y5i + y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + x0r = y5r + y7i; + x0i = y5i - y7r; + x3r = wn4r * (x0r - x0i); + x3i = wn4r * (x0i + x0r); + x0r = y4r - y6i; + x0i = y4i + y6r; + x1r = y4r + y6i; + x1i = y4i - y6r; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[10] = x0r - x2r; + a[11] = x0i - x2i; + a[12] = x1r - x3i; + a[13] = x1i + x3r; + a[14] = x1r + x3i; + a[15] = x1i - x3r; + x0r = y0r + y2r; + x0i = y0i + y2i; + x1r = y0r - y2r; + x1i = y0i - y2i; + x2r = y1r + y3r; + x2i = y1i + y3i; + x3r = y1r - y3r; + x3i = y1i - y3i; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x0r - x2r; + a[3] = x0i - x2i; + a[4] = x1r - x3i; + a[5] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; +} + +static void cftf162(double *a, double *w) +{ + double wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, + x0r, x0i, x1r, x1i, x2r, x2i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, + y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, + y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; + + wn4r = w[1]; + wk1r = w[4]; + wk1i = w[5]; + wk3r = w[6]; + wk3i = -w[7]; + wk2r = w[8]; + wk2i = w[9]; + x1r = a[0] - a[17]; + x1i = a[1] + a[16]; + x0r = a[8] - a[25]; + x0i = a[9] + a[24]; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + y0r = x1r + x2r; + y0i = x1i + x2i; + y4r = x1r - x2r; + y4i = x1i - x2i; + x1r = a[0] + a[17]; + x1i = a[1] - a[16]; + x0r = a[8] + a[25]; + x0i = a[9] - a[24]; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + y8r = x1r - x2i; + y8i = x1i + x2r; + y12r = x1r + x2i; + y12i = x1i - x2r; + x0r = a[2] - a[19]; + x0i = a[3] + a[18]; + x1r = wk1r * x0r - wk1i * x0i; + x1i = wk1r * x0i + wk1i * x0r; + x0r = a[10] - a[27]; + x0i = a[11] + a[26]; + x2r = wk3i * x0r - wk3r * x0i; + x2i = wk3i * x0i + wk3r * x0r; + y1r = x1r + x2r; + y1i = x1i + x2i; + y5r = x1r - x2r; + y5i = x1i - x2i; + x0r = a[2] + a[19]; + x0i = a[3] - a[18]; + x1r = wk3r * x0r - wk3i * x0i; + x1i = wk3r * x0i + wk3i * x0r; + x0r = a[10] + a[27]; + x0i = a[11] - a[26]; + x2r = wk1r * x0r + wk1i * x0i; + x2i = wk1r * x0i - wk1i * x0r; + y9r = x1r - x2r; + y9i = x1i - x2i; + y13r = x1r + x2r; + y13i = x1i + x2i; + x0r = a[4] - a[21]; + x0i = a[5] + a[20]; + x1r = wk2r * x0r - wk2i * x0i; + x1i = wk2r * x0i + wk2i * x0r; + x0r = a[12] - a[29]; + x0i = a[13] + a[28]; + x2r = wk2i * x0r - wk2r * x0i; + x2i = wk2i * x0i + wk2r * x0r; + y2r = x1r + x2r; + y2i = x1i + x2i; + y6r = x1r - x2r; + y6i = x1i - x2i; + x0r = a[4] + a[21]; + x0i = a[5] - a[20]; + x1r = wk2i * x0r - wk2r * x0i; + x1i = wk2i * x0i + wk2r * x0r; + x0r = a[12] + a[29]; + x0i = a[13] - a[28]; + x2r = wk2r * x0r - wk2i * x0i; + x2i = wk2r * x0i + wk2i * x0r; + y10r = x1r - x2r; + y10i = x1i - x2i; + y14r = x1r + x2r; + y14i = x1i + x2i; + x0r = a[6] - a[23]; + x0i = a[7] + a[22]; + x1r = wk3r * x0r - wk3i * x0i; + x1i = wk3r * x0i + wk3i * x0r; + x0r = a[14] - a[31]; + x0i = a[15] + a[30]; + x2r = wk1i * x0r - wk1r * x0i; + x2i = wk1i * x0i + wk1r * x0r; + y3r = x1r + x2r; + y3i = x1i + x2i; + y7r = x1r - x2r; + y7i = x1i - x2i; + x0r = a[6] + a[23]; + x0i = a[7] - a[22]; + x1r = wk1i * x0r + wk1r * x0i; + x1i = wk1i * x0i - wk1r * x0r; + x0r = a[14] + a[31]; + x0i = a[15] - a[30]; + x2r = wk3i * x0r - wk3r * x0i; + x2i = wk3i * x0i + wk3r * x0r; + y11r = x1r + x2r; + y11i = x1i + x2i; + y15r = x1r - x2r; + y15i = x1i - x2i; + x1r = y0r + y2r; + x1i = y0i + y2i; + x2r = y1r + y3r; + x2i = y1i + y3i; + a[0] = x1r + x2r; + a[1] = x1i + x2i; + a[2] = x1r - x2r; + a[3] = x1i - x2i; + x1r = y0r - y2r; + x1i = y0i - y2i; + x2r = y1r - y3r; + x2i = y1i - y3i; + a[4] = x1r - x2i; + a[5] = x1i + x2r; + a[6] = x1r + x2i; + a[7] = x1i - x2r; + x1r = y4r - y6i; + x1i = y4i + y6r; + x0r = y5r - y7i; + x0i = y5i + y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[8] = x1r + x2r; + a[9] = x1i + x2i; + a[10] = x1r - x2r; + a[11] = x1i - x2i; + x1r = y4r + y6i; + x1i = y4i - y6r; + x0r = y5r + y7i; + x0i = y5i - y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[12] = x1r - x2i; + a[13] = x1i + x2r; + a[14] = x1r + x2i; + a[15] = x1i - x2r; + x1r = y8r + y10r; + x1i = y8i + y10i; + x2r = y9r - y11r; + x2i = y9i - y11i; + a[16] = x1r + x2r; + a[17] = x1i + x2i; + a[18] = x1r - x2r; + a[19] = x1i - x2i; + x1r = y8r - y10r; + x1i = y8i - y10i; + x2r = y9r + y11r; + x2i = y9i + y11i; + a[20] = x1r - x2i; + a[21] = x1i + x2r; + a[22] = x1r + x2i; + a[23] = x1i - x2r; + x1r = y12r - y14i; + x1i = y12i + y14r; + x0r = y13r + y15i; + x0i = y13i - y15r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[24] = x1r + x2r; + a[25] = x1i + x2i; + a[26] = x1r - x2r; + a[27] = x1i - x2i; + x1r = y12r + y14i; + x1i = y12i - y14r; + x0r = y13r - y15i; + x0i = y13i + y15r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[28] = x1r - x2i; + a[29] = x1i + x2r; + a[30] = x1r + x2i; + a[31] = x1i - x2r; +} + +static void cftf081(double *a, double *w) +{ + double wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + wn4r = w[1]; + x0r = a[0] + a[8]; + x0i = a[1] + a[9]; + x1r = a[0] - a[8]; + x1i = a[1] - a[9]; + x2r = a[4] + a[12]; + x2i = a[5] + a[13]; + x3r = a[4] - a[12]; + x3i = a[5] - a[13]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[2] + a[10]; + x0i = a[3] + a[11]; + x1r = a[2] - a[10]; + x1i = a[3] - a[11]; + x2r = a[6] + a[14]; + x2i = a[7] + a[15]; + x3r = a[6] - a[14]; + x3i = a[7] - a[15]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x1i - x3r; + y5r = wn4r * (x0r - x0i); + y5i = wn4r * (x0r + x0i); + y7r = wn4r * (x2r - x2i); + y7i = wn4r * (x2r + x2i); + a[8] = y1r + y5r; + a[9] = y1i + y5i; + a[10] = y1r - y5r; + a[11] = y1i - y5i; + a[12] = y3r - y7i; + a[13] = y3i + y7r; + a[14] = y3r + y7i; + a[15] = y3i - y7r; + a[0] = y0r + y4r; + a[1] = y0i + y4i; + a[2] = y0r - y4r; + a[3] = y0i - y4i; + a[4] = y2r - y6i; + a[5] = y2i + y6r; + a[6] = y2r + y6i; + a[7] = y2i - y6r; +} + +static void cftf082(double *a, double *w) +{ + double wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + wn4r = w[1]; + wk1r = w[2]; + wk1i = w[3]; + y0r = a[0] - a[9]; + y0i = a[1] + a[8]; + y1r = a[0] + a[9]; + y1i = a[1] - a[8]; + x0r = a[4] - a[13]; + x0i = a[5] + a[12]; + y2r = wn4r * (x0r - x0i); + y2i = wn4r * (x0i + x0r); + x0r = a[4] + a[13]; + x0i = a[5] - a[12]; + y3r = wn4r * (x0r - x0i); + y3i = wn4r * (x0i + x0r); + x0r = a[2] - a[11]; + x0i = a[3] + a[10]; + y4r = wk1r * x0r - wk1i * x0i; + y4i = wk1r * x0i + wk1i * x0r; + x0r = a[2] + a[11]; + x0i = a[3] - a[10]; + y5r = wk1i * x0r - wk1r * x0i; + y5i = wk1i * x0i + wk1r * x0r; + x0r = a[6] - a[15]; + x0i = a[7] + a[14]; + y6r = wk1i * x0r - wk1r * x0i; + y6i = wk1i * x0i + wk1r * x0r; + x0r = a[6] + a[15]; + x0i = a[7] - a[14]; + y7r = wk1r * x0r - wk1i * x0i; + y7i = wk1r * x0i + wk1i * x0r; + x0r = y0r + y2r; + x0i = y0i + y2i; + x1r = y4r + y6r; + x1i = y4i + y6i; + a[0] = x0r + x1r; + a[1] = x0i + x1i; + a[2] = x0r - x1r; + a[3] = x0i - x1i; + x0r = y0r - y2r; + x0i = y0i - y2i; + x1r = y4r - y6r; + x1i = y4i - y6i; + a[4] = x0r - x1i; + a[5] = x0i + x1r; + a[6] = x0r + x1i; + a[7] = x0i - x1r; + x0r = y1r - y3i; + x0i = y1i + y3r; + x1r = y5r - y7r; + x1i = y5i - y7i; + a[8] = x0r + x1r; + a[9] = x0i + x1i; + a[10] = x0r - x1r; + a[11] = x0i - x1i; + x0r = y1r + y3i; + x0i = y1i - y3r; + x1r = y5r + y7r; + x1i = y5i + y7i; + a[12] = x0r - x1i; + a[13] = x0i + x1r; + a[14] = x0r + x1i; + a[15] = x0i - x1r; +} + +static void cftf040(double *a) +{ + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[4]; + x0i = a[1] + a[5]; + x1r = a[0] - a[4]; + x1i = a[1] - a[5]; + x2r = a[2] + a[6]; + x2i = a[3] + a[7]; + x3r = a[2] - a[6]; + x3i = a[3] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[6] = x1r + x3i; + a[7] = x1i - x3r; +} + +static void cftb040(double *a) +{ + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[4]; + x0i = a[1] + a[5]; + x1r = a[0] - a[4]; + x1i = a[1] - a[5]; + x2r = a[2] + a[6]; + x2i = a[3] + a[7]; + x3r = a[2] - a[6]; + x3i = a[3] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x1r + x3i; + a[3] = x1i - x3r; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[6] = x1r - x3i; + a[7] = x1i + x3r; +} + +static void cftx020(double *a) +{ + double x0r, x0i; + + x0r = a[0] - a[2]; + x0i = a[1] - a[3]; + a[0] += a[2]; + a[1] += a[3]; + a[2] = x0r; + a[3] = x0i; +} + +static void cftfx41(int n, double *a, int nw, double *w) +{ + if (n == 128) { + cftf161(a, &w[nw - 8]); + cftf162(&a[32], &w[nw - 32]); + cftf161(&a[64], &w[nw - 8]); + cftf161(&a[96], &w[nw - 8]); + } else { + cftf081(a, &w[nw - 8]); + cftf082(&a[16], &w[nw - 8]); + cftf081(&a[32], &w[nw - 8]); + cftf081(&a[48], &w[nw - 8]); + } +} + +static void cftleaf(int n, int isplt, double *a, int nw, double *w) +{ + if (n == 512) { + cftmdl1(128, a, &w[nw - 64]); + cftf161(a, &w[nw - 8]); + cftf162(&a[32], &w[nw - 32]); + cftf161(&a[64], &w[nw - 8]); + cftf161(&a[96], &w[nw - 8]); + cftmdl2(128, &a[128], &w[nw - 128]); + cftf161(&a[128], &w[nw - 8]); + cftf162(&a[160], &w[nw - 32]); + cftf161(&a[192], &w[nw - 8]); + cftf162(&a[224], &w[nw - 32]); + cftmdl1(128, &a[256], &w[nw - 64]); + cftf161(&a[256], &w[nw - 8]); + cftf162(&a[288], &w[nw - 32]); + cftf161(&a[320], &w[nw - 8]); + cftf161(&a[352], &w[nw - 8]); + if (isplt != 0) { + cftmdl1(128, &a[384], &w[nw - 64]); + cftf161(&a[480], &w[nw - 8]); + } else { + cftmdl2(128, &a[384], &w[nw - 128]); + cftf162(&a[480], &w[nw - 32]); + } + cftf161(&a[384], &w[nw - 8]); + cftf162(&a[416], &w[nw - 32]); + cftf161(&a[448], &w[nw - 8]); + } else { + cftmdl1(64, a, &w[nw - 32]); + cftf081(a, &w[nw - 8]); + cftf082(&a[16], &w[nw - 8]); + cftf081(&a[32], &w[nw - 8]); + cftf081(&a[48], &w[nw - 8]); + cftmdl2(64, &a[64], &w[nw - 64]); + cftf081(&a[64], &w[nw - 8]); + cftf082(&a[80], &w[nw - 8]); + cftf081(&a[96], &w[nw - 8]); + cftf082(&a[112], &w[nw - 8]); + cftmdl1(64, &a[128], &w[nw - 32]); + cftf081(&a[128], &w[nw - 8]); + cftf082(&a[144], &w[nw - 8]); + cftf081(&a[160], &w[nw - 8]); + cftf081(&a[176], &w[nw - 8]); + if (isplt != 0) { + cftmdl1(64, &a[192], &w[nw - 32]); + cftf081(&a[240], &w[nw - 8]); + } else { + cftmdl2(64, &a[192], &w[nw - 64]); + cftf082(&a[240], &w[nw - 8]); + } + cftf081(&a[192], &w[nw - 8]); + cftf082(&a[208], &w[nw - 8]); + cftf081(&a[224], &w[nw - 8]); + } +} + +static void cftrec4(int n, double *a, int nw, double *w) +{ + int isplt, j, k, m; + + m = n; + while (m > 512) { + m >>= 2; + cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); + } + cftleaf(m, 1, &a[n - m], nw, w); + k = 0; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } +} + +static void rftfsub(int n, double *a, int nc, double *c) +{ + int j, k, kk, ks, m; + double wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + +static void rftbsub(int n, double *a, int nc, double *c) +{ + int j, k, kk, ks, m; + double wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + +#ifdef USE_CDFT_THREADS +struct cdft_arg_st { + int n0; + int n; + double *a; + int nw; + double *w; +}; +typedef struct cdft_arg_st cdft_arg_t; + +static void cftrec4_th(int n, double *a, int nw, double *w) +{ + int i, idiv4, m, nthread; + cdft_thread_t th[4]; + cdft_arg_t ag[4]; + + nthread = 2; + idiv4 = 0; + m = n >> 1; + if (n > CDFT_4THREADS_BEGIN_N) { + nthread = 4; + idiv4 = 1; + m >>= 1; + } + for (i = 0; i < nthread; i++) { + ag[i].n0 = n; + ag[i].n = m; + ag[i].a = &a[i * m]; + ag[i].nw = nw; + ag[i].w = w; + if (i != idiv4) { + cdft_thread_create(&th[i], cftrec1_th, &ag[i]); + } else { + cdft_thread_create(&th[i], cftrec2_th, &ag[i]); + } + } + for (i = 0; i < nthread; i++) { + cdft_thread_wait(th[i]); + } +} + +static void *cftrec1_th(void *p) +{ + int cfttree(int n, int j, int k, double *a, int nw, double *w); + void cftleaf(int n, int isplt, double *a, int nw, double *w); + void cftmdl1(int n, double *a, double *w); + int isplt, j, k, m, n, n0, nw; + double *a, *w; + + n0 = ((cdft_arg_t *) p)->n0; + n = ((cdft_arg_t *) p)->n; + a = ((cdft_arg_t *) p)->a; + nw = ((cdft_arg_t *) p)->nw; + w = ((cdft_arg_t *) p)->w; + m = n0; + while (m > 512) { + m >>= 2; + cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); + } + cftleaf(m, 1, &a[n - m], nw, w); + k = 0; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } + return (void *) 0; +} + +static void *cftrec2_th(void *p) +{ + int cfttree(int n, int j, int k, double *a, int nw, double *w); + void cftleaf(int n, int isplt, double *a, int nw, double *w); + void cftmdl2(int n, double *a, double *w); + int isplt, j, k, m, n, n0, nw; + double *a, *w; + + n0 = ((cdft_arg_t *) p)->n0; + n = ((cdft_arg_t *) p)->n; + a = ((cdft_arg_t *) p)->a; + nw = ((cdft_arg_t *) p)->nw; + w = ((cdft_arg_t *) p)->w; + k = 1; + m = n0; + while (m > 512) { + m >>= 2; + k <<= 2; + cftmdl2(m, &a[n - m], &w[nw - m]); + } + cftleaf(m, 0, &a[n - m], nw, w); + k >>= 1; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } + return (void *) 0; +} +#endif /* USE_CDFT_THREADS */ + +static void cftbsub(int n, double *a, int *ip, int nw, double *w) +{ + if (n > 8) { + if (n > 32) { + cftb1st(n, a, &w[nw - (n >> 2)]); +#ifdef USE_CDFT_THREADS + if (n > CDFT_THREADS_BEGIN_N) { + cftrec4_th(n, a, nw, w); + } else +#endif /* USE_CDFT_THREADS */ + if (n > 512) { + cftrec4(n, a, nw, w); + } else if (n > 128) { + cftleaf(n, 1, a, nw, w); + } else { + cftfx41(n, a, nw, w); + } + bitrv2conj(n, ip, a); + } else if (n == 32) { + cftf161(a, &w[nw - 8]); + bitrv216neg(a); + } else { + cftf081(a, w); + bitrv208neg(a); + } + } else if (n == 8) { + cftb040(a); + } else if (n == 4) { + cftx020(a); + } +} + +static void cftfsub(int n, double *a, int *ip, int nw, double *w) +{ + if (n > 8) { + if (n > 32) { + cftf1st(n, a, &w[nw - (n >> 2)]); +#ifdef USE_CDFT_THREADS + if (n > CDFT_THREADS_BEGIN_N) { + cftrec4_th(n, a, nw, w); + } else +#endif /* USE_CDFT_THREADS */ + if (n > 512) { + cftrec4(n, a, nw, w); + } else if (n > 128) { + cftleaf(n, 1, a, nw, w); + } else { + cftfx41(n, a, nw, w); + } + bitrv2(n, ip, a); + } else if (n == 32) { + cftf161(a, &w[nw - 8]); + bitrv216(a); + } else { + cftf081(a, w); + bitrv208(a); + } + } else if (n == 8) { + cftf040(a); + } else if (n == 4) { + cftx020(a); + } +} + +void rdft(int n, int isgn, double *a, int *ip, double *w) +{ + int nw, nc; + double xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + cftfsub(n, a, ip, nw, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, ip, nw, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } else { + a[1] = 0.5 * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + cftbsub(n, a, ip, nw, w); + } else if (n == 4) { + cftbsub(n, a, ip, nw, w); + } + } +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/kaldi-math.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/kaldi-math.cc new file mode 100644 index 0000000..bcc0dcc --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/kaldi-math.cc @@ -0,0 +1,51 @@ +// kaldi-native-fbank/csrc/kaldi-math.cc +// +// Copyright (c) 2024 Brno University of Technology (authors: Karel Vesely) + +// This file is an excerpt from kaldi/src/base/kaldi-math.cc + +#include "kaldi-native-fbank/csrc/kaldi-math.h" + +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) +#include // NOLINT +#endif + +#include + +namespace knf { + +int Rand(struct RandomState *state) { +#if !defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // On Windows and Cygwin, just call Rand() + return rand(); +#else + if (state) { + return rand_r(&(state->seed)); + } else { + static std::mutex _RandMutex; + std::lock_guard lock(_RandMutex); + return rand(); + } +#endif +} + +RandomState::RandomState() { + // we initialize it as Rand() + 27437 instead of just Rand(), because on some + // systems, e.g. at the very least Mac OSX Yosemite and later, it seems to be + // the case that rand_r when initialized with rand() will give you the exact + // same sequence of numbers that rand() will give if you keep calling rand() + // after that initial call. This can cause problems with repeated sequences. + // For example if you initialize two RandomState structs one after the other + // without calling rand() in between, they would give you the same sequence + // offset by one (if we didn't have the "+ 27437" in the code). 27437 is just + // a randomly chosen prime number. + seed = unsigned(Rand()) + 27437; +} + +void Sqrt(float *in_out, int32_t n) { + for (int32_t i = 0; i != n; ++i) { + in_out[i] = std::sqrt(in_out[i]); + } +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/kaldi-math.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/kaldi-math.h new file mode 100644 index 0000000..4db3f83 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/kaldi-math.h @@ -0,0 +1,52 @@ +// kaldi-native-fbank/csrc/kaldi-math.h +// +// Copyright (c) 2024 Brno University of Technology (authors: Karel Vesely) + +// This file is an excerpt from kaldi/src/base/kaldi-math.h + +#ifndef KALDI_NATIVE_FBANK_CSRC_KALDI_MATH_H_ +#define KALDI_NATIVE_FBANK_CSRC_KALDI_MATH_H_ + +#include // logf, sqrtf, cosf +#include +#include // RAND_MAX + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +#ifndef M_2PI +#define M_2PI 6.283185307179586476925286766559005 +#endif + +#ifndef M_SQRT2 +#define M_SQRT2 1.4142135623730950488016887 +#endif + +namespace knf { + +inline float Log(float x) { return logf(x); } + +// Returns a random integer between 0 and RAND_MAX, inclusive +int Rand(struct RandomState *state = NULL); + +// State for thread-safe random number generator +struct RandomState { + RandomState(); + unsigned seed; +}; + +/// Returns a random number strictly between 0 and 1. +inline float RandUniform(struct RandomState *state = NULL) { + return static_cast((Rand(state) + 1.0) / (RAND_MAX + 2.0)); +} + +inline float RandGauss(struct RandomState *state = NULL) { + return static_cast(sqrtf(-2 * Log(RandUniform(state))) * + cosf(2 * M_PI * RandUniform(state))); +} + +void Sqrt(float *in_out, int32_t n); + +} // namespace knf +#endif // KALDI_NATIVE_FBANK_CSRC_KALDI_MATH_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/log.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/log.cc new file mode 100644 index 0000000..7223337 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/log.cc @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Stack trace related stuff is from kaldi. + * Refer to + * https://github.com/kaldi-asr/kaldi/blob/master/src/base/kaldi-error.cc + */ + +#include "kaldi-native-fbank/csrc/log.h" + +#ifdef KNF_HAVE_EXECINFO_H +#include // To get stack trace in error messages. +#ifdef KNF_HAVE_CXXABI_H +#include // For name demangling. +// Useful to decode the stack trace, but only used if we have execinfo.h +#endif // KNF_HAVE_CXXABI_H +#endif // KNF_HAVE_EXECINFO_H + +#include + +#include +#include +#include + +namespace knf { + +std::string GetDateTimeStr() { + std::ostringstream os; + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + os << std::put_time(&tm, "%F %T"); // yyyy-mm-dd hh:mm:ss + return os.str(); +} + +static bool LocateSymbolRange(const std::string &trace_name, std::size_t *begin, + std::size_t *end) { + // Find the first '_' with leading ' ' or '('. + *begin = std::string::npos; + for (std::size_t i = 1; i < trace_name.size(); ++i) { + if (trace_name[i] != '_') { + continue; + } + if (trace_name[i - 1] == ' ' || trace_name[i - 1] == '(') { + *begin = i; + break; + } + } + if (*begin == std::string::npos) { + return false; + } + *end = trace_name.find_first_of(" +", *begin); + return *end != std::string::npos; +} + +#ifdef KNF_HAVE_EXECINFO_H +static std::string Demangle(const std::string &trace_name) { +#ifndef KNF_HAVE_CXXABI_H + return trace_name; +#else // KNF_HAVE_CXXABI_H + // Try demangle the symbol. We are trying to support the following formats + // produced by different platforms: + // + // Linux: + // ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d] + // + // Mac: + // 0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813 + // + // We want to extract the name e.g., '_ZN5kaldi13UnitTestErrorEv' and + // demangle it info a readable name like kaldi::UnitTextError. + std::size_t begin, end; + if (!LocateSymbolRange(trace_name, &begin, &end)) { + return trace_name; + } + std::string symbol = trace_name.substr(begin, end - begin); + int status; + char *demangled_name = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status); + if (status == 0 && demangled_name != nullptr) { + symbol = demangled_name; + free(demangled_name); + } + return trace_name.substr(0, begin) + symbol + + trace_name.substr(end, std::string::npos); +#endif // KNF_HAVE_CXXABI_H +} +#endif // KNF_HAVE_EXECINFO_H + +std::string GetStackTrace() { + std::string ans; +#ifdef KNF_HAVE_EXECINFO_H + constexpr const std::size_t kMaxTraceSize = 50; + constexpr const std::size_t kMaxTracePrint = 50; // Must be even. + // Buffer for the trace. + void *trace[kMaxTraceSize]; + // Get the trace. + std::size_t size = backtrace(trace, kMaxTraceSize); + // Get the trace symbols. + char **trace_symbol = backtrace_symbols(trace, size); + if (trace_symbol == nullptr) return ans; + + // Compose a human-readable backtrace string. + ans += "[ Stack-Trace: ]\n"; + if (size <= kMaxTracePrint) { + for (std::size_t i = 0; i < size; ++i) { + ans += Demangle(trace_symbol[i]) + "\n"; + } + } else { // Print out first+last (e.g.) 5. + for (std::size_t i = 0; i < kMaxTracePrint / 2; ++i) { + ans += Demangle(trace_symbol[i]) + "\n"; + } + ans += ".\n.\n.\n"; + for (std::size_t i = size - kMaxTracePrint / 2; i < size; ++i) { + ans += Demangle(trace_symbol[i]) + "\n"; + } + if (size == kMaxTraceSize) + ans += ".\n.\n.\n"; // Stack was too long, probably a bug. + } + + // We must free the array of pointers allocated by backtrace_symbols(), + // but not the strings themselves. + free(trace_symbol); +#endif // KNF_HAVE_EXECINFO_H + return ans; +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/log.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/log.h new file mode 100644 index 0000000..bd21cc3 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/log.h @@ -0,0 +1,383 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// The content in this file is copied/modified from +// https://github.com/k2-fsa/k2/blob/master/k2/csrc/log.h +#ifndef KALDI_NATIVE_FBANK_CSRC_LOG_H_ +#define KALDI_NATIVE_FBANK_CSRC_LOG_H_ + +#include + +#include // NOLINT +#include +#include + +namespace knf { + +#if KNF_ENABLE_CHECK + +#if defined(NDEBUG) +constexpr bool kDisableDebug = true; +#else +constexpr bool kDisableDebug = false; +#endif + +enum class LogLevel { + kTrace = 0, + kDebug = 1, + kInfo = 2, + kWarning = 3, + kError = 4, + kFatal = 5, // print message and abort the program +}; + +// They are used in KNF_LOG(xxx), so their names +// do not follow the google c++ code style +// +// You can use them in the following way: +// +// KNF_LOG(TRACE) << "some message"; +// KNF_LOG(DEBUG) << "some message"; +#ifndef _MSC_VER +constexpr LogLevel TRACE = LogLevel::kTrace; +constexpr LogLevel DEBUG = LogLevel::kDebug; +constexpr LogLevel INFO = LogLevel::kInfo; +constexpr LogLevel WARNING = LogLevel::kWarning; +constexpr LogLevel ERROR = LogLevel::kError; +constexpr LogLevel FATAL = LogLevel::kFatal; +#else +#define TRACE LogLevel::kTrace +#define DEBUG LogLevel::kDebug +#define INFO LogLevel::kInfo +#define WARNING LogLevel::kWarning +#define ERROR LogLevel::kError +#define FATAL LogLevel::kFatal +#endif + +std::string GetStackTrace(); + +/* Return the current log level. + + + If the current log level is TRACE, then all logged messages are printed out. + + If the current log level is DEBUG, log messages with "TRACE" level are not + shown and all other levels are printed out. + + Similarly, if the current log level is INFO, log message with "TRACE" and + "DEBUG" are not shown and all other levels are printed out. + + If it is FATAL, then only FATAL messages are shown. + */ +inline LogLevel GetCurrentLogLevel() { + static LogLevel log_level = INFO; + static std::once_flag init_flag; + std::call_once(init_flag, []() { + const char *env_log_level = std::getenv("KNF_LOG_LEVEL"); + if (env_log_level == nullptr) return; + + std::string s = env_log_level; + if (s == "TRACE") + log_level = TRACE; + else if (s == "DEBUG") + log_level = DEBUG; + else if (s == "INFO") + log_level = INFO; + else if (s == "WARNING") + log_level = WARNING; + else if (s == "ERROR") + log_level = ERROR; + else if (s == "FATAL") + log_level = FATAL; + else + fprintf(stderr, + "Unknown KNF_LOG_LEVEL: %s" + "\nSupported values are: " + "TRACE, DEBUG, INFO, WARNING, ERROR, FATAL", + s.c_str()); + }); + return log_level; +} + +inline bool EnableAbort() { + static std::once_flag init_flag; + static bool enable_abort = false; + std::call_once(init_flag, []() { + enable_abort = (std::getenv("KNF_ABORT") != nullptr); + }); + return enable_abort; +} + +class Logger { + public: + Logger(const char *filename, const char *func_name, uint32_t line_num, + LogLevel level) + : filename_(filename), + func_name_(func_name), + line_num_(line_num), + level_(level) { + cur_level_ = GetCurrentLogLevel(); + fprintf(stderr, "here\n"); + switch (level) { + case TRACE: + if (cur_level_ <= TRACE) fprintf(stderr, "[T] "); + break; + case DEBUG: + if (cur_level_ <= DEBUG) fprintf(stderr, "[D] "); + break; + case INFO: + if (cur_level_ <= INFO) fprintf(stderr, "[I] "); + break; + case WARNING: + if (cur_level_ <= WARNING) fprintf(stderr, "[W] "); + break; + case ERROR: + if (cur_level_ <= ERROR) fprintf(stderr, "[E] "); + break; + case FATAL: + if (cur_level_ <= FATAL) fprintf(stderr, "[F] "); + break; + } + + if (cur_level_ <= level_) { + fprintf(stderr, "%s:%u:%s ", filename, line_num, func_name); + } + } + + ~Logger() noexcept(false) { + static constexpr const char *kErrMsg = R"( + Some bad things happened. Please read the above error messages and stack + trace. If you are using Python, the following command may be helpful: + + gdb --args python /path/to/your/code.py + + (You can use `gdb` to debug the code. Please consider compiling + a debug version of KNF.). + + If you are unable to fix it, please open an issue at: + + https://github.com/csukuangfj/kaldi-native-fbank/issues/new + )"; + fprintf(stderr, "\n"); + if (level_ == FATAL) { + std::string stack_trace = GetStackTrace(); + if (!stack_trace.empty()) { + fprintf(stderr, "\n\n%s\n", stack_trace.c_str()); + } + + fflush(nullptr); + +#ifndef __ANDROID_API__ + if (EnableAbort()) { + // NOTE: abort() will terminate the program immediately without + // printing the Python stack backtrace. + abort(); + } + + throw std::runtime_error(kErrMsg); +#else + abort(); +#endif + } + } + + const Logger &operator<<(bool b) const { + if (cur_level_ <= level_) { + fprintf(stderr, b ? "true" : "false"); + } + return *this; + } + + const Logger &operator<<(int8_t i) const { + if (cur_level_ <= level_) fprintf(stderr, "%d", i); + return *this; + } + + const Logger &operator<<(const char *s) const { + if (cur_level_ <= level_) fprintf(stderr, "%s", s); + return *this; + } + + const Logger &operator<<(int32_t i) const { + if (cur_level_ <= level_) fprintf(stderr, "%d", i); + return *this; + } + + const Logger &operator<<(uint32_t i) const { + if (cur_level_ <= level_) fprintf(stderr, "%u", i); + return *this; + } + + const Logger &operator<<(uint64_t i) const { + if (cur_level_ <= level_) + fprintf(stderr, "%llu", (long long unsigned int)i); // NOLINT + return *this; + } + + const Logger &operator<<(int64_t i) const { + if (cur_level_ <= level_) + fprintf(stderr, "%lli", (long long int)i); // NOLINT + return *this; + } + + const Logger &operator<<(float f) const { + if (cur_level_ <= level_) fprintf(stderr, "%f", f); + return *this; + } + + const Logger &operator<<(double d) const { + if (cur_level_ <= level_) fprintf(stderr, "%f", d); + return *this; + } + + template + const Logger &operator<<(const T &t) const { + // require T overloads operator<< + std::ostringstream os; + os << t; + return *this << os.str().c_str(); + } + + // specialization to fix compile error: `stringstream << nullptr` is ambiguous + const Logger &operator<<(const std::nullptr_t &null) const { + if (cur_level_ <= level_) *this << "(null)"; + return *this; + } + + private: + const char *filename_; + const char *func_name_; + uint32_t line_num_; + LogLevel level_; + LogLevel cur_level_; +}; +#endif // KNF_ENABLE_CHECK + +class Voidifier { + public: +#if KNF_ENABLE_CHECK + void operator&(const Logger &) const {} +#endif +}; +#if !defined(KNF_ENABLE_CHECK) +template +const Voidifier &operator<<(const Voidifier &v, T &&) { + return v; +} +#endif + +} // namespace knf + +#define KNF_STATIC_ASSERT(x) static_assert(x, "") + +#ifdef KNF_ENABLE_CHECK + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) || \ + defined(__PRETTY_FUNCTION__) +// for clang and GCC +#define KNF_FUNC __PRETTY_FUNCTION__ +#else +// for other compilers +#define KNF_FUNC __func__ +#endif + +#define KNF_CHECK(x) \ + (x) ? (void)0 \ + : ::knf::Voidifier() & \ + ::knf::Logger(__FILE__, KNF_FUNC, __LINE__, ::knf::FATAL) \ + << "Check failed: " << #x << " " + +// WARNING: x and y may be evaluated multiple times, but this happens only +// when the check fails. Since the program aborts if it fails, we don't think +// the extra evaluation of x and y matters. +// +// CAUTION: we recommend the following use case: +// +// auto x = Foo(); +// auto y = Bar(); +// KNF_CHECK_EQ(x, y) << "Some message"; +// +// And please avoid +// +// KNF_CHECK_EQ(Foo(), Bar()); +// +// if `Foo()` or `Bar()` causes some side effects, e.g., changing some +// local static variables or global variables. +#define _KNF_CHECK_OP(x, y, op) \ + ((x)op(y)) ? (void)0 \ + : ::knf::Voidifier() & \ + ::knf::Logger(__FILE__, KNF_FUNC, __LINE__, ::knf::FATAL) \ + << "Check failed: " << #x << " " << #op << " " << #y \ + << " (" << (x) << " vs. " << (y) << ") " + +#define KNF_CHECK_EQ(x, y) _KNF_CHECK_OP(x, y, ==) +#define KNF_CHECK_NE(x, y) _KNF_CHECK_OP(x, y, !=) +#define KNF_CHECK_LT(x, y) _KNF_CHECK_OP(x, y, <) +#define KNF_CHECK_LE(x, y) _KNF_CHECK_OP(x, y, <=) +#define KNF_CHECK_GT(x, y) _KNF_CHECK_OP(x, y, >) +#define KNF_CHECK_GE(x, y) _KNF_CHECK_OP(x, y, >=) + +#define KNF_LOG(x) ::knf::Logger(__FILE__, KNF_FUNC, __LINE__, ::knf::x) + +// ------------------------------------------------------------ +// For debug check +// ------------------------------------------------------------ +// If you define the macro "-D NDEBUG" while compiling kaldi-native-fbank, +// the following macros are in fact empty and does nothing. + +#define KNF_DCHECK(x) ::knf::kDisableDebug ? (void)0 : KNF_CHECK(x) + +#define KNF_DCHECK_EQ(x, y) ::knf::kDisableDebug ? (void)0 : KNF_CHECK_EQ(x, y) + +#define KNF_DCHECK_NE(x, y) ::knf::kDisableDebug ? (void)0 : KNF_CHECK_NE(x, y) + +#define KNF_DCHECK_LT(x, y) ::knf::kDisableDebug ? (void)0 : KNF_CHECK_LT(x, y) + +#define KNF_DCHECK_LE(x, y) ::knf::kDisableDebug ? (void)0 : KNF_CHECK_LE(x, y) + +#define KNF_DCHECK_GT(x, y) ::knf::kDisableDebug ? (void)0 : KNF_CHECK_GT(x, y) + +#define KNF_DCHECK_GE(x, y) ::knf::kDisableDebug ? (void)0 : KNF_CHECK_GE(x, y) + +#define KNF_DLOG(x) \ + ::knf::kDisableDebug ? (void)0 : ::knf::Voidifier() & KNF_LOG(x) + +#else + +#define KNF_CHECK(x) ::knf::Voidifier() +#define KNF_LOG(x) ::knf::Voidifier() + +#define KNF_CHECK_EQ(x, y) ::knf::Voidifier() +#define KNF_CHECK_NE(x, y) ::knf::Voidifier() +#define KNF_CHECK_LT(x, y) ::knf::Voidifier() +#define KNF_CHECK_LE(x, y) ::knf::Voidifier() +#define KNF_CHECK_GT(x, y) ::knf::Voidifier() +#define KNF_CHECK_GE(x, y) ::knf::Voidifier() + +#define KNF_DCHECK(x) ::knf::Voidifier() +#define KNF_DLOG(x) ::knf::Voidifier() +#define KNF_DCHECK_EQ(x, y) ::knf::Voidifier() +#define KNF_DCHECK_NE(x, y) ::knf::Voidifier() +#define KNF_DCHECK_LT(x, y) ::knf::Voidifier() +#define KNF_DCHECK_LE(x, y) ::knf::Voidifier() +#define KNF_DCHECK_GT(x, y) ::knf::Voidifier() +#define KNF_DCHECK_GE(x, y) ::knf::Voidifier() + +#endif // KNF_CHECK_NE + +#endif // KALDI_NATIVE_FBANK_CSRC_LOG_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/mel-computations.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/mel-computations.cc new file mode 100644 index 0000000..c083a56 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/mel-computations.cc @@ -0,0 +1,422 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is copied/modified from kaldi/src/feat/mel-computations.cc + +#include "kaldi-native-fbank/csrc/mel-computations.h" + +#include + +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/kaldi-math.h" +#include "kaldi-native-fbank/csrc/log.h" + +namespace knf { + +std::ostream &operator<<(std::ostream &os, const MelBanksOptions &opts) { + os << opts.ToString(); + return os; +} + +float MelBanks::VtlnWarpFreq( + float vtln_low_cutoff, // upper+lower frequency cutoffs for VTLN. + float vtln_high_cutoff, + float low_freq, // upper+lower frequency cutoffs in mel computation + float high_freq, float vtln_warp_factor, float freq) { + /// This computes a VTLN warping function that is not the same as HTK's one, + /// but has similar inputs (this function has the advantage of never producing + /// empty bins). + + /// This function computes a warp function F(freq), defined between low_freq + /// and high_freq inclusive, with the following properties: + /// F(low_freq) == low_freq + /// F(high_freq) == high_freq + /// The function is continuous and piecewise linear with two inflection + /// points. + /// The lower inflection point (measured in terms of the unwarped + /// frequency) is at frequency l, determined as described below. + /// The higher inflection point is at a frequency h, determined as + /// described below. + /// If l <= f <= h, then F(f) = f/vtln_warp_factor. + /// If the higher inflection point (measured in terms of the unwarped + /// frequency) is at h, then max(h, F(h)) == vtln_high_cutoff. + /// Since (by the last point) F(h) == h/vtln_warp_factor, then + /// max(h, h/vtln_warp_factor) == vtln_high_cutoff, so + /// h = vtln_high_cutoff / max(1, 1/vtln_warp_factor). + /// = vtln_high_cutoff * min(1, vtln_warp_factor). + /// If the lower inflection point (measured in terms of the unwarped + /// frequency) is at l, then min(l, F(l)) == vtln_low_cutoff + /// This implies that l = vtln_low_cutoff / min(1, 1/vtln_warp_factor) + /// = vtln_low_cutoff * max(1, vtln_warp_factor) + + if (freq < low_freq || freq > high_freq) + return freq; // in case this gets called + // for out-of-range frequencies, just return the freq. + + KNF_CHECK_GT(vtln_low_cutoff, low_freq); + KNF_CHECK_LT(vtln_high_cutoff, high_freq); + + float one = 1.0f; + float l = vtln_low_cutoff * std::max(one, vtln_warp_factor); + float h = vtln_high_cutoff * std::min(one, vtln_warp_factor); + float scale = 1.0f / vtln_warp_factor; + float Fl = scale * l; // F(l); + float Fh = scale * h; // F(h); + KNF_CHECK(l > low_freq && h < high_freq); + // slope of left part of the 3-piece linear function + float scale_left = (Fl - low_freq) / (l - low_freq); + // [slope of center part is just "scale"] + + // slope of right part of the 3-piece linear function + float scale_right = (high_freq - Fh) / (high_freq - h); + + if (freq < l) { + return low_freq + scale_left * (freq - low_freq); + } else if (freq < h) { + return scale * freq; + } else { // freq >= h + return high_freq + scale_right * (freq - high_freq); + } +} + +float MelBanks::VtlnWarpMelFreq( + float vtln_low_cutoff, // upper+lower frequency cutoffs for VTLN. + float vtln_high_cutoff, + float low_freq, // upper+lower frequency cutoffs in mel computation + float high_freq, float vtln_warp_factor, float mel_freq) { + return MelScale(VtlnWarpFreq(vtln_low_cutoff, vtln_high_cutoff, low_freq, + high_freq, vtln_warp_factor, + InverseMelScale(mel_freq))); +} + +MelBanks::MelBanks(const MelBanksOptions &opts, + const FrameExtractionOptions &frame_opts, + float vtln_warp_factor) { + if (opts.is_librosa) { + InitLibrosaMelBanks(opts, frame_opts, vtln_warp_factor); + } else { + InitKaldiMelBanks(opts, frame_opts, vtln_warp_factor); + } +} + +void MelBanks::InitKaldiMelBanks(const MelBanksOptions &opts, + const FrameExtractionOptions &frame_opts, + float vtln_warp_factor) { + htk_mode_ = opts.htk_mode; + int32_t num_bins = opts.num_bins; + if (num_bins < 3) { + KNF_LOG(FATAL) << "Must have at least 3 mel bins"; + } + + float sample_freq = frame_opts.samp_freq; + int32_t window_length_padded = frame_opts.PaddedWindowSize(); + KNF_CHECK_EQ(window_length_padded % 2, 0); + + int32_t num_fft_bins = window_length_padded / 2; + float nyquist = 0.5f * sample_freq; + + float low_freq = opts.low_freq, high_freq; + if (opts.high_freq > 0.0f) { + high_freq = opts.high_freq; + } else { + high_freq = nyquist + opts.high_freq; + } + + if (low_freq < 0.0f || low_freq >= nyquist || high_freq <= 0.0f || + high_freq > nyquist || high_freq <= low_freq) { + KNF_LOG(FATAL) << "Bad values in options: low-freq " << low_freq + << " and high-freq " << high_freq << " vs. nyquist " + << nyquist; + } + + float fft_bin_width = sample_freq / window_length_padded; + // fft-bin width [think of it as Nyquist-freq / half-window-length] + + float mel_low_freq = MelScale(low_freq); + float mel_high_freq = MelScale(high_freq); + + debug_ = opts.debug_mel; + + // divide by num_bins+1 in next line because of end-effects where the bins + // spread out to the sides. + float mel_freq_delta = (mel_high_freq - mel_low_freq) / (num_bins + 1); + + float vtln_low = opts.vtln_low, vtln_high = opts.vtln_high; + if (vtln_high < 0.0f) { + vtln_high += nyquist; + } + + if (vtln_warp_factor != 1.0f && + (vtln_low < 0.0f || vtln_low <= low_freq || vtln_low >= high_freq || + vtln_high <= 0.0f || vtln_high >= high_freq || vtln_high <= vtln_low)) { + KNF_LOG(FATAL) << "Bad values in options: vtln-low " << vtln_low + << " and vtln-high " << vtln_high << ", versus " + << "low-freq " << low_freq << " and high-freq " << high_freq; + } + + bins_.resize(num_bins); + + for (int32_t bin = 0; bin < num_bins; ++bin) { + float left_mel = mel_low_freq + bin * mel_freq_delta, + center_mel = mel_low_freq + (bin + 1) * mel_freq_delta, + right_mel = mel_low_freq + (bin + 2) * mel_freq_delta; + + if (vtln_warp_factor != 1.0f) { + left_mel = VtlnWarpMelFreq(vtln_low, vtln_high, low_freq, high_freq, + vtln_warp_factor, left_mel); + center_mel = VtlnWarpMelFreq(vtln_low, vtln_high, low_freq, high_freq, + vtln_warp_factor, center_mel); + right_mel = VtlnWarpMelFreq(vtln_low, vtln_high, low_freq, high_freq, + vtln_warp_factor, right_mel); + } + + // this_bin will be a vector of coefficients that is only + // nonzero where this mel bin is active. + std::vector this_bin(num_fft_bins); + + int32_t first_index = -1, last_index = -1; + for (int32_t i = 0; i < num_fft_bins; ++i) { + float freq = (fft_bin_width * i); // Center frequency of this fft + // bin. + float mel = MelScale(freq); + if (mel > left_mel && mel < right_mel) { + float weight; + if (mel <= center_mel) { + weight = (mel - left_mel) / (center_mel - left_mel); + } else { + weight = (right_mel - mel) / (right_mel - center_mel); + } + this_bin[i] = weight; + if (first_index == -1) { + first_index = i; + } + last_index = i; + } + } + KNF_CHECK(first_index != -1 && last_index >= first_index && + "You may have set num_mel_bins too large."); + + bins_[bin].first = first_index; + int32_t size = last_index + 1 - first_index; + bins_[bin].second.insert(bins_[bin].second.end(), + this_bin.begin() + first_index, + this_bin.begin() + first_index + size); + + // Replicate a bug in HTK, for testing purposes. + if (opts.htk_mode && bin == 0 && mel_low_freq != 0.0f) { + bins_[bin].second[0] = 0.0; + } + } // for (int32_t bin = 0; bin < num_bins; ++bin) { + + if (debug_) { + std::ostringstream os; + for (size_t i = 0; i < bins_.size(); i++) { + os << "bin " << i << ", offset = " << bins_[i].first << ", vec = "; + for (auto k : bins_[i].second) os << k << ", "; + os << "\n"; + } + KNF_LOG(INFO) << os.str(); + } +} + +void MelBanks::InitLibrosaMelBanks(const MelBanksOptions &opts, + const FrameExtractionOptions &frame_opts, + float vtln_warp_factor) { + htk_mode_ = opts.htk_mode; + int32_t num_bins = opts.num_bins; + if (num_bins < 3) { + KNF_LOG(FATAL) << "Must have at least 3 mel bins"; + } + + float sample_freq = frame_opts.samp_freq; + int32_t window_length_padded = frame_opts.PaddedWindowSize(); + KNF_CHECK_EQ(window_length_padded % 2, 0); + + int32_t num_fft_bins = window_length_padded / 2; + float nyquist = 0.5f * sample_freq; + + float low_freq = opts.low_freq, high_freq; + if (opts.high_freq > 0.0f) { + high_freq = opts.high_freq; + } else { + high_freq = nyquist + opts.high_freq; + } + + if (low_freq < 0.0f || low_freq >= nyquist || high_freq <= 0.0f || + high_freq > nyquist || high_freq <= low_freq) { + KNF_LOG(FATAL) << "Bad values in options: low-freq " << low_freq + << " and high-freq " << high_freq << " vs. nyquist " + << nyquist; + } + + float fft_bin_width = sample_freq / window_length_padded; + + float mel_low_freq = MelScaleSlaney(low_freq); + float mel_high_freq = MelScaleSlaney(high_freq); + + debug_ = opts.debug_mel; + + // divide by num_bins+1 in next line because of end-effects where the bins + // spread out to the sides. + float mel_freq_delta = (mel_high_freq - mel_low_freq) / (num_bins + 1); + + bool slaney_norm = false; + if (!opts.norm.empty()) { + if (opts.norm != "slaney") { + KNF_LOG(FATAL) << "Unsupported norm: " << opts.norm; + } + slaney_norm = true; + } + + bins_.resize(num_bins); + for (int32_t bin = 0; bin < num_bins; ++bin) { + float left_mel = mel_low_freq + bin * mel_freq_delta; + float center_mel = mel_low_freq + (bin + 1) * mel_freq_delta; + float right_mel = mel_low_freq + (bin + 2) * mel_freq_delta; + + float left_hz = InverseMelScaleSlaney(left_mel); + float center_hz = InverseMelScaleSlaney(center_mel); + float right_hz = InverseMelScaleSlaney(right_mel); + + // this_bin will be a vector of coefficients that is only + // nonzero where this mel bin is active. + // + // It is not an error to use num_fft_bins + 1 here. It is different + // from Kaldi. + std::vector this_bin(num_fft_bins + 1); + + int32_t first_index = -1, last_index = -1; + for (int32_t i = 0; i < num_fft_bins + 1; ++i) { + float hz = (fft_bin_width * i); // Center frequency of this fft bin. + if (hz > left_hz && hz < right_hz) { + float weight; + if (hz <= center_hz) { + weight = (hz - left_hz) / (center_hz - left_hz); + } else { + weight = (right_hz - hz) / (right_hz - center_hz); + } + + if (slaney_norm) { + weight *= 2 / (right_hz - left_hz); + } + + this_bin[i] = weight; + if (first_index == -1) { + first_index = i; + } + + last_index = i; + } + } // for (int32_t i = 0; i < num_fft_bins + 1; ++i) + + KNF_CHECK(first_index != -1 && last_index >= first_index && + "You may have set num_mel_bins too large."); + + bins_[bin].first = first_index; + int32_t size = last_index + 1 - first_index; + bins_[bin].second.insert(bins_[bin].second.end(), + this_bin.begin() + first_index, + this_bin.begin() + first_index + size); + } // for (int32_t bin = 0; bin < num_bins; ++bin) + + if (debug_) { + std::ostringstream os; + for (size_t i = 0; i < bins_.size(); i++) { + os << "bin " << i << ", offset = " << bins_[i].first << ", vec = "; + for (auto k : bins_[i].second) os << k << ", "; + os << "\n"; + } + fprintf(stderr, "%s\n", os.str().c_str()); + } +} + +MelBanks::MelBanks(const float *weights, int32_t num_rows, int32_t num_cols) + : debug_(false), htk_mode_(false) { + bins_.resize(num_rows); + for (int32_t bin = 0; bin < num_rows; ++bin) { + const float *this_bin = weights + bin * num_cols; + + int32_t first_index = -1, last_index = -1; + + for (int32_t i = 0; i < num_cols; ++i) { + if (this_bin[i] == 0) { + continue; + } + if (first_index == -1) first_index = i; + last_index = i; + } + + KNF_CHECK(first_index != -1 && last_index >= first_index && + "You have an incorrect weight matrix."); + + bins_[bin].first = first_index; + int32_t size = last_index + 1 - first_index; + + bins_[bin].second.insert(bins_[bin].second.end(), this_bin + first_index, + this_bin + first_index + size); + } +} + +// "power_spectrum" contains fft energies. +void MelBanks::Compute(const float *power_spectrum, + float *mel_energies_out) const { + int32_t num_bins = bins_.size(); + + for (int32_t i = 0; i < num_bins; i++) { + int32_t offset = bins_[i].first; + const auto &v = bins_[i].second; + float energy = 0; + for (int32_t k = 0; k != v.size(); ++k) { + energy += v[k] * power_spectrum[k + offset]; + } + + // HTK-like flooring- for testing purposes (we prefer dither) + if (htk_mode_ && energy < 1.0) { + energy = 1.0; + } + + mel_energies_out[i] = energy; + + // The following assert was added due to a problem with OpenBlas that + // we had at one point (it was a bug in that library). Just to detect + // it early. + KNF_CHECK_EQ(energy, energy); // check that energy is not nan + } + + if (debug_) { + fprintf(stderr, "MEL BANKS:\n"); + for (int32_t i = 0; i < num_bins; i++) + fprintf(stderr, " %f", mel_energies_out[i]); + fprintf(stderr, "\n"); + } +} + +void ComputeLifterCoeffs(float Q, std::vector *coeffs) { + // Compute liftering coefficients (scaling on cepstral coeffs) + // coeffs are numbered slightly differently from HTK: the zeroth + // index is C0, which is not affected. + for (int32_t i = 0; i != static_cast(coeffs->size()); ++i) { + (*coeffs)[i] = 1.0 + 0.5 * Q * sin(M_PI * i / Q); + } +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/mel-computations.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/mel-computations.h new file mode 100644 index 0000000..c81ce97 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/mel-computations.h @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// This file is copied/modified from kaldi/src/feat/mel-computations.h +#ifndef KALDI_NATIVE_FBANK_CSRC_MEL_COMPUTATIONS_H_ +#define KALDI_NATIVE_FBANK_CSRC_MEL_COMPUTATIONS_H_ + +#include +#include +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-window.h" + +namespace knf { +struct FrameExtractionOptions; + +struct MelBanksOptions { + int32_t num_bins = 25; // e.g. 25; number of triangular bins + float low_freq = 20; // e.g. 20; lower frequency cutoff + + // an upper frequency cutoff; 0 -> no cutoff, negative + // ->added to the Nyquist frequency to get the cutoff. + float high_freq = 0; + + float vtln_low = 100; // vtln lower cutoff of warping function. + + // vtln upper cutoff of warping function: if negative, added + // to the Nyquist frequency to get the cutoff. + float vtln_high = -500; + + bool debug_mel = false; + // htk_mode is a "hidden" config, it does not show up on command line. + // Enables more exact compatibility with HTK, for testing purposes. Affects + // mel-energy flooring and reproduces a bug in HTK. + bool htk_mode = false; + + // Note that if you set is_librosa, you probably need to set + // low_freq to 0. + // Please see + // https://librosa.org/doc/main/generated/librosa.filters.mel.html + bool is_librosa = false; + + // used only when is_librosa=true + // Possible values: "", slaney. We don't support a numeric value here, but + // it can be added on demand. + // See https://librosa.org/doc/main/generated/librosa.filters.mel.html + std::string norm = "slaney"; + + std::string ToString() const { + std::ostringstream os; + os << "num_bins: " << num_bins << "\n"; + os << "low_freq: " << low_freq << "\n"; + os << "high_freq: " << high_freq << "\n"; + os << "vtln_low: " << vtln_low << "\n"; + os << "vtln_high: " << vtln_high << "\n"; + os << "debug_mel: " << debug_mel << "\n"; + os << "htk_mode: " << htk_mode << "\n"; + os << "is_librosa: " << is_librosa << "\n"; + os << "norm: " << norm << "\n"; + return os.str(); + } +}; + +std::ostream &operator<<(std::ostream &os, const MelBanksOptions &opts); + +class MelBanks { + public: + // see also https://en.wikipedia.org/wiki/Mel_scale + // htk, mel to hz + static inline float InverseMelScale(float mel_freq) { + return 700.0f * (expf(mel_freq / 1127.0f) - 1.0f); + } + + // htk, hz to mel + static inline float MelScale(float freq) { + return 1127.0f * logf(1.0f + freq / 700.0f); + } + + // slaney, mel to hz + static inline float InverseMelScaleSlaney(float mel_freq) { + if (mel_freq <= 15) { + return 200.0f / 3 * mel_freq; + } + + // return 1000 * expf((mel_freq - 15) * logf(6.4f) / 27); + + // Note: log(6.4)/27 = 0.06875177742094911 + + return 1000 * expf((mel_freq - 15) * 0.06875177742094911f); + } + + // slaney, hz to mel + static inline float MelScaleSlaney(float freq) { + if (freq <= 1000) { + return freq * 3 / 200.0f; + } + + // return 15 + 27 * logf(freq / 1000) / logf(6.4f) + // + // Note: 27/log(6.4) = 14.545078505785561 + + return 15 + 14.545078505785561f * logf(freq / 1000); + } + + static float VtlnWarpFreq( + float vtln_low_cutoff, + float vtln_high_cutoff, // discontinuities in warp func + float low_freq, + float high_freq, // upper+lower frequency cutoffs in + // the mel computation + float vtln_warp_factor, float freq); + + static float VtlnWarpMelFreq(float vtln_low_cutoff, float vtln_high_cutoff, + float low_freq, float high_freq, + float vtln_warp_factor, float mel_freq); + + // TODO(fangjun): Remove vtln_warp_factor + MelBanks(const MelBanksOptions &opts, + const FrameExtractionOptions &frame_opts, float vtln_warp_factor); + + // Initialize with a 2-d weights matrix + // @param weights Pointer to the start address of the matrix + // @param num_rows It equls to number of mel bins + // @param num_cols It equals to (number of fft bins)/2+1 + MelBanks(const float *weights, int32_t num_rows, int32_t num_cols); + + /// Compute Mel energies (note: not log energies). + /// At input, "fft_energies" contains the FFT energies (not log). + /// + /// @param fft_energies 1-D array of size num_fft_bins/2+1 + /// @param mel_energies_out 1-D array of size num_mel_bins + void Compute(const float *fft_energies, float *mel_energies_out) const; + + int32_t NumBins() const { return bins_.size(); } + + private: + // for kaldi-compatible + void InitKaldiMelBanks(const MelBanksOptions &opts, + const FrameExtractionOptions &frame_opts, + float vtln_warp_factor); + + // for librosa-compatible + // See https://librosa.org/doc/main/generated/librosa.filters.mel.html + void InitLibrosaMelBanks(const MelBanksOptions &opts, + const FrameExtractionOptions &frame_opts, + float vtln_warp_factor); + + private: + // the "bins_" vector is a vector, one for each bin, of a pair: + // (the first nonzero fft-bin), (the vector of weights). + std::vector>> bins_; + + // TODO(fangjun): Remove debug_ and htk_mode_ + bool debug_ = false; + bool htk_mode_ = false; +}; + +// Compute liftering coefficients (scaling on cepstral coeffs) +// coeffs are numbered slightly differently from HTK: the zeroth +// index is C0, which is not affected. +void ComputeLifterCoeffs(float Q, std::vector *coeffs); + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_CSRC_MEL_COMPUTATIONS_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/online-feature.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/online-feature.cc new file mode 100644 index 0000000..13c4369 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/online-feature.cc @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// The content in this file is copied/modified from +// This file is copied/modified from kaldi/src/feat/online-feature.cc + +#include "kaldi-native-fbank/csrc/online-feature.h" + +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/log.h" + +namespace knf { + +RecyclingVector::RecyclingVector(int32_t items_to_hold) + : items_to_hold_(items_to_hold == 0 ? -1 : items_to_hold), + first_available_index_(0) {} + +const float *RecyclingVector::At(int32_t index) const { + if (index < first_available_index_) { + KNF_LOG(FATAL) << "Attempted to retrieve feature vector that was " + "already removed by the RecyclingVector (index = " + << index << "; " + << "first_available_index = " << first_available_index_ + << "; " + << "size = " << Size() << ")"; + } + // 'at' does size checking. + return items_.at(index - first_available_index_).data(); +} + +void RecyclingVector::PushBack(std::vector item) { + // Note: -1 is a larger number when treated as unsigned + if (items_.size() == static_cast(items_to_hold_)) { + items_.pop_front(); + ++first_available_index_; + } + items_.push_back(std::move(item)); +} + +int32_t RecyclingVector::Size() const { + return first_available_index_ + static_cast(items_.size()); +} + +// discard the first n frames +void RecyclingVector::Pop(int32_t n) { + for (int32_t i = 0; i < n && !items_.empty(); ++i) { + items_.pop_front(); + ++first_available_index_; + } +} + +template +OnlineGenericBaseFeature::OnlineGenericBaseFeature( + const typename C::Options &opts) + : computer_(opts), + window_function_(computer_.GetFrameOptions()), + input_finished_(false), + waveform_offset_(0) {} + +template +void OnlineGenericBaseFeature::AcceptWaveform(float sampling_rate, + const float *waveform, + int32_t n) { + if (n == 0) { + return; // Nothing to do. + } + + if (input_finished_) { + KNF_LOG(FATAL) << "AcceptWaveform called after InputFinished() was called."; + } + + KNF_CHECK_EQ(sampling_rate, computer_.GetFrameOptions().samp_freq); + + waveform_remainder_.insert(waveform_remainder_.end(), waveform, waveform + n); + + ComputeFeatures(); +} + +template +void OnlineGenericBaseFeature::InputFinished() { + input_finished_ = true; + ComputeFeatures(); +} + +template +void OnlineGenericBaseFeature::ComputeFeatures() { + const FrameExtractionOptions &frame_opts = computer_.GetFrameOptions(); + + int64_t num_samples_total = waveform_offset_ + waveform_remainder_.size(); + + int32_t num_frames_old = features_.Size(); + + int32_t num_frames_new = + NumFrames(num_samples_total, frame_opts, input_finished_); + + KNF_CHECK_GE(num_frames_new, num_frames_old); + + // note: this online feature-extraction code does not support VTLN. + float vtln_warp = 1.0; + + std::vector window; + bool need_raw_log_energy = computer_.NeedRawLogEnergy(); + + for (int32_t frame = num_frames_old; frame < num_frames_new; ++frame) { + std::fill(window.begin(), window.end(), 0); + float raw_log_energy = 0.0; + ExtractWindow(waveform_offset_, waveform_remainder_, frame, frame_opts, + window_function_, &window, + need_raw_log_energy ? &raw_log_energy : nullptr); + + std::vector this_feature(computer_.Dim()); + + computer_.Compute(raw_log_energy, vtln_warp, &window, this_feature.data()); + features_.PushBack(std::move(this_feature)); + } + + // OK, we will now discard any portion of the signal that will not be + // necessary to compute frames in the future. + int64_t first_sample_of_next_frame = + FirstSampleOfFrame(num_frames_new, frame_opts); + + int32_t samples_to_discard = first_sample_of_next_frame - waveform_offset_; + + if (samples_to_discard > 0) { + // discard the leftmost part of the waveform that we no longer need. + int32_t new_num_samples = + static_cast(waveform_remainder_.size()) - samples_to_discard; + + if (new_num_samples <= 0) { + // odd, but we'll try to handle it. + waveform_offset_ += waveform_remainder_.size(); + waveform_remainder_.resize(0); + } else { + std::vector new_remainder(new_num_samples); + + std::copy(waveform_remainder_.begin() + samples_to_discard, + waveform_remainder_.end(), new_remainder.begin()); + waveform_offset_ += samples_to_discard; + + waveform_remainder_.swap(new_remainder); + } + } +} + +template class OnlineGenericBaseFeature; +template class OnlineGenericBaseFeature; +template class OnlineGenericBaseFeature; + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/online-feature.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/online-feature.h new file mode 100644 index 0000000..3d42fd4 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/online-feature.h @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// The content in this file is copied/modified from +// This file is copied/modified from kaldi/src/feat/online-feature.h +#ifndef KALDI_NATIVE_FBANK_CSRC_ONLINE_FEATURE_H_ +#define KALDI_NATIVE_FBANK_CSRC_ONLINE_FEATURE_H_ + +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-fbank.h" +#include "kaldi-native-fbank/csrc/feature-mfcc.h" +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/whisper-feature.h" + +namespace knf { + +/// This class serves as a storage for feature vectors with an option to limit +/// the memory usage by removing old elements. The deleted frames indices are +/// "remembered" so that regardless of the MAX_ITEMS setting, the user always +/// provides the indices as if no deletion was being performed. +/// This is useful when processing very long recordings which would otherwise +/// cause the memory to eventually blow up when the features are not being +/// removed. +class RecyclingVector { + public: + /// By default it does not remove any elements. + explicit RecyclingVector(int32_t items_to_hold = -1); + + ~RecyclingVector() = default; + RecyclingVector(const RecyclingVector &) = delete; + RecyclingVector &operator=(const RecyclingVector &) = delete; + + // The pointer is owned by RecyclingVector + // Users should not free it + const float *At(int32_t index) const; + + void PushBack(std::vector item); + + /// This method returns the size as if no "recycling" had happened, + /// i.e. equivalent to the number of times the PushBack method has been + /// called. + int32_t Size() const; + + // discard the first n frames + void Pop(int32_t n); + + private: + std::deque> items_; + int32_t items_to_hold_; + int32_t first_available_index_; +}; + +/// This is a templated class for online feature extraction; +/// it's templated on a class like MfccComputer or PlpComputer +/// that does the basic feature extraction. +template +class OnlineGenericBaseFeature { + public: + // Constructor from options class + explicit OnlineGenericBaseFeature(const typename C::Options &opts); + + int32_t Dim() const { return computer_.Dim(); } + + float FrameShiftInSeconds() const { + return computer_.GetFrameOptions().frame_shift_ms / 1000.0f; + } + + int32_t NumFramesReady() const { return features_.Size(); } + + // Note: IsLastFrame() will only ever return true if you have called + // InputFinished() (and this frame is the last frame). + bool IsLastFrame(int32_t frame) const { + return input_finished_ && frame == NumFramesReady() - 1; + } + + const float *GetFrame(int32_t frame) const { return features_.At(frame); } + + // This would be called from the application, when you get + // more wave data. Note: the sampling_rate is only provided so + // the code can assert that it matches the sampling rate + // expected in the options. + // + // @param sampling_rate The sampling_rate of the input waveform + // @param waveform Pointer to a 1-D array of size n + // @param n Number of entries in waveform + void AcceptWaveform(float sampling_rate, const float *waveform, int32_t n); + + // InputFinished() tells the class you won't be providing any + // more waveform. This will help flush out the last frame or two + // of features, in the case where snip-edges == false; it also + // affects the return value of IsLastFrame(). + void InputFinished(); + + // discard the first n frames + void Pop(int32_t n) { features_.Pop(n); } + + private: + // This function computes any additional feature frames that it is possible to + // compute from 'waveform_remainder_', which at this point may contain more + // than just a remainder-sized quantity (because AcceptWaveform() appends to + // waveform_remainder_ before calling this function). It adds these feature + // frames to features_, and shifts off any now-unneeded samples of input from + // waveform_remainder_ while incrementing waveform_offset_ by the same amount. + void ComputeFeatures(); + + C computer_; // class that does the MFCC or PLP or filterbank computation + + FeatureWindowFunction window_function_; + + // features_ is the Mfcc or Plp or Fbank features that we have already + // computed. + + RecyclingVector features_; + + // True if the user has called "InputFinished()" + bool input_finished_; + + // waveform_offset_ is the number of samples of waveform that we have + // already discarded, i.e. that were prior to 'waveform_remainder_'. + int64_t waveform_offset_; + + // waveform_remainder_ is a short piece of waveform that we may need to keep + // after extracting all the whole frames we can (whatever length of feature + // will be required for the next phase of computation). + // It is a 1-D tensor + std::vector waveform_remainder_; +}; + +using OnlineFbank = OnlineGenericBaseFeature; +using OnlineMfcc = OnlineGenericBaseFeature; +using OnlineWhisperFbank = OnlineGenericBaseFeature; + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_CSRC_ONLINE_FEATURE_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/rfft.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/rfft.cc new file mode 100644 index 0000000..619a607 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/rfft.cc @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/csrc/rfft.h" + +#include +#include +#include + +#include "kaldi-native-fbank/csrc/log.h" + +namespace knf { + +// see fftsg.cc +void rdft(int n, int isgn, double *a, int *ip, double *w); + +class Rfft::RfftImpl { + public: + explicit RfftImpl(int32_t n) : n_(n), ip_(2 + std::sqrt(n / 2)), w_(n / 2) { + KNF_CHECK_EQ(n & (n - 1), 0); + } + + void Compute(float *in_out) { + std::vector d(in_out, in_out + n_); + + Compute(d.data()); + + std::copy(d.begin(), d.end(), in_out); + } + + void Compute(double *in_out) { + // 1 means forward fft + rdft(n_, 1, in_out, ip_.data(), w_.data()); + } + + private: + int32_t n_; + std::vector ip_; + std::vector w_; +}; + +Rfft::Rfft(int32_t n) : impl_(std::make_unique(n)) {} + +Rfft::~Rfft() = default; + +void Rfft::Compute(float *in_out) { impl_->Compute(in_out); } +void Rfft::Compute(double *in_out) { impl_->Compute(in_out); } + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/rfft.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/rfft.h new file mode 100644 index 0000000..d4652f8 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/rfft.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_CSRC_RFFT_H_ +#define KALDI_NATIVE_FBANK_CSRC_RFFT_H_ + +#include +#include + +namespace knf { + +// n-point Real discrete Fourier transform +// where n is a power of 2. n >= 2 +// +// R[k] = sum_j=0^n-1 in[j]*cos(2*pi*j*k/n), 0<=k<=n/2 +// I[k] = sum_j=0^n-1 in[j]*sin(2*pi*j*k/n), 0 impl_; +}; + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_CSRC_RFFT_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-log.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-log.cc new file mode 100644 index 0000000..1e6ef73 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-log.cc @@ -0,0 +1,71 @@ +/** + * Copyright 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if KNF_ENABLE_CHECK +#include "gtest/gtest.h" +#include "kaldi-native-fbank/csrc/log.h" + +namespace knf { + +TEST(Log, TestLog) { + KNF_LOG(TRACE) << "this is a trace message"; + KNF_LOG(DEBUG) << "this is a debug message"; + KNF_LOG(INFO) << "this is an info message"; + KNF_LOG(WARNING) << "this is a warning message"; + KNF_LOG(ERROR) << "this is an error message"; + + ASSERT_THROW(KNF_LOG(FATAL) << "This will crash the program", + std::runtime_error); + + // For debug build + + KNF_DLOG(TRACE) << "this is a trace message for debug build"; + KNF_DLOG(DEBUG) << "this is a trace message for debug build"; + KNF_DLOG(INFO) << "this is a trace message for debug build"; + KNF_DLOG(ERROR) << "this is an error message for debug build"; + KNF_DLOG(WARNING) << "this is a trace message for debug build"; + +#if !defined(NDEBUG) + ASSERT_THROW(KNF_DLOG(FATAL) << "this is a trace message for debug build", + std::runtime_error); +#endif +} + +TEST(Log, TestCheck) { + KNF_CHECK_EQ(1, 1) << "ok"; + KNF_CHECK_LE(1, 3) << "ok"; + KNF_CHECK_LT(1, 2) << "ok"; + KNF_CHECK_GT(2, 1) << "ok"; + KNF_CHECK_GE(2, 1) << "ok"; + + ASSERT_THROW(KNF_CHECK_EQ(2, 1) << "bad things happened", std::runtime_error); + + // for debug build + KNF_DCHECK_EQ(1, 1) << "ok"; + KNF_DCHECK_LE(1, 3) << "ok"; + KNF_DCHECK_LT(1, 2) << "ok"; + KNF_DCHECK_GT(2, 1) << "ok"; + KNF_DCHECK_GE(2, 1) << "ok"; + +#if !defined(NDEBUG) + ASSERT_THROW(KNF_CHECK_EQ(2, 1) << "bad things happened", std::runtime_error); +#endif +} + +} // namespace knf +#endif diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-online-fbank.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-online-fbank.cc new file mode 100644 index 0000000..c2d437a --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-online-fbank.cc @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "kaldi-native-fbank/csrc/online-feature.h" +#include "kaldi-native-fbank/csrc/feature-fbank.h" +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" + +int main() { + knf::FbankOptions opts; + opts.frame_opts.dither = 0; + opts.mel_opts.num_bins = 10; + + knf::OnlineFbank fbank(opts); + for (int32_t i = 0; i < 1600; ++i) { + float s = (i * i - i / 2) / 32767.; + fbank.AcceptWaveform(16000, &s, 1); + } + + std::ostringstream os; + + int32_t n = fbank.NumFramesReady(); + for (int32_t i = 0; i != n; ++i) { + const float *frame = fbank.GetFrame(i); + for (int32_t k = 0; k != opts.mel_opts.num_bins; ++k) { + os << frame[k] << ", "; + } + os << "\n"; + } + + std::cout << os.str() << "\n"; + + return 0; +} diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-online-feature.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-online-feature.cc new file mode 100644 index 0000000..bfbe621 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-online-feature.cc @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "kaldi-native-fbank/csrc/online-feature.h" +namespace knf { + +TEST(RecyclingVector, TestUnlimited) { + RecyclingVector v(-1); + constexpr int32_t N = 100; + for (int32_t i = 0; i != N; ++i) { + std::unique_ptr p(new float[3]{i, i + 1, i + 2}); + v.PushBack(std::move(p)); + } + ASSERT_EQ(v.Size(), N); + + for (int32_t i = 0; i != N; ++i) { + const float *t = v.At(i); + for (int32_t k = 0; k != 3; ++k) { + EXPECT_EQ(t[k], (i + k)); + } + } +} + +TEST(RecyclingVector, Testlimited) { + constexpr int32_t K = 3; + constexpr int32_t N = 10; + RecyclingVector v(K); + for (int32_t i = 0; i != N; ++i) { + std::unique_ptr p(new float[3]{i, i + 1, i + 2}); + v.PushBack(std::move(p)); + } + + ASSERT_EQ(v.Size(), N); + + for (int32_t i = N - K; i != N; ++i) { + const float *t = v.At(i); + + for (int32_t k = 0; k != 3; ++k) { + EXPECT_EQ(t[k], (i + k)); + } + } +} +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-rfft.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-rfft.cc new file mode 100644 index 0000000..e93adac --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/test-rfft.cc @@ -0,0 +1,55 @@ +/** + * Copyright 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "gtest/gtest.h" +#include "kaldi-native-fbank/csrc/rfft.h" + +namespace knf { + +#if 0 +>>> import torch +>>> a = torch.tensor([1., -1, 3, 8, 20, 6, 0, 2]) +>>> torch.fft.rfft(a) +tensor([ 39.0000+0.0000j, -28.1924-2.2929j, 18.0000+5.0000j, -9.8076+3.7071j, + 9.0000+0.0000j]) +#endif + +TEST(Rfft, TestRfft) { + knf::Rfft fft(8); + for (int32_t i = 0; i != 10; ++i) { + std::vector d = {1, -1, 3, 8, 20, 6, 0, 2}; + fft.Compute(d.data()); + + EXPECT_EQ(d[0], 39); + EXPECT_EQ(d[1], 9); + + EXPECT_NEAR(d[2], -28.1924, 1e-3); + EXPECT_NEAR(-d[3], -2.2929, 1e-3); + + EXPECT_NEAR(d[4], 18, 1e-3); + EXPECT_NEAR(-d[5], 5, 1e-3); + + EXPECT_NEAR(d[6], -9.8076, 1e-3); + EXPECT_NEAR(-d[7], 3.7071, 1e-3); + } +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/whisper-feature.cc b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/whisper-feature.cc new file mode 100644 index 0000000..1b69984 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/whisper-feature.cc @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2023 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/csrc/whisper-feature.h" + +#include +#include +#include + +#include "kaldi-native-fbank/csrc/log.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" + +#ifndef M_2PI +#define M_2PI 6.283185307179586476925286766559005 +#endif + +namespace knf { + +std::string WhisperFeatureOptions::ToString() const { + std::ostringstream os; + os << "WhisperFeatureOptions("; + os << "frame_opts=" << frame_opts.ToString() << ", "; + os << "dim=" << dim << ")"; + return os.str(); +} + +static void dft(const std::vector &in, std::vector *out) { + // this function is modified from + // https://github.com/ggerganov/whisper.cpp/blob/master/whisper.cpp#L2353 + int32_t N = in.size(); + + out->resize(N * 2); + + auto M_2PI_over_N = M_2PI / N; + for (int32_t k = 0; k < N; ++k) { + float re = 0; + float im = 0; + + for (int32_t n = 0; n < N; ++n) { + float angle = M_2PI_over_N * k * n; + re += in[n] * cos(angle); + im -= in[n] * sin(angle); + } + + (*out)[k * 2 + 0] = re; + (*out)[k * 2 + 1] = im; + } +} + +// Cooley-Tukey FFT +// poor man's implementation - use something better +// input is real-valued +// output is complex-valued +static void fft(const std::vector &in, std::vector *out) { + // this function is copied from + // https://github.com/ggerganov/whisper.cpp/blob/master/whisper.cpp#L2373C1-L2429C1 + + int32_t N = in.size(); + out->resize(N * 2); + + if (N == 1) { + (*out)[0] = in[0]; + (*out)[1] = 0; + return; + } + + if (N % 2 == 1) { + dft(in, out); + return; + } + + std::vector even; + std::vector odd; + + even.reserve(N / 2); + odd.reserve(N / 2); + + for (int32_t i = 0; i != N; ++i) { + if (i % 2 == 0) { + even.push_back(in[i]); + } else { + odd.push_back(in[i]); + } + } + + std::vector even_fft; + std::vector odd_fft; + + fft(even, &even_fft); + fft(odd, &odd_fft); + + for (int32_t k = 0; k < N / 2; ++k) { + float theta = M_2PI * k / N; + + float re = cos(theta); + float im = -sin(theta); + + float re_odd = odd_fft[2 * k + 0]; + float im_odd = odd_fft[2 * k + 1]; + + (*out)[2 * k + 0] = even_fft[2 * k + 0] + re * re_odd - im * im_odd; + (*out)[2 * k + 1] = even_fft[2 * k + 1] + re * im_odd + im * re_odd; + + (*out)[2 * (k + N / 2) + 0] = + even_fft[2 * k + 0] - re * re_odd + im * im_odd; + (*out)[2 * (k + N / 2) + 1] = + even_fft[2 * k + 1] - re * im_odd - im * re_odd; + } +} + +WhisperFeatureComputer::WhisperFeatureComputer( + const WhisperFeatureOptions &opts /*= {}*/) + : opts_(opts) { + opts_.frame_opts.samp_freq = 16000; + opts_.frame_opts.frame_shift_ms = 10; + opts_.frame_opts.frame_length_ms = 25; + opts_.frame_opts.dither = 0; + opts_.frame_opts.preemph_coeff = 0; + opts_.frame_opts.remove_dc_offset = false; + opts_.frame_opts.window_type = "hann"; + opts_.frame_opts.round_to_power_of_two = false; + opts_.frame_opts.snip_edges = false; + + MelBanksOptions mel_opts; + mel_opts.num_bins = opts_.dim; + mel_opts.low_freq = 0; + mel_opts.is_librosa = true; + + mel_banks_ = std::make_unique(mel_opts, opts_.frame_opts, 1.0f); +} + +void WhisperFeatureComputer::Compute(float /*signal_raw_log_energy*/, + float /*vtln_warp*/, + std::vector *signal_frame, + float *feature) { + KNF_CHECK_EQ(signal_frame->size(), frame_opts_.PaddedWindowSize()); + // we have already applied window function to signal_frame before + // calling this method + std::vector fft_out; + fft(*signal_frame, &fft_out); + + int32_t num_fft = signal_frame->size(); + std::vector power(num_fft / 2 + 1); + for (int32_t i = 0; i <= num_fft / 2; ++i) { + float re = fft_out[2 * i + 0]; + float im = fft_out[2 * i + 1]; + power[i] = re * re + im * im; + } + + // feature is pre-allocated by the user + mel_banks_->Compute(power.data(), feature); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/csrc/whisper-feature.h b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/whisper-feature.h new file mode 100644 index 0000000..8ada4bd --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/csrc/whisper-feature.h @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2023 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_CSRC_WHISPER_FEATURE_H_ +#define KALDI_NATIVE_FBANK_CSRC_WHISPER_FEATURE_H_ + +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" + +namespace knf { + +struct WhisperFeatureOptions { + WhisperFeatureOptions(const FrameExtractionOptions &frame_opts = {}, + int32_t dim = 80) + : frame_opts(frame_opts), dim(dim) {} + + FrameExtractionOptions frame_opts; + int32_t dim = 80; + + std::string ToString() const; +}; + +class WhisperFeatureComputer { + public: + // note: opts.frame_opts is ignored and we reset it inside + explicit WhisperFeatureComputer(const WhisperFeatureOptions &opts = {}); + + int32_t Dim() const { return opts_.dim; } + + const FrameExtractionOptions &GetFrameOptions() const { + return opts_.frame_opts; + } + + void Compute(float /*signal_raw_log_energy*/, float /*vtln_warp*/, + std::vector *signal_frame, float *feature); + + // if true, compute log_energy_pre_window but after dithering and dc removal + bool NeedRawLogEnergy() const { return false; } + + using Options = WhisperFeatureOptions; + + private: + std::unique_ptr mel_banks_; + WhisperFeatureOptions opts_; +}; + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_CSRC_WHISPER_FEATURE_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/CMakeLists.txt b/cli/fbank/sys/knf/kaldi-native-fbank/python/CMakeLists.txt new file mode 100644 index 0000000..60d6382 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(csrc) +add_subdirectory(tests) diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/CMakeLists.txt b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/CMakeLists.txt new file mode 100644 index 0000000..0366b08 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/CMakeLists.txt @@ -0,0 +1,34 @@ +pybind11_add_module(_kaldi_native_fbank + feature-fbank.cc + feature-mfcc.cc + feature-window.cc + kaldi-native-fbank.cc + mel-computations.cc + online-feature.cc + rfft.cc + utils.cc +) + +if(APPLE) + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE PYTHON_SITE_PACKAGE_DIR + ) + message(STATUS "PYTHON_SITE_PACKAGE_DIR: ${PYTHON_SITE_PACKAGE_DIR}") + if(PYTHON_SITE_PACKAGE_DIR STREQUAL "") + message(WARNING "PYTHON_SITE_PACKAGE_DIR is empty!") + else() + target_link_libraries(_kaldi_native_fbank PRIVATE "-Wl,-rpath,${PYTHON_SITE_PACKAGE_DIR}") + endif() +endif() + +if(NOT WIN32) + target_link_libraries(_kaldi_native_fbank PRIVATE "-Wl,-rpath,${kaldi_native_fbank_rpath_origin}/kaldi_native_fbank/lib") +endif() + +target_link_libraries(_kaldi_native_fbank PRIVATE kaldi-native-fbank-core) + +install(TARGETS _kaldi_native_fbank + DESTINATION ../ +) diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-fbank.cc b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-fbank.cc new file mode 100644 index 0000000..870dc86 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-fbank.cc @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/python/csrc/feature-fbank.h" + +#include + +#include "kaldi-native-fbank/csrc/feature-fbank.h" +#include "kaldi-native-fbank/python/csrc/utils.h" +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" + +namespace knf { + +static void PybindFbankOptions(py::module &m) { // NOLINT + using PyClass = FbankOptions; + py::class_(m, "FbankOptions") + .def(py::init<>()) + .def_readwrite("frame_opts", &PyClass::frame_opts) + .def_readwrite("mel_opts", &PyClass::mel_opts) + .def_readwrite("use_energy", &PyClass::use_energy) + .def_readwrite("energy_floor", &PyClass::energy_floor) + .def_readwrite("raw_energy", &PyClass::raw_energy) + .def_readwrite("htk_compat", &PyClass::htk_compat) + .def_readwrite("use_log_fbank", &PyClass::use_log_fbank) + .def_readwrite("use_power", &PyClass::use_power) + .def("__str__", + [](const PyClass &self) -> std::string { return self.ToString(); }) + .def("as_dict", + [](const PyClass &self) -> py::dict { return AsDict(self); }) + .def_static( + "from_dict", + [](py::dict dict) -> PyClass { return FbankOptionsFromDict(dict); }) + .def(py::pickle( + [](const PyClass &self) -> py::dict { return AsDict(self); }, + [](py::dict dict) -> PyClass { return FbankOptionsFromDict(dict); })); +} + +void PybindFeatureFbank(py::module &m) { // NOLINT + PybindFbankOptions(m); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-fbank.h b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-fbank.h new file mode 100644 index 0000000..6490c22 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-fbank.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_FBANK_H_ +#define KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_FBANK_H_ + +#include "kaldi-native-fbank/python/csrc/kaldi-native-fbank.h" + +namespace knf { + +void PybindFeatureFbank(py::module &m); // NOLINT + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_FBANK_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-mfcc.cc b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-mfcc.cc new file mode 100644 index 0000000..d910016 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-mfcc.cc @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2024 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/csrc/feature-mfcc.h" + +#include + +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" +#include "kaldi-native-fbank/python/csrc/feature-mfcc.h" +#include "kaldi-native-fbank/python/csrc/utils.h" + +namespace knf { + +static void PybindMfccOptions(py::module &m) { // NOLINT + using PyClass = MfccOptions; + py::class_(m, "MfccOptions") + .def(py::init<>()) + .def_readwrite("frame_opts", &PyClass::frame_opts) + .def_readwrite("mel_opts", &PyClass::mel_opts) + .def_readwrite("num_ceps", &PyClass::num_ceps) + .def_readwrite("use_energy", &PyClass::use_energy) + .def_readwrite("energy_floor", &PyClass::energy_floor) + .def_readwrite("raw_energy", &PyClass::raw_energy) + .def_readwrite("cepstral_lifter", &PyClass::cepstral_lifter) + .def_readwrite("htk_compat", &PyClass::htk_compat) + .def("__str__", + [](const PyClass &self) -> std::string { return self.ToString(); }) + .def("as_dict", + [](const PyClass &self) -> py::dict { return AsDict(self); }) + .def_static( + "from_dict", + [](py::dict dict) -> PyClass { return MfccOptionsFromDict(dict); }) + .def(py::pickle( + [](const PyClass &self) -> py::dict { return AsDict(self); }, + [](py::dict dict) -> PyClass { return MfccOptionsFromDict(dict); })); +} + +void PybindFeatureMfcc(py::module &m) { // NOLINT + PybindMfccOptions(m); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-mfcc.h b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-mfcc.h new file mode 100644 index 0000000..1a44578 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-mfcc.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2024 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_MFCC_H_ +#define KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_MFCC_H_ + +#include "kaldi-native-fbank/python/csrc/kaldi-native-fbank.h" + +namespace knf { + +void PybindFeatureMfcc(py::module &m); // NOLINT + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_MFCC_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-window.cc b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-window.cc new file mode 100644 index 0000000..196e832 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-window.cc @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/python/csrc/feature-window.h" + +#include +#include + +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/python/csrc/utils.h" + +namespace knf { + +static void PybindFrameExtractionOptions(py::module &m) { // NOLINT + using PyClass = FrameExtractionOptions; + py::class_(m, "FrameExtractionOptions") + .def(py::init<>()) + .def_readwrite("samp_freq", &PyClass::samp_freq) + .def_readwrite("frame_shift_ms", &PyClass::frame_shift_ms) + .def_readwrite("frame_length_ms", &PyClass::frame_length_ms) + .def_readwrite("dither", &PyClass::dither) + .def_readwrite("preemph_coeff", &PyClass::preemph_coeff) + .def_readwrite("remove_dc_offset", &PyClass::remove_dc_offset) + .def_readwrite("window_type", &PyClass::window_type) + .def_readwrite("round_to_power_of_two", &PyClass::round_to_power_of_two) + .def_readwrite("blackman_coeff", &PyClass::blackman_coeff) + .def_readwrite("snip_edges", &PyClass::snip_edges) + .def("as_dict", + [](const PyClass &self) -> py::dict { return AsDict(self); }) + .def_static("from_dict", + [](py::dict dict) -> PyClass { + return FrameExtractionOptionsFromDict(dict); + }) +#if 0 + .def_readwrite("allow_downsample", + &PyClass::allow_downsample) + .def_readwrite("allow_upsample", &PyClass::allow_upsample) +#endif + .def("__str__", + [](const PyClass &self) -> std::string { return self.ToString(); }) + .def(py::pickle( + [](const PyClass &self) -> py::dict { return AsDict(self); }, + [](py::dict dict) -> PyClass { + return FrameExtractionOptionsFromDict(dict); + })); +} +static void PybindFeatureWindowFunction(py::module *m) { + using PyClass = FeatureWindowFunction; + py::class_(*m, "FeatureWindowFunction") + .def(py::init(), py::arg("opts")) + .def("apply", + [](const PyClass &self, + std::vector &wave) -> std::vector { + self.Apply(wave.data()); + return wave; + }) + .def_property_readonly("window", &PyClass::GetWindow); +} + +void PybindFeatureWindow(py::module &m) { // NOLINT + PybindFrameExtractionOptions(m); + PybindFeatureWindowFunction(&m); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-window.h b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-window.h new file mode 100644 index 0000000..aba5b9e --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/feature-window.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_WINDOW_H_ +#define KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_WINDOW_H_ + +#include "kaldi-native-fbank/python/csrc/kaldi-native-fbank.h" + +namespace knf { + +void PybindFeatureWindow(py::module &m); // NOLINT + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_PYTHON_CSRC_FEATURE_WINDOW_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/kaldi-native-fbank.cc b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/kaldi-native-fbank.cc new file mode 100644 index 0000000..be12a47 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/kaldi-native-fbank.cc @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/python/csrc/kaldi-native-fbank.h" + +#include "kaldi-native-fbank/python/csrc/feature-fbank.h" +#include "kaldi-native-fbank/python/csrc/feature-mfcc.h" +#include "kaldi-native-fbank/python/csrc/feature-window.h" +#include "kaldi-native-fbank/python/csrc/mel-computations.h" +#include "kaldi-native-fbank/python/csrc/online-feature.h" +#include "kaldi-native-fbank/python/csrc/rfft.h" + +namespace knf { + +PYBIND11_MODULE(_kaldi_native_fbank, m) { + m.doc() = "Python wrapper for kaldi native fbank"; + PybindFeatureWindow(m); + PybindMelComputations(m); + PybindFeatureFbank(m); + PybindFeatureMfcc(m); + PybindRfft(m); + + PybindOnlineFeature(m); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/kaldi-native-fbank.h b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/kaldi-native-fbank.h new file mode 100644 index 0000000..756f4ce --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/kaldi-native-fbank.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_PYTHON_CSRC_KALDI_NATIVE_FBANK_H_ +#define KALDI_NATIVE_FBANK_PYTHON_CSRC_KALDI_NATIVE_FBANK_H_ + +#include "pybind11/numpy.h" +#include "pybind11/pybind11.h" +#include "pybind11/stl.h" +namespace py = pybind11; + +#endif // KALDI_NATIVE_FBANK_PYTHON_CSRC_KALDI_NATIVE_FBANK_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/mel-computations.cc b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/mel-computations.cc new file mode 100644 index 0000000..305dcca --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/mel-computations.cc @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/python/csrc/mel-computations.h" + +#include + +#include "kaldi-native-fbank/csrc/mel-computations.h" +#include "kaldi-native-fbank/python/csrc/utils.h" + +namespace knf { + +static void PybindMelBanksOptions(py::module &m) { // NOLINT + using PyClass = MelBanksOptions; + py::class_(m, "MelBanksOptions") + .def(py::init<>()) + .def_readwrite("num_bins", &PyClass::num_bins) + .def_readwrite("low_freq", &PyClass::low_freq) + .def_readwrite("high_freq", &PyClass::high_freq) + .def_readwrite("vtln_low", &PyClass::vtln_low) + .def_readwrite("vtln_high", &PyClass::vtln_high) + .def_readwrite("debug_mel", &PyClass::debug_mel) + .def_readwrite("htk_mode", &PyClass::htk_mode) + .def_readwrite("is_librosa", &PyClass::is_librosa) + .def_readwrite("norm", &PyClass::norm) + .def("__str__", + [](const PyClass &self) -> std::string { return self.ToString(); }) + .def("as_dict", + [](const PyClass &self) -> py::dict { return AsDict(self); }) + .def_static("from_dict", + [](py::dict dict) -> PyClass { + return MelBanksOptionsFromDict(dict); + }) + .def(py::pickle( + [](const PyClass &self) -> py::dict { return AsDict(self); }, + [](py::dict dict) -> PyClass { + return MelBanksOptionsFromDict(dict); + })); +} + +void PybindMelComputations(py::module &m) { // NOLINT + PybindMelBanksOptions(m); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/mel-computations.h b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/mel-computations.h new file mode 100644 index 0000000..2ca9ac7 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/mel-computations.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_PYTHON_CSRC_MEL_COMPUTATIONS_H_ +#define KALDI_NATIVE_FBANK_PYTHON_CSRC_MEL_COMPUTATIONS_H_ + +#include "kaldi-native-fbank/python/csrc/kaldi-native-fbank.h" + +namespace knf { + +void PybindMelComputations(py::module &m); // NOLINT + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_PYTHON_CSRC_MEL_COMPUTATIONS_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/online-feature.cc b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/online-feature.cc new file mode 100644 index 0000000..b650c13 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/online-feature.cc @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/python/csrc/online-feature.h" + +#include +#include +#include + +#include "kaldi-native-fbank/csrc/feature-fbank.h" +#include "kaldi-native-fbank/csrc/feature-mfcc.h" +#include "kaldi-native-fbank/csrc/online-feature.h" +#include "kaldi-native-fbank/csrc/whisper-feature.h" +#include "kaldi-native-fbank/python/csrc/utils.h" + +namespace pybind11 { +class gil_scoped_release; +} // namespace pybind11 + +namespace knf { + +static void PybindWhisperFeatureOptions(py::module &m) { // NOLINT + using PyClass = WhisperFeatureOptions; + py::class_(m, "WhisperFeatureOptions") + .def(py::init<>()) + .def_readwrite("frame_opts", &PyClass::frame_opts) + .def_readwrite("dim", &PyClass::dim) + .def("__str__", + [](const PyClass &self) -> std::string { return self.ToString(); }) + .def("as_dict", + [](const PyClass &self) -> py::dict { return AsDict(self); }) + .def_static("from_dict", + [](py::dict dict) -> PyClass { + return WhisperFeatureOptionsFromDict(dict); + }) + .def(py::pickle( + [](const PyClass &self) -> py::dict { return AsDict(self); }, + [](py::dict dict) -> PyClass { + return WhisperFeatureOptionsFromDict(dict); + })); +} + +template +void PybindOnlineFeatureTpl(py::module &m, // NOLINT + const std::string &class_name, + const std::string &class_help_doc = "") { + using PyClass = OnlineGenericBaseFeature; + using Options = typename C::Options; + py::class_(m, class_name.c_str(), class_help_doc.c_str()) + .def(py::init(), py::arg("opts")) + .def_property_readonly("dim", &PyClass::Dim) + .def_property_readonly("frame_shift_in_seconds", + &PyClass::FrameShiftInSeconds) + .def_property_readonly("num_frames_ready", &PyClass::NumFramesReady) + .def("is_last_frame", &PyClass::IsLastFrame, py::arg("frame")) + .def( + "get_frame", + [](py::object obj, int32_t frame) { + auto *self = obj.cast(); + const float *f = self->GetFrame(frame); + return py::array_t({self->Dim()}, // shape + {sizeof(float)}, // stride in bytes + f, // ptr + obj); // it will increase the reference + // count of **this** vector + }, + py::arg("frame")) + .def( + "accept_waveform", + [](PyClass &self, float sampling_rate, + const std::vector &waveform) { + self.AcceptWaveform(sampling_rate, waveform.data(), + waveform.size()); + }, + py::arg("sampling_rate"), py::arg("waveform"), + py::call_guard()) + .def("input_finished", &PyClass::InputFinished, + py::call_guard()) + .def("pop", &PyClass::Pop, py::arg("n"), + py::call_guard()); +} + +void PybindOnlineFeature(py::module &m) { // NOLINT + PybindOnlineFeatureTpl(m, "OnlineFbank"); + PybindOnlineFeatureTpl(m, "OnlineMfcc"); + + PybindWhisperFeatureOptions(m); + + PybindOnlineFeatureTpl(m, "OnlineWhisperFbank"); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/online-feature.h b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/online-feature.h new file mode 100644 index 0000000..b4a05df --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/online-feature.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_PYTHON_CSRC_ONLINE_FEATURE_H_ +#define KALDI_NATIVE_FBANK_PYTHON_CSRC_ONLINE_FEATURE_H_ + +#include "kaldi-native-fbank/python/csrc/kaldi-native-fbank.h" + +namespace knf { + +void PybindOnlineFeature(py::module &m); // NOLINT + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_PYTHON_CSRC_ONLINE_FEATURE_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/rfft.cc b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/rfft.cc new file mode 100644 index 0000000..90d2ea3 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/rfft.cc @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022-2023 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/python/csrc/rfft.h" + +#include +#include + +#include "kaldi-native-fbank/csrc/rfft.h" + +namespace knf { + +void PybindRfft(py::module &m) { // NOLINT + py::class_(m, "Rfft") + .def(py::init(), py::arg("n")) + .def("compute", + [](Rfft &self, std::vector &d) -> std::vector { + self.Compute(d.data()); + return d; + }); +} + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/rfft.h b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/rfft.h new file mode 100644 index 0000000..49ffa46 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/rfft.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022-2023 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_PYTHON_CSRC_RFFT_H_ +#define KALDI_NATIVE_FBANK_PYTHON_CSRC_RFFT_H_ + +#include "kaldi-native-fbank/python/csrc/kaldi-native-fbank.h" + +namespace knf { + +void PybindRfft(py::module &m); // NOLINT + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_PYTHON_CSRC_RFFT_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/utils.cc b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/utils.cc new file mode 100644 index 0000000..3a5cc40 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/utils.cc @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaldi-native-fbank/python/csrc/utils.h" + +#include + +#include "kaldi-native-fbank/csrc/feature-fbank.h" +#include "kaldi-native-fbank/csrc/feature-mfcc.h" +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/whisper-feature.h" + +#define FROM_DICT(type, key) \ + if (dict.contains(#key)) { \ + opts.key = py::type(dict[#key]); \ + } + +#define AS_DICT(key) dict[#key] = opts.key + +namespace knf { + +FrameExtractionOptions FrameExtractionOptionsFromDict(py::dict dict) { + FrameExtractionOptions opts; + + FROM_DICT(float_, samp_freq); + FROM_DICT(float_, frame_shift_ms); + FROM_DICT(float_, frame_length_ms); + FROM_DICT(float_, dither); + FROM_DICT(float_, preemph_coeff); + FROM_DICT(bool_, remove_dc_offset); + FROM_DICT(str, window_type); + FROM_DICT(bool_, round_to_power_of_two); + FROM_DICT(float_, blackman_coeff); + FROM_DICT(bool_, snip_edges); + + return opts; +} + +py::dict AsDict(const FrameExtractionOptions &opts) { + py::dict dict; + + AS_DICT(samp_freq); + AS_DICT(frame_shift_ms); + AS_DICT(frame_length_ms); + AS_DICT(dither); + AS_DICT(preemph_coeff); + AS_DICT(remove_dc_offset); + AS_DICT(window_type); + AS_DICT(round_to_power_of_two); + AS_DICT(blackman_coeff); + AS_DICT(snip_edges); + + return dict; +} + +MelBanksOptions MelBanksOptionsFromDict(py::dict dict) { + MelBanksOptions opts; + + FROM_DICT(int_, num_bins); + FROM_DICT(float_, low_freq); + FROM_DICT(float_, high_freq); + FROM_DICT(float_, vtln_low); + FROM_DICT(float_, vtln_high); + FROM_DICT(bool_, debug_mel); + FROM_DICT(bool_, htk_mode); + FROM_DICT(bool_, is_librosa); + FROM_DICT(str, norm); + + return opts; +} + +py::dict AsDict(const MelBanksOptions &opts) { + py::dict dict; + + AS_DICT(num_bins); + AS_DICT(low_freq); + AS_DICT(high_freq); + AS_DICT(vtln_low); + AS_DICT(vtln_high); + AS_DICT(debug_mel); + AS_DICT(htk_mode); + AS_DICT(is_librosa); + AS_DICT(norm); + + return dict; +} + +FbankOptions FbankOptionsFromDict(py::dict dict) { + FbankOptions opts; + + if (dict.contains("frame_opts")) { + opts.frame_opts = FrameExtractionOptionsFromDict(dict["frame_opts"]); + } + + if (dict.contains("mel_opts")) { + opts.mel_opts = MelBanksOptionsFromDict(dict["mel_opts"]); + } + + FROM_DICT(bool_, use_energy); + FROM_DICT(float_, energy_floor); + FROM_DICT(bool_, raw_energy); + FROM_DICT(bool_, htk_compat); + FROM_DICT(bool_, use_log_fbank); + FROM_DICT(bool_, use_power); + + return opts; +} + +py::dict AsDict(const FbankOptions &opts) { + py::dict dict; + + dict["frame_opts"] = AsDict(opts.frame_opts); + dict["mel_opts"] = AsDict(opts.mel_opts); + AS_DICT(use_energy); + AS_DICT(energy_floor); + AS_DICT(raw_energy); + AS_DICT(htk_compat); + AS_DICT(use_log_fbank); + AS_DICT(use_power); + + return dict; +} + +MfccOptions MfccOptionsFromDict(py::dict dict) { + MfccOptions opts; + + if (dict.contains("frame_opts")) { + opts.frame_opts = FrameExtractionOptionsFromDict(dict["frame_opts"]); + } + + if (dict.contains("mel_opts")) { + opts.mel_opts = MelBanksOptionsFromDict(dict["mel_opts"]); + } + + FROM_DICT(int_, num_ceps); + FROM_DICT(bool_, use_energy); + FROM_DICT(float_, energy_floor); + FROM_DICT(bool_, raw_energy); + FROM_DICT(float_, cepstral_lifter); + FROM_DICT(bool_, htk_compat); + + return opts; +} + +py::dict AsDict(const MfccOptions &opts) { + py::dict dict; + + dict["frame_opts"] = AsDict(opts.frame_opts); + dict["mel_opts"] = AsDict(opts.mel_opts); + AS_DICT(num_ceps); + AS_DICT(use_energy); + AS_DICT(energy_floor); + AS_DICT(raw_energy); + AS_DICT(cepstral_lifter); + AS_DICT(htk_compat); + + return dict; +} + +WhisperFeatureOptions WhisperFeatureOptionsFromDict(py::dict dict) { + WhisperFeatureOptions opts; + + if (dict.contains("frame_opts")) { + opts.frame_opts = FrameExtractionOptionsFromDict(dict["frame_opts"]); + } + + FROM_DICT(int_, dim); + + return opts; +} + +py::dict AsDict(const WhisperFeatureOptions &opts) { + py::dict dict; + + dict["frame_opts"] = AsDict(opts.frame_opts); + AS_DICT(dim); + + return dict; +} + +#undef FROM_DICT +#undef AS_DICT + +} // namespace knf diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/utils.h b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/utils.h new file mode 100644 index 0000000..ab6bee1 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/csrc/utils.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + * + * See LICENSE for clarification regarding multiple authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KALDI_NATIVE_FBANK_PYTHON_CSRC_UTILS_H_ +#define KALDI_NATIVE_FBANK_PYTHON_CSRC_UTILS_H_ + +#include "kaldi-native-fbank/csrc/feature-fbank.h" +#include "kaldi-native-fbank/csrc/feature-mfcc.h" +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" +#include "kaldi-native-fbank/csrc/whisper-feature.h" +#include "kaldi-native-fbank/python/csrc/kaldi-native-fbank.h" + +/* + * This file contains code about `from_dict` and + * `as_dict` for various options in kaldi-native-fbank. + * + * Regarding `from_dict`, users don't need to provide + * all the fields in the options. If some fields + * are not provided, it just uses the default one. + * + * If the provided dict in `from_dict` is empty, + * all fields use their default values. + */ + +namespace knf { + +FrameExtractionOptions FrameExtractionOptionsFromDict(py::dict dict); +py::dict AsDict(const FrameExtractionOptions &opts); + +MelBanksOptions MelBanksOptionsFromDict(py::dict dict); +py::dict AsDict(const MelBanksOptions &opts); + +FbankOptions FbankOptionsFromDict(py::dict dict); +py::dict AsDict(const FbankOptions &opts); + +MfccOptions MfccOptionsFromDict(py::dict dict); +py::dict AsDict(const MfccOptions &opts); + +WhisperFeatureOptions WhisperFeatureOptionsFromDict(py::dict dict); +py::dict AsDict(const WhisperFeatureOptions &opts); + +} // namespace knf + +#endif // KALDI_NATIVE_FBANK_PYTHON_CSRC_UTILS_H_ diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/kaldi_native_fbank/__init__.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/kaldi_native_fbank/__init__.py new file mode 100644 index 0000000..d336426 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/kaldi_native_fbank/__init__.py @@ -0,0 +1,12 @@ +from _kaldi_native_fbank import ( + FbankOptions, + FeatureWindowFunction, + FrameExtractionOptions, + MelBanksOptions, + MfccOptions, + OnlineFbank, + OnlineMfcc, + OnlineWhisperFbank, + Rfft, + WhisperFeatureOptions, +) diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/CMakeLists.txt b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/CMakeLists.txt new file mode 100644 index 0000000..d2c671c --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/CMakeLists.txt @@ -0,0 +1,36 @@ +function(kaldi_native_fbank_add_py_test source) + get_filename_component(name ${source} NAME_WE) + set(name "${name}_py") + + message(STATUS "source: ${source}") + + add_test(NAME ${name} + COMMAND + "${PYTHON_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/${source}" + ) + + get_filename_component(kaldi_native_fbank_path ${CMAKE_CURRENT_LIST_DIR} DIRECTORY) + + set_property(TEST ${name} + PROPERTY ENVIRONMENT "PYTHONPATH=${kaldi_native_fbank_path}:$:$ENV{PYTHONPATH}" + ) +endfunction() + +# please sort the files in alphabetic order +set(py_test_files + test_fbank_options.py + test_feature_window_function.py + test_frame_extraction_options.py + test_mel_bank_options.py + test_online_whisper_fbank.py + test_online_fbank.py + test_online_mfcc.py + # test_rfft.py +) + +if(KALDI_NATIVE_FBANK_BUILD_TESTS) + foreach(source IN LISTS py_test_files) + kaldi_native_fbank_add_py_test(${source}) + endforeach() +endif() diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_fbank_options.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_fbank_options.py new file mode 100755 index 0000000..3c291df --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_fbank_options.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021 Xiaomi Corporation (authors: Fangjun Kuang) + + +import pickle + +import kaldi_native_fbank as knf + + +def test_default(): + opts = knf.FbankOptions() + assert opts.frame_opts.samp_freq == 16000 + assert opts.frame_opts.frame_shift_ms == 10.0 + assert opts.frame_opts.frame_length_ms == 25.0 + assert abs(opts.frame_opts.dither - 0.00003) < 1e-6 + assert abs(opts.frame_opts.preemph_coeff - 0.97) < 1e-6 + assert opts.frame_opts.remove_dc_offset is True + assert opts.frame_opts.window_type == "povey" + assert opts.frame_opts.round_to_power_of_two is True + assert abs(opts.frame_opts.blackman_coeff - 0.42) < 1e-6 + assert opts.frame_opts.snip_edges is True + + assert opts.mel_opts.num_bins == 23 + assert opts.mel_opts.low_freq == 20 + assert opts.mel_opts.high_freq == 0 + assert opts.mel_opts.vtln_low == 100 + assert opts.mel_opts.vtln_high == -500 + assert opts.mel_opts.debug_mel is False + assert opts.mel_opts.htk_mode is False + + assert opts.use_energy is False + assert opts.energy_floor == 0.0 + assert opts.raw_energy is True + assert opts.htk_compat is False + assert opts.use_log_fbank is True + assert opts.use_power is True + + +def test_set_get(): + opts = knf.FbankOptions() + opts.use_energy = True + assert opts.use_energy is True + + opts.energy_floor = 1 + assert opts.energy_floor == 1 + + opts.raw_energy = False + assert opts.raw_energy is False + + opts.htk_compat = True + assert opts.htk_compat is True + + opts.use_log_fbank = False + assert opts.use_log_fbank is False + + opts.use_power = False + assert opts.use_power is False + + +def test_set_get_frame_opts(): + opts = knf.FbankOptions() + + opts.frame_opts.samp_freq = 44100 + assert opts.frame_opts.samp_freq == 44100 + + opts.frame_opts.frame_shift_ms = 20.5 + assert opts.frame_opts.frame_shift_ms == 20.5 + + opts.frame_opts.frame_length_ms = 1 + assert opts.frame_opts.frame_length_ms == 1 + + opts.frame_opts.dither = 0.5 + assert opts.frame_opts.dither == 0.5 + + opts.frame_opts.preemph_coeff = 0.25 + assert opts.frame_opts.preemph_coeff == 0.25 + + opts.frame_opts.remove_dc_offset = False + assert opts.frame_opts.remove_dc_offset is False + + opts.frame_opts.window_type = "hanning" + assert opts.frame_opts.window_type == "hanning" + + opts.frame_opts.round_to_power_of_two = False + assert opts.frame_opts.round_to_power_of_two is False + + opts.frame_opts.blackman_coeff = 0.25 + assert opts.frame_opts.blackman_coeff == 0.25 + + opts.frame_opts.snip_edges = False + assert opts.frame_opts.snip_edges is False + + +def test_set_get_mel_opts(): + opts = knf.FbankOptions() + + opts.mel_opts.num_bins = 100 + assert opts.mel_opts.num_bins == 100 + + opts.mel_opts.low_freq = 22 + assert opts.mel_opts.low_freq == 22 + + opts.mel_opts.high_freq = 1 + assert opts.mel_opts.high_freq == 1 + + opts.mel_opts.vtln_low = 101 + assert opts.mel_opts.vtln_low == 101 + + opts.mel_opts.vtln_high = -100 + assert opts.mel_opts.vtln_high == -100 + + opts.mel_opts.debug_mel = True + assert opts.mel_opts.debug_mel is True + + opts.mel_opts.htk_mode = True + assert opts.mel_opts.htk_mode is True + + +def test_from_empty_dict(): + opts = knf.FbankOptions.from_dict({}) + opts2 = knf.FbankOptions() + + assert str(opts) == str(opts2) + + +def test_from_dict_partial(): + d = { + "energy_floor": 10.5, + "htk_compat": True, + "mel_opts": {"num_bins": 80, "vtln_low": 1}, + "frame_opts": {"window_type": "hanning"}, + } + opts = knf.FbankOptions.from_dict(d) + assert opts.energy_floor == 10.5 + assert opts.htk_compat is True + assert opts.mel_opts.num_bins == 80 + assert opts.mel_opts.vtln_low == 1 + assert opts.frame_opts.window_type == "hanning" + + mel_opts = knf.MelBanksOptions.from_dict(d["mel_opts"]) + assert str(opts.mel_opts) == str(mel_opts) + + +def test_from_dict_full_and_as_dict(): + opts = knf.FbankOptions() + opts.htk_compat = True + opts.mel_opts.num_bins = 80 + opts.frame_opts.samp_freq = 10 + + d = opts.as_dict() + assert d["htk_compat"] is True + assert d["mel_opts"]["num_bins"] == 80 + assert d["frame_opts"]["samp_freq"] == 10 + + mel_opts = knf.MelBanksOptions() + mel_opts.num_bins = 80 + assert d["mel_opts"] == mel_opts.as_dict() + + frame_opts = knf.FrameExtractionOptions() + frame_opts.samp_freq = 10 + assert d["frame_opts"] == frame_opts.as_dict() + + opts2 = knf.FbankOptions.from_dict(d) + assert str(opts2) == str(opts) + + d["htk_compat"] = False + opts3 = knf.FbankOptions.from_dict(d) + assert opts3.htk_compat is False + + +def test_pickle(): + opts = knf.FbankOptions() + opts.use_energy = True + opts.use_power = False + + opts.frame_opts.samp_freq = 44100 + opts.mel_opts.num_bins = 100 + + data = pickle.dumps(opts) + + opts2 = pickle.loads(data) + assert str(opts) == str(opts2) + + +def main(): + test_default() + test_set_get() + test_set_get_frame_opts() + test_set_get_mel_opts() + test_from_empty_dict() + test_from_dict_partial() + test_from_dict_full_and_as_dict() + test_pickle() + + +if __name__ == "__main__": + main() diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_feature_window_function.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_feature_window_function.py new file mode 100755 index 0000000..f05a822 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_feature_window_function.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022-2023 Xiaomi Corporation (authors: Fangjun Kuang) + +import kaldi_native_fbank as knf +import torch + + +def test_hann_window(): + opts = knf.FrameExtractionOptions() + assert opts.samp_freq == 16000 + assert opts.frame_shift_ms == 10.0 + assert opts.frame_length_ms == 25.0 + opts.window_type = "hann" + + n = int((opts.samp_freq * opts.frame_length_ms) / 1000) + + window = knf.FeatureWindowFunction(opts) + assert len(window.window) == n + w = torch.tensor(window.window) + + t = torch.hann_window(n, periodic=True) + assert torch.allclose(w, t, atol=1e-6), (w - t).abs().max() + + +def main(): + test_hann_window() + + +if __name__ == "__main__": + main() diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_frame_extraction_options.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_frame_extraction_options.py new file mode 100755 index 0000000..f17d63b --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_frame_extraction_options.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang) + +import pickle + +import kaldi_native_fbank as knf + + +def test_default(): + opts = knf.FrameExtractionOptions() + assert opts.samp_freq == 16000 + assert opts.frame_shift_ms == 10.0 + assert opts.frame_length_ms == 25.0 + assert abs(opts.dither - 0.00003) < 1e-6 + assert abs(opts.preemph_coeff - 0.97) < 1e-6 + assert opts.remove_dc_offset is True + assert opts.window_type == "povey" + assert opts.round_to_power_of_two is True + assert abs(opts.blackman_coeff - 0.42) < 1e-6 + assert opts.snip_edges is True + + +def test_set_get(): + opts = knf.FrameExtractionOptions() + opts.samp_freq = 44100 + assert opts.samp_freq == 44100 + + opts.frame_shift_ms = 20.5 + assert opts.frame_shift_ms == 20.5 + + opts.frame_length_ms = 1 + assert opts.frame_length_ms == 1 + + opts.dither = 0.5 + assert opts.dither == 0.5 + + opts.preemph_coeff = 0.25 + assert opts.preemph_coeff == 0.25 + + opts.remove_dc_offset = False + assert opts.remove_dc_offset is False + + opts.window_type = "hanning" + assert opts.window_type == "hanning" + + opts.round_to_power_of_two = False + assert opts.round_to_power_of_two is False + + opts.blackman_coeff = 0.25 + assert opts.blackman_coeff == 0.25 + + opts.snip_edges = False + assert opts.snip_edges is False + + +def test_from_empty_dict(): + opts = knf.FrameExtractionOptions.from_dict({}) + opts2 = knf.FrameExtractionOptions() + + assert str(opts) == str(opts2) + + +def test_from_dict_partial(): + d = {"samp_freq": 10, "frame_shift_ms": 2} + + opts = knf.FrameExtractionOptions.from_dict(d) + + opts2 = knf.FrameExtractionOptions() + assert str(opts) != str(opts2) + + opts2.samp_freq = 10 + assert str(opts) != str(opts2) + + opts2.frame_shift_ms = 2 + assert str(opts) == str(opts2) + + opts2.frame_shift_ms = 3 + assert str(opts) != str(opts2) + + +def test_from_dict_full_and_as_dict(): + opts = knf.FrameExtractionOptions() + opts.samp_freq = 20 + opts.frame_length_ms = 100 + + d = opts.as_dict() + for key, value in d.items(): + assert value == getattr(opts, key) + + opts2 = knf.FrameExtractionOptions.from_dict(d) + assert str(opts2) == str(opts) + + d["window_type"] = "hanning" + opts3 = knf.FrameExtractionOptions.from_dict(d) + assert opts3.window_type == "hanning" + + +def test_pickle(): + opts = knf.FrameExtractionOptions() + opts.samp_freq = 44100 + opts.dither = 5.5 + data = pickle.dumps(opts) + + opts2 = pickle.loads(data) + assert str(opts) == str(opts2) + + +def main(): + test_default() + test_set_get() + test_from_empty_dict() + test_from_dict_partial() + test_from_dict_full_and_as_dict() + test_pickle() + + +if __name__ == "__main__": + main() diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_mel_bank_options.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_mel_bank_options.py new file mode 100755 index 0000000..cd10376 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_mel_bank_options.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021 Xiaomi Corporation (authors: Fangjun Kuang) + +import pickle + +import kaldi_native_fbank as knf + + +def test_default(): + opts = knf.MelBanksOptions() + assert opts.num_bins == 25 + assert opts.low_freq == 20 + assert opts.high_freq == 0 + assert opts.vtln_low == 100 + assert opts.vtln_high == -500 + assert opts.debug_mel is False + assert opts.htk_mode is False + assert opts.is_librosa is False + assert opts.norm == "slaney" + + +def test_set_get(): + opts = knf.MelBanksOptions() + opts.num_bins = 100 + assert opts.num_bins == 100 + + opts.low_freq = 22 + assert opts.low_freq == 22 + + opts.high_freq = 1 + assert opts.high_freq == 1 + + opts.vtln_low = 101 + assert opts.vtln_low == 101 + + opts.vtln_high = -100 + assert opts.vtln_high == -100 + + opts.debug_mel = True + assert opts.debug_mel is True + + opts.htk_mode = True + assert opts.htk_mode is True + + opts.is_librosa = True + assert opts.is_librosa is True + + opts.norm = "slaney" + assert opts.norm == "slaney" + + +def test_from_empty_dict(): + opts = knf.MelBanksOptions.from_dict({}) + opts2 = knf.MelBanksOptions() + + assert str(opts) == str(opts2) + + +def test_from_dict_partial(): + d = {"num_bins": 10, "debug_mel": True} + + opts = knf.MelBanksOptions.from_dict(d) + + opts2 = knf.MelBanksOptions() + assert str(opts) != str(opts2) + + opts2.num_bins = 10 + assert str(opts) != str(opts2) + + opts2.debug_mel = True + assert str(opts) == str(opts2) + + opts2.debug_mel = False + assert str(opts) != str(opts2) + + +def test_from_dict_full_and_as_dict(): + opts = knf.MelBanksOptions() + opts.num_bins = 80 + opts.vtln_high = 2 + + d = opts.as_dict() + for key, value in d.items(): + assert value == getattr(opts, key) + + opts2 = knf.MelBanksOptions.from_dict(d) + assert str(opts2) == str(opts) + + d["htk_mode"] = True + opts3 = knf.MelBanksOptions.from_dict(d) + assert opts3.htk_mode is True + + +def test_pickle(): + opts = knf.MelBanksOptions() + opts.num_bins = 100 + opts.low_freq = 22 + data = pickle.dumps(opts) + + opts2 = pickle.loads(data) + assert str(opts) == str(opts2) + + +def main(): + test_default() + test_set_get() + test_from_empty_dict() + test_from_dict_partial() + test_from_dict_full_and_as_dict() + test_pickle() + + +if __name__ == "__main__": + main() diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_fbank.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_fbank.py new file mode 100755 index 0000000..12f2c66 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_fbank.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +import sys + +try: + import kaldifeat +except: + print("Please install kaldifeat first") + sys.exit(0) + +import kaldi_native_fbank as knf +import torch + + +def main(): + sampling_rate = 16000 + samples = torch.randn(16000 * 10) + + opts = kaldifeat.FbankOptions() + opts.frame_opts.dither = 0 + opts.mel_opts.num_bins = 80 + opts.frame_opts.snip_edges = False + opts.mel_opts.debug_mel = False + + online_fbank = kaldifeat.OnlineFbank(opts) + + online_fbank.accept_waveform(sampling_rate, samples) + + opts = knf.FbankOptions() + opts.frame_opts.dither = 0 + opts.mel_opts.num_bins = 80 + opts.frame_opts.snip_edges = False + opts.mel_opts.debug_mel = False + + fbank = knf.OnlineFbank(opts) + fbank.accept_waveform(sampling_rate, samples.tolist()) + + assert online_fbank.num_frames_ready == fbank.num_frames_ready + for i in range(fbank.num_frames_ready): + f1 = online_fbank.get_frame(i) + f2 = torch.from_numpy(fbank.get_frame(i)) + assert torch.allclose(f1, f2, atol=1e-3), (i, (f1 - f2).abs().max()) + + +if __name__ == "__main__": + torch.manual_seed(20220825) + main() + print("success") diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_mfcc.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_mfcc.py new file mode 100755 index 0000000..794da95 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_mfcc.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import sys + +try: + import kaldifeat +except: + print("Please install kaldifeat first") + sys.exit(0) + +import kaldi_native_fbank as knf +import torch + + +def main(): + sampling_rate = 16000 + samples = torch.randn(sampling_rate * 10) + + opts = kaldifeat.MfccOptions() + opts.frame_opts.dither = 0 + opts.num_ceps = 40 + opts.mel_opts.num_bins = 40 + opts.mel_opts.high_freq = -200 + opts.frame_opts.snip_edges = False + online_mfcc = kaldifeat.OnlineMfcc(opts) + + online_mfcc.accept_waveform(sampling_rate, samples) + + opts = knf.MfccOptions() + opts.frame_opts.dither = 0 + opts.num_ceps = 40 + opts.mel_opts.num_bins = 40 + opts.mel_opts.high_freq = -200 + opts.frame_opts.snip_edges = False + + mfcc = knf.OnlineMfcc(opts) + mfcc.accept_waveform(sampling_rate, samples.tolist()) + + assert online_mfcc.num_frames_ready == mfcc.num_frames_ready + for i in range(mfcc.num_frames_ready): + f1 = online_mfcc.get_frame(i) + f2 = torch.from_numpy(mfcc.get_frame(i)) + assert torch.allclose(f1, f2, atol=1e-3), (i, (f1 - f2).abs().max(), f1, f2) + + +if __name__ == "__main__": + torch.manual_seed(20240603) + main() + print("success") diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_whisper_fbank.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_whisper_fbank.py new file mode 100755 index 0000000..5219f4a --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_online_whisper_fbank.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023 Xiaomi Corporation (authors: Fangjun Kuang) + +import pickle + +import kaldi_native_fbank as knf +import torch + + +def test(): + opts = knf.WhisperFeatureOptions() + + # Use 128 for whisper large v3 + opts.dim = 128 + online_whisper_fbank = knf.OnlineWhisperFbank(opts) + + audio = torch.rand(100000) + # audio should be normalized into the range [-1, 1] + print(audio.shape, audio.max(), audio.min()) + online_whisper_fbank.accept_waveform(sampling_rate=16000, waveform=audio.numpy()) + online_whisper_fbank.input_finished() + print(online_whisper_fbank.num_frames_ready) + + features = [] + for i in range(online_whisper_fbank.num_frames_ready): + f = online_whisper_fbank.get_frame(i) + f = torch.from_numpy(f) + features.append(f) + + features = torch.stack(features) + print(features.shape) + + log_spec = torch.clamp(features, min=1e-10).log10() + log_spec = torch.maximum(log_spec, log_spec.max() - 8.0) + mel = (log_spec + 4.0) / 4.0 + target = 3000 + mel = torch.nn.functional.pad(mel, (0, 0, 0, target - mel.shape[0]), "constant", 0) + mel = mel.t().unsqueeze(0) + print(mel.shape) + + assert mel.shape == (1, opts.dim, 3000), mel.shape + # Now you can input 'mel' to whisper.encoder model + + +def main(): + test() + + +if __name__ == "__main__": + torch.manual_seed(20240112) + main() diff --git a/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_rfft.py b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_rfft.py new file mode 100755 index 0000000..8852483 --- /dev/null +++ b/cli/fbank/sys/knf/kaldi-native-fbank/python/tests/test_rfft.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021-2023 Xiaomi Corporation (authors: Fangjun Kuang) + + +import torch + +import kaldi_native_fbank as knf + + +def test_rfft(): + N = 12 + t = torch.arange(N) + r = torch.fft.rfft(t) + assert len(r) == N // 2 + 1, (len(r), N // 2 + 1) + + real = r.real + imag = r.imag + print(r) + + k = t.tolist() + rfft = knf.Rfft(N) + + p = rfft.compute(k) + print(p) + return + + assert abs(p[0] - real[0]) < 1e-5, (p[0], real[0]) + assert imag[0] == 0, imag[0] + + assert abs(p[1] - real[-1]) < 1e-5, (p[1], real[-1]) + assert imag[-1] == 0, imag[-1] + + for i in range(1, N // 2): + assert abs(p[2 * i] - real[i]) < 1e-5, (p[2 * i], real[i]) + # Note: the imaginary part is multiplied by negative 1 + assert abs(p[2 * i + 1] - -1 * imag[i]) < 1e-5, (p[2 * i + 1], imag[i]) + + +def main(): + test_rfft() + + +if __name__ == "__main__": + main() diff --git a/cli/fbank/sys/knf/scripts/check_style_cpplint.sh b/cli/fbank/sys/knf/scripts/check_style_cpplint.sh new file mode 100755 index 0000000..2fc150e --- /dev/null +++ b/cli/fbank/sys/knf/scripts/check_style_cpplint.sh @@ -0,0 +1,126 @@ +#!/bin/bash +# +# Copyright 2020 Mobvoi Inc. (authors: Fangjun Kuang) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Usage: +# +# (1) To check files of the last commit +# ./scripts/check_style_cpplint.sh +# +# (2) To check changed files not committed yet +# ./scripts/check_style_cpplint.sh 1 +# +# (3) To check all files in the project +# ./scripts/check_style_cpplint.sh 2 + + +cpplint_version="1.5.4" +cur_dir=$(cd $(dirname $BASH_SOURCE) && pwd) +kaldi_native_fbank_dir=$(cd $cur_dir/.. && pwd) + +build_dir=$kaldi_native_fbank_dir/build +mkdir -p $build_dir + +cpplint_src=$build_dir/cpplint-${cpplint_version}/cpplint.py + +if [ ! -d "$build_dir/cpplint-${cpplint_version}" ]; then + pushd $build_dir + if command -v wget &> /dev/null; then + wget https://github.com/cpplint/cpplint/archive/${cpplint_version}.tar.gz + elif command -v curl &> /dev/null; then + curl -O -SL https://github.com/cpplint/cpplint/archive/${cpplint_version}.tar.gz + else + echo "Please install wget or curl to download cpplint" + exit 1 + fi + tar xf ${cpplint_version}.tar.gz + rm ${cpplint_version}.tar.gz + + # cpplint will report the following error for: __host__ __device__ ( + # + # Extra space before ( in function call [whitespace/parens] [4] + # + # the following patch disables the above error + sed -i "3490i\ not Search(r'__host__ __device__\\\s+\\\(', fncall) and" $cpplint_src + popd +fi + +source $kaldi_native_fbank_dir/scripts/utils.sh + +# return true if the given file is a c++ source file +# return false otherwise +function is_source_code_file() { + case "$1" in + *.cc|*.h|*.cu) + echo true;; + *) + echo false;; + esac +} + +function check_style() { + python3 $cpplint_src $1 || abort $1 +} + +function check_last_commit() { + files=$(git diff HEAD^1 --name-only --diff-filter=ACDMRUXB) + echo $files +} + +function check_current_dir() { + files=$(git status -s -uno --porcelain | awk '{ + if (NF == 4) { + # a file has been renamed + print $NF + } else { + print $2 + }}') + + echo $files +} + +function do_check() { + case "$1" in + 1) + echo "Check changed files" + files=$(check_current_dir) + ;; + 2) + echo "Check all files" + files=$(find $kaldi_native_fbank_dir/kaldi-native-fbank -name "*.h" -o -name "*.cc" -o -name "*.cu") + ;; + *) + echo "Check last commit" + files=$(check_last_commit) + ;; + esac + + for f in $files; do + need_check=$(is_source_code_file $f) + if $need_check; then + [[ -f $f ]] && check_style $f + fi + done +} + +function main() { + do_check $1 + + ok "Great! Style check passed!" +} + +cd $kaldi_native_fbank_dir + +main $1 diff --git a/cli/fbank/sys/knf/scripts/utils.sh b/cli/fbank/sys/knf/scripts/utils.sh new file mode 100644 index 0000000..fb424a7 --- /dev/null +++ b/cli/fbank/sys/knf/scripts/utils.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +default='\033[0m' +bold='\033[1m' +red='\033[31m' +green='\033[32m' + +function ok() { + printf "${bold}${green}[OK]${default} $1\n" +} + +function error() { + printf "${bold}${red}[FAILED]${default} $1\n" +} + +function abort() { + printf "${bold}${red}[FAILED]${default} $1\n" + exit 1 +} diff --git a/cli/fbank/sys/knf/setup.py b/cli/fbank/sys/knf/setup.py new file mode 100644 index 0000000..de3d8a2 --- /dev/null +++ b/cli/fbank/sys/knf/setup.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021 Xiaomi Corporation (author: Fangjun Kuang) + +import re + +import setuptools + +from cmake.cmake_extension import BuildExtension, bdist_wheel, cmake_extension + + +def read_long_description(): + with open("README.md", encoding="utf8") as f: + readme = f.read() + return readme + + +def get_package_version(): + with open("CMakeLists.txt") as f: + content = f.read() + + match = re.search(r"set\(KALDI_NATIVE_FBANK_VERSION (.*)\)", content) + latest_version = match.group(1).strip('"') + return latest_version + + +package_name = "kaldi-native-fbank" + +with open("kaldi-native-fbank/python/kaldi_native_fbank/__init__.py", "a") as f: + f.write(f"__version__ = '{get_package_version()}'\n") + +setuptools.setup( + name=package_name, + version=get_package_version(), + author="Fangjun Kuang", + author_email="csukuangfj@gmail.com", + package_dir={"kaldi_native_fbank": "kaldi-native-fbank/python/kaldi_native_fbank"}, + packages=["kaldi_native_fbank"], + url="https://github.com/csukuangfj/kaldi-native-fbank", + long_description=read_long_description(), + long_description_content_type="text/markdown", + ext_modules=[cmake_extension("_kaldi_native_fbank")], + cmdclass={"build_ext": BuildExtension, "bdist_wheel": bdist_wheel}, + zip_safe=False, + classifiers=[ + "Programming Language :: C++", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + ], + python_requires=">=3.6.0", + license="Apache licensed, as found in the LICENSE file", +) + +# remove the line __version__ from kaldi-native-fbank/python/kaldi_native_fbank/__init__.py +with open("kaldi-native-fbank/python/kaldi_native_fbank/__init__.py", "r") as f: + lines = f.readlines() + +with open("kaldi-native-fbank/python/kaldi_native_fbank/__init__.py", "w") as f: + for line in lines: + if "__version__" in line: + # skip __version__ = "x.x.x" + continue + f.write(line) diff --git a/cli/fbank/sys/knf/toolchains/README.md b/cli/fbank/sys/knf/toolchains/README.md new file mode 100644 index 0000000..41924c0 --- /dev/null +++ b/cli/fbank/sys/knf/toolchains/README.md @@ -0,0 +1,11 @@ +# gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz + +Go to to download the toolchain. + +```bash +mkdir /ceph-fj/fangjun/software +cd /ceph-fj/fangjun/software +tar xvf /path/to/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf.tar.xz + +export PATH=/ceph-fj/fangjun/software/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin:$PATH +``` diff --git a/cli/fbank/sys/knf/toolchains/arm-linux-gnueabihf.toolchain.cmake b/cli/fbank/sys/knf/toolchains/arm-linux-gnueabihf.toolchain.cmake new file mode 100644 index 0000000..abe1a22 --- /dev/null +++ b/cli/fbank/sys/knf/toolchains/arm-linux-gnueabihf.toolchain.cmake @@ -0,0 +1,17 @@ +# Copied from https://github.com/Tencent/ncnn/blob/master/toolchains/arm-linux-gnueabihf.toolchain.cmake +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(CMAKE_C_COMPILER "arm-linux-gnueabihf-gcc") +set(CMAKE_CXX_COMPILER "arm-linux-gnueabihf-g++") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_C_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=neon") +set(CMAKE_CXX_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=neon") + +# cache flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags") diff --git a/cli/fbank/sys/knfc/CMakeLists.txt b/cli/fbank/sys/knfc/CMakeLists.txt new file mode 100644 index 0000000..74b42e3 --- /dev/null +++ b/cli/fbank/sys/knfc/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.10) + +project(knfc) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) +set(KALDI_NATIVE_FBANK_BUILD_PYTHON OFF CACHE BOOL "Disable Python build") +set(KALDI_NATIVE_FBANK_BUILD_TESTS OFF CACHE BOOL "Disable Tests build") + +add_subdirectory(../knf ${CMAKE_BINARY_DIR}/knf) + +add_library(knfc STATIC knfc.cc) + +target_link_libraries(knfc PRIVATE kaldi-native-fbank-core) + +target_include_directories(knfc PRIVATE + ${KNF_LIB_PATH}/include + ../knf/kaldi-native-fbank/csrc + ../knf +) + +install(TARGETS knfc + ARCHIVE DESTINATION lib) \ No newline at end of file diff --git a/cli/fbank/sys/knfc/knfc.cc b/cli/fbank/sys/knfc/knfc.cc new file mode 100644 index 0000000..a82eaca --- /dev/null +++ b/cli/fbank/sys/knfc/knfc.cc @@ -0,0 +1,48 @@ +#include "kaldi-native-fbank/csrc/feature-window.h" +#include "kaldi-native-fbank/csrc/mel-computations.h" +#include "kaldi-native-fbank/csrc/feature-fbank.h" +#include "kaldi-native-fbank/csrc/online-feature.h" +#include "knfc.h" +#include + +void DestroyFbankResult(FbankResult* result) { + if (result) { + delete[] result->frames; + result->frames = nullptr; + result->num_frames = 0; + } +} + +FbankResult ComputeFbank(const float *waveform, int32_t waveform_size) { + knf::FbankOptions options; + options.htk_compat = true; + options.use_energy = false; + + // Frame options + options.frame_opts.window_type = "hanning"; + options.frame_opts.dither = 0.0; + + // Mel options + options.mel_opts.num_bins = 128; + + knf::OnlineFbank fbank(options); + fbank.AcceptWaveform(options.frame_opts.samp_freq, waveform, waveform_size); + fbank.InputFinished(); + + int32_t num_frames = fbank.NumFramesReady(); + int32_t num_bins = options.mel_opts.num_bins; + + // Allocate memory for the frames + float* frames = new float[num_frames * num_bins]; + + // Fill the frames + for (int32_t i = 0; i < num_frames; i++) { + const float* frame = fbank.GetFrame(i); + std::copy(frame, frame + num_bins, frames + i * num_bins); + } + + FbankResult result; + result.frames = frames; + result.num_frames = num_frames; + return result; +} diff --git a/cli/fbank/sys/knfc/knfc.h b/cli/fbank/sys/knfc/knfc.h new file mode 100644 index 0000000..3e4db75 --- /dev/null +++ b/cli/fbank/sys/knfc/knfc.h @@ -0,0 +1,18 @@ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + float* frames; + int32_t num_frames; +} FbankResult; + +extern "C" void DestroyFbankResult(FbankResult* result); + +FbankResult ComputeFbank(const float *waveform, int32_t waveform_size); + +#ifdef __cplusplus +} +#endif diff --git a/cli/fbank/sys/src/lib.rs b/cli/fbank/sys/src/lib.rs new file mode 100644 index 0000000..a38a13a --- /dev/null +++ b/cli/fbank/sys/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/cli/fbank/sys/wrapper.hpp b/cli/fbank/sys/wrapper.hpp new file mode 100644 index 0000000..d7bb2d7 --- /dev/null +++ b/cli/fbank/sys/wrapper.hpp @@ -0,0 +1 @@ +#include "knfc/knfc.h" \ No newline at end of file diff --git a/cli/src/audio.rs b/cli/src/audio.rs new file mode 100644 index 0000000..e639aa1 --- /dev/null +++ b/cli/src/audio.rs @@ -0,0 +1,53 @@ +use anyhow::{Context, Error, Result}; +use async_walkdir::DirEntry; +use fon::{chan::Ch32, Audio}; +use itertools::Itertools; +use knf_rs::compute_fbank; +use rodio::{Decoder, Source}; +use std::io::Cursor; + +pub const SAMPLE_RATE: u32 = 16000; +pub const NUM_FRAMES: usize = 1024; +pub const NUM_MEL_BINS: usize = 128; + +pub async fn is_audio_file(entry: DirEntry) -> Result { + if let Some(mime) = mime_guess::from_path(entry.path()).first() { + if mime.type_() != "audio" { + return Ok(false); + } + } + Ok(true) +} + +fn extract_samples(decoder: Decoder>>) -> Result<(Box<[f32]>, u32)> { + let channels = decoder.channels() as usize; + let sample_rate = decoder.sample_rate(); + let samples = decoder + .map(|sample| sample as f32 / i16::MAX as f32) + .chunks(channels) + .into_iter() + .map(|chunk| chunk.into_iter().sum::() / channels as f32) + .collect_vec() + .into_boxed_slice(); + Ok((samples, sample_rate)) +} + +pub fn extract_audio(buffer: Vec) -> Result> { + let cursor = Cursor::new(buffer); + let decoder = Decoder::new(cursor).with_context(|| "Failed to initialize decoder")?; + let (samples, sample_rate) = + extract_samples(decoder).with_context(|| "Failed to extract samples")?; + let audio = Audio::with_f32_buffer(sample_rate, samples); + Ok(audio) +} + +pub fn resample(audio: &mut Audio, target_sample_rate: u32) { + *audio = Audio::with_audio(target_sample_rate, audio); +} + +pub fn create_fbank(audio: &mut Audio) -> Result> { + let samples = audio.as_f32_slice(); + let mut fbank = compute_fbank(samples).map_err(|e| Error::msg(e.to_string()))?; + fbank.truncate(NUM_FRAMES); + Ok(fbank.into_boxed_slice()) +} diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 0000000..76e0b1c --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,164 @@ +use anyhow::{Context, Error, Result}; +use async_walkdir::{Filtering, WalkDir}; +use clap::{Parser, Subcommand}; +use spinoff::{spinners, Spinner}; +use std::path::{Path, PathBuf}; +use tokio::{ + fs::{copy, rename}, + sync::Semaphore, + task::JoinSet, +}; +use tokio_stream::StreamExt; + +pub mod audio; +use audio::is_audio_file; + +pub mod processing; +use processing::{get_result_path, process, Label, ResultPathOptions}; + +const MAX_OPEN_FILES: usize = 128; +static PERMITS: Semaphore = Semaphore::const_new(MAX_OPEN_FILES); + +#[derive(Parser)] +#[command(about, long_about = None, version)] +struct Args { + #[command(subcommand)] + command: Option, + + #[arg(required = true)] + path: PathBuf, + + #[arg(short, long, default_value = "0.0.0.0:8000")] + address: String, +} + +#[derive(Subcommand, Debug)] +pub enum Command { + #[command()] + Copy { + #[arg(short, long)] + speech_dir: Option, + + #[arg(short, long)] + music_dir: Option, + + #[arg(short, long)] + noise_dir: Option, + }, + + #[command()] + Move { + #[arg(short, long)] + speech_dir: Option, + + #[arg(short, long)] + music_dir: Option, + + #[arg(short, long)] + noise_dir: Option, + }, +} + +impl Command { + async fn perform(&self, path: &Path, label: &Label, options: &ResultPathOptions) -> Result<()> { + if let Some(dir) = get_result_path(path, label, options) { + match self { + Command::Copy { .. } => { + let _ = copy(path, &dir).await?; + return Ok(()); + } + Command::Move { .. } => { + rename(path, &dir).await?; + return Ok(()); + } + } + } + Ok(()) + } +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + let args = Args::parse(); + let url = format!("http:/{}/", args.address); + let options = match args.command { + Some(Command::Copy { + ref speech_dir, + ref music_dir, + ref noise_dir, + }) => Some(ResultPathOptions { + speech_dir: speech_dir.clone(), + music_dir: music_dir.clone(), + noise_dir: noise_dir.clone(), + }), + Some(Command::Move { + ref speech_dir, + ref music_dir, + ref noise_dir, + }) => Some(ResultPathOptions { + speech_dir: speech_dir.clone(), + music_dir: music_dir.clone(), + noise_dir: noise_dir.clone(), + }), + _ => None, + }; + + let mut spinner = Spinner::new(spinners::Line, "Loading...", None); + + if args.path.is_file() { + let permit = PERMITS + .acquire() + .await + .with_context(|| "Failed to acquire permit")?; + spinner.update_text(format!("Labelling {:?}", args.path)); + let (path, label) = process(args.path.clone(), url.clone(), permit) + .await + .with_context(|| format!("Failed to process {:?}", args.path))?; + if let Some(ref command) = args.command { + command + .perform(&path, &label, &options.unwrap()) + .await + .with_context(|| format!("failed to perform command {:?}", command))?; + } else { + spinner.stop_with_message(&format!("{:?}: {:?}", path, label)); + } + return Ok(()); + } + + let mut entries = WalkDir::new(args.path).filter(|entry| async move { + if is_audio_file(entry).await.unwrap_or(false) { + Filtering::Continue + } else { + Filtering::Ignore + } + }); + let mut jobs = JoinSet::new(); + loop { + match entries.next().await { + Some(Ok(entry)) => { + let permit = PERMITS + .acquire() + .await + .with_context(|| "Failed to acquire permit")?; + let path = entry.path(); + spinner.update_text(format!("Labelling {:?}", path)); + let future = process(path, url.clone(), permit); + jobs.spawn(future); + } + Some(Err(e)) => return Err(e.into()), + None => break, + }; + } + spinner.stop(); + while let Some(result) = jobs.join_next().await { + let (path, label) = result?.with_context(|| "Failed to label a file")?; + println!("{:?}: {:?}", path, label); + if let Some(ref command) = args.command { + command + .perform(&path, &label, &options.clone().unwrap()) + .await + .with_context(|| format!("failed to perform command {:?}", command))?; + } + } + Ok(()) +} diff --git a/cli/src/processing.rs b/cli/src/processing.rs new file mode 100644 index 0000000..145b517 --- /dev/null +++ b/cli/src/processing.rs @@ -0,0 +1,119 @@ +use crate::audio::{create_fbank, extract_audio, resample, NUM_MEL_BINS, SAMPLE_RATE}; +use anyhow::{Context, Result}; +use byte_slice_cast::AsByteSlice; +use reqwest::Client; +use safetensors::{serialize, tensor::TensorView, Dtype}; +use serde::Deserialize; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; +use tokio::{ + fs::File, + io::AsyncReadExt, + sync::SemaphorePermit, + task::{block_in_place, spawn_blocking}, +}; + +#[derive(Debug, Clone, Copy, Deserialize)] +pub enum Label { + Speech, + Music, + Noise, +} + +async fn label<'a>(tensor: &'a TensorView<'a>, url: String) -> Result