mirror of
https://github.com/openai/codex.git
synced 2026-09-17 12:23:33 +00:00
Route TUI realtime audio through shared echo cancellation
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
103
MODULE.bazel
103
MODULE.bazel
@@ -278,6 +278,74 @@ http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "ht
|
||||
http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
|
||||
new_local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")
|
||||
|
||||
http_archive(
|
||||
name = "ninja_1.11.0_linux",
|
||||
urls = [
|
||||
"https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-linux.zip",
|
||||
],
|
||||
sha256 = "9726e730d5b8599f82654dc80265e64a10a8a817552c34153361ed0c017f9f02",
|
||||
strip_prefix = "",
|
||||
build_file_content = """\
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "ninja_bin",
|
||||
srcs = ["ninja"],
|
||||
)
|
||||
""",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "ninja_1.12.1_linux_aarch64",
|
||||
urls = [
|
||||
"https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux-aarch64.zip",
|
||||
],
|
||||
sha256 = "5c25c6570b0155e95fce5918cb95f1ad9870df5768653afe128db822301a05a1",
|
||||
strip_prefix = "",
|
||||
build_file_content = """\
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "ninja_bin",
|
||||
srcs = ["ninja"],
|
||||
)
|
||||
""",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "ninja_1.11.0_mac",
|
||||
urls = [
|
||||
"https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-mac.zip",
|
||||
],
|
||||
sha256 = "21915277db59756bfc61f6f281c1f5e3897760b63776fd3d360f77dd7364137f",
|
||||
strip_prefix = "",
|
||||
build_file_content = """\
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "ninja_bin",
|
||||
srcs = ["ninja"],
|
||||
)
|
||||
""",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "ninja_1.11.0_win",
|
||||
urls = [
|
||||
"https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-win.zip",
|
||||
],
|
||||
sha256 = "d0ee3da143211aa447e750085876c9b9d7bcdd637ab5b2c5b41349c617f22f3b",
|
||||
strip_prefix = "",
|
||||
build_file_content = """\
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "ninja_bin",
|
||||
srcs = ["ninja.exe"],
|
||||
)
|
||||
""",
|
||||
)
|
||||
|
||||
new_local_repository(
|
||||
name = "v8_targets",
|
||||
build_file = "//third_party/v8:BUILD.bazel",
|
||||
@@ -345,6 +413,41 @@ crate.annotation(
|
||||
|
||||
inject_repo(crate, "alsa_lib")
|
||||
|
||||
bazel_dep(name = "meson", version = "1.5.1.bcr.1")
|
||||
|
||||
crate.annotation(
|
||||
build_script_tools = [
|
||||
"@meson//:meson",
|
||||
"@ninja_1.11.0_linux//:ninja_bin",
|
||||
"@ninja_1.12.1_linux_aarch64//:ninja_bin",
|
||||
"@ninja_1.11.0_mac//:ninja_bin",
|
||||
"@ninja_1.11.0_win//:ninja_bin",
|
||||
],
|
||||
build_script_env = {
|
||||
"MESON": "$(execpath @meson//:meson)",
|
||||
},
|
||||
build_script_env_select = {
|
||||
"aarch64-apple-darwin": "{\"NINJA_REAL\":\"$(execpath @ninja_1.12.1_linux_aarch64//:ninja_bin)\"}",
|
||||
"x86_64-apple-darwin": "{\"NINJA_REAL\":\"$(execpath @ninja_1.12.1_linux_aarch64//:ninja_bin)\"}",
|
||||
"aarch64-unknown-linux-gnu": "{\"NINJA_REAL\":\"$(execpath @ninja_1.11.0_linux//:ninja_bin)\"}",
|
||||
"x86_64-unknown-linux-gnu": "{\"NINJA_REAL\":\"$(execpath @ninja_1.11.0_linux//:ninja_bin)\"}",
|
||||
"aarch64-unknown-linux-musl": "{\"NINJA_REAL\":\"$(execpath @ninja_1.11.0_linux//:ninja_bin)\"}",
|
||||
"x86_64-unknown-linux-musl": "{\"NINJA_REAL\":\"$(execpath @ninja_1.11.0_linux//:ninja_bin)\"}",
|
||||
"aarch64-pc-windows-msvc": "{\"NINJA\":\"$(execpath @ninja_1.11.0_win//:ninja_bin)\"}",
|
||||
"x86_64-pc-windows-msvc": "{\"NINJA\":\"$(execpath @ninja_1.11.0_win//:ninja_bin)\"}",
|
||||
"aarch64-pc-windows-gnullvm": "{\"NINJA\":\"$(execpath @ninja_1.11.0_win//:ninja_bin)\"}",
|
||||
"x86_64-pc-windows-gnullvm": "{\"NINJA\":\"$(execpath @ninja_1.11.0_win//:ninja_bin)\"}",
|
||||
},
|
||||
crate = "webrtc-audio-processing-sys",
|
||||
gen_build_script = "on",
|
||||
patch_args = ["-p1"],
|
||||
patches = [
|
||||
"//patches:webrtc-audio-processing-sys_meson_env.patch",
|
||||
"//patches:webrtc-audio-processing-sys_logging_macos_sdk.patch",
|
||||
],
|
||||
)
|
||||
|
||||
inject_repo(crate, "meson", "ninja_1.11.0_linux", "ninja_1.12.1_linux_aarch64", "ninja_1.11.0_mac", "ninja_1.11.0_win")
|
||||
bazel_dep(name = "v8", version = "14.6.202.9")
|
||||
archive_override(
|
||||
module_name = "v8",
|
||||
|
||||
11
MODULE.bazel.lock
generated
11
MODULE.bazel.lock
generated
@@ -88,6 +88,8 @@
|
||||
"https://bcr.bazel.build/modules/llvm/0.6.7/MODULE.bazel": "d37a2e10571864dc6a5bb53c29216d90b9400bbcadb422337f49107fd2eaf0d2",
|
||||
"https://bcr.bazel.build/modules/llvm/0.6.8/MODULE.bazel": "53468e4a4be409c2d34e5b7331d2e1fef982151b777655ca3c0047225b333629",
|
||||
"https://bcr.bazel.build/modules/llvm/0.6.8/source.json": "b673af466f716e01d6243f59e47729e99f37dc5e17026d2bf18c98206f09b6c5",
|
||||
"https://bcr.bazel.build/modules/meson/1.5.1.bcr.1/MODULE.bazel": "fd48110cf099881cb57d7bd98f81d4fba231956b96df3ab5352c03950371701c",
|
||||
"https://bcr.bazel.build/modules/meson/1.5.1.bcr.1/source.json": "98f39eef2bac9db11b898013f748668ccb7f94cb072519d754d2c1c8a89261e7",
|
||||
"https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74",
|
||||
"https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9",
|
||||
"https://bcr.bazel.build/modules/openssl/3.5.4.bcr.0/MODULE.bazel": "0f6b8f20b192b9ff0781406256150bcd46f19e66d807dcb0c540548439d6fc35",
|
||||
@@ -105,6 +107,8 @@
|
||||
"https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580",
|
||||
"https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96",
|
||||
"https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
|
||||
"https://bcr.bazel.build/modules/protobuf/23.1/MODULE.bazel": "88b393b3eb4101d18129e5db51847cd40a5517a53e81216144a8c32dfeeca52a",
|
||||
"https://bcr.bazel.build/modules/protobuf/24.4/MODULE.bazel": "7bc7ce5f2abf36b3b7b7c8218d3acdebb9426aeb35c2257c96445756f970eb12",
|
||||
"https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
|
||||
"https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d",
|
||||
"https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df",
|
||||
@@ -151,6 +155,7 @@
|
||||
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
|
||||
"https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
|
||||
"https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.1.0/MODULE.bazel": "30d9135a2b6561c761bd67bd4990da591e6bdc128790ce3e7afd6a3558b2fb64",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
|
||||
@@ -194,6 +199,7 @@
|
||||
"https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.35.0/MODULE.bazel": "c3657951764cdcdb5a7370d5e885fad5e8c1583320aad18d46f9f110d2c22755",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
|
||||
"https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43",
|
||||
"https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13",
|
||||
@@ -226,6 +232,7 @@
|
||||
"https://bcr.bazel.build/modules/tar.bzl/0.9.0/MODULE.bazel": "452a22d7f02b1c9d7a22ab25edf20f46f3e1101f0f67dc4bfbf9a474ddf02445",
|
||||
"https://bcr.bazel.build/modules/tar.bzl/0.9.0/source.json": "c732760a374831a2cf5b08839e4be75017196b4d796a5aa55235272ee17cd839",
|
||||
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
|
||||
"https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9",
|
||||
"https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/MODULE.bazel": "b573395fe63aef4299ba095173e2f62ccfee5ad9bbf7acaa95dba73af9fc2b38",
|
||||
"https://bcr.bazel.build/modules/with_cfg.bzl/0.12.0/source.json": "3f3fbaeafecaf629877ad152a2c9def21f8d330d91aa94c5dc75bbb98c10b8b8",
|
||||
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
|
||||
@@ -646,6 +653,7 @@
|
||||
"atoi_2.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4.0\"},{\"default_features\":false,\"name\":\"num-traits\",\"req\":\"^0.2.14\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"num-traits/std\"]}}",
|
||||
"atomic-waker_1.1.2": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.5\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.7.0\"}],\"features\":{}}",
|
||||
"autocfg_1.5.0": "{\"dependencies\":[],\"features\":{}}",
|
||||
"autotools_0.2.7": "{\"dependencies\":[{\"name\":\"cc\",\"req\":\"^1.0.3\"}],\"features\":{}}",
|
||||
"aws-lc-rs_1.16.2": "{\"dependencies\":[{\"name\":\"aws-lc-fips-sys\",\"optional\":true,\"req\":\"^0.13.1\"},{\"default_features\":false,\"name\":\"aws-lc-sys\",\"optional\":true,\"req\":\"^0.39.0\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.4\"},{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"paste\",\"req\":\"^1.0.15\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.11.1\"},{\"name\":\"untrusted\",\"optional\":true,\"req\":\"^0.7.1\"},{\"name\":\"zeroize\",\"req\":\"^1.8.1\"}],\"features\":{\"alloc\":[],\"asan\":[\"aws-lc-sys?/asan\",\"aws-lc-fips-sys?/asan\"],\"bindgen\":[\"aws-lc-sys?/bindgen\",\"aws-lc-fips-sys?/bindgen\"],\"default\":[\"aws-lc-sys\",\"alloc\",\"ring-io\",\"ring-sig-verify\"],\"dev-tests-only\":[],\"fips\":[\"dep:aws-lc-fips-sys\"],\"non-fips\":[\"aws-lc-sys\"],\"prebuilt-nasm\":[\"aws-lc-sys?/prebuilt-nasm\"],\"ring-io\":[\"dep:untrusted\"],\"ring-sig-verify\":[\"dep:untrusted\"],\"test_logging\":[],\"unstable\":[]}}",
|
||||
"aws-lc-sys_0.39.0": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"bindgen\",\"optional\":true,\"req\":\"^0.72.0\"},{\"features\":[\"parallel\"],\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.2.26\"},{\"kind\":\"build\",\"name\":\"cmake\",\"req\":\"^0.1.54\"},{\"kind\":\"build\",\"name\":\"dunce\",\"req\":\"^1.0.5\"},{\"kind\":\"build\",\"name\":\"fs_extra\",\"req\":\"^1.3.0\"}],\"features\":{\"all-bindings\":[],\"asan\":[],\"bindgen\":[\"dep:bindgen\"],\"default\":[\"all-bindings\"],\"disable-prebuilt-nasm\":[],\"fips\":[\"dep:bindgen\"],\"prebuilt-nasm\":[],\"ssl\":[\"bindgen\",\"all-bindings\"]}}",
|
||||
"axum-core_0.5.6": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1.2\"},{\"name\":\"futures-core\",\"req\":\"^0.3\"},{\"name\":\"http\",\"req\":\"^1.0.0\"},{\"name\":\"http-body\",\"req\":\"^1.0.0\"},{\"name\":\"http-body-util\",\"req\":\"^0.1.0\"},{\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1.0.0\"},{\"name\":\"mime\",\"req\":\"^0.3.16\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"},{\"name\":\"sync_wrapper\",\"req\":\"^1.0.0\"},{\"features\":[\"macros\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.25.0\"},{\"features\":[\"limit\"],\"name\":\"tower-http\",\"optional\":true,\"req\":\"^0.6.0\"},{\"features\":[\"limit\"],\"kind\":\"dev\",\"name\":\"tower-http\",\"req\":\"^0.6.0\"},{\"name\":\"tower-layer\",\"req\":\"^0.3\"},{\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.37\"}],\"features\":{\"__private_docs\":[\"dep:tower-http\"],\"tracing\":[\"dep:tracing\"]}}",
|
||||
@@ -1553,6 +1561,9 @@
|
||||
"webpki-root-certs_1.0.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"}],\"features\":{}}",
|
||||
"webpki-roots_0.26.11": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"name\":\"parent\",\"package\":\"webpki-roots\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.13\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.102\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"yasna\",\"req\":\"^0.5.2\"}],\"features\":{}}",
|
||||
"webpki-roots_1.0.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"^0.4.3\"},{\"kind\":\"dev\",\"name\":\"percent-encoding\",\"req\":\"^2.3\"},{\"default_features\":false,\"name\":\"pki-types\",\"package\":\"rustls-pki-types\",\"req\":\"^1.8\"},{\"kind\":\"dev\",\"name\":\"rcgen\",\"req\":\"^0.14.3\"},{\"kind\":\"dev\",\"name\":\"ring\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"rustls\",\"req\":\"^0.23\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"webpki\",\"package\":\"rustls-webpki\",\"req\":\"^0.103\"},{\"kind\":\"dev\",\"name\":\"x509-parser\",\"req\":\"^0.17.0\"},{\"kind\":\"dev\",\"name\":\"yasna\",\"req\":\"^0.5.2\"}],\"features\":{}}",
|
||||
"webrtc-audio-processing-config_2.0.3": "{\"dependencies\":[{\"name\":\"schemars\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"name\":\"strum\",\"optional\":true,\"req\":\"^0.27\"}],\"features\":{\"schemars\":[\"dep:schemars\"],\"serde\":[\"dep:serde\"],\"strum\":[\"dep:strum\"]}}",
|
||||
"webrtc-audio-processing-sys_2.0.3": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"autotools\",\"req\":\"^0.2\"},{\"kind\":\"build\",\"name\":\"bindgen\",\"req\":\"^0.72\"},{\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"fs_extra\",\"req\":\"^1\"},{\"kind\":\"build\",\"name\":\"pkg-config\",\"req\":\"^0.3\"},{\"kind\":\"build\",\"name\":\"regex\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"bundled\":[],\"experimental-aec3-config\":[\"bundled\"],\"serde\":[\"dep:serde\"]}}",
|
||||
"webrtc-audio-processing_2.0.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"crossbeam-channel\",\"req\":\"^0.5\"},{\"features\":[\"termination\"],\"kind\":\"dev\",\"name\":\"ctrlc\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"hound\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"json5\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"portaudio\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_ignored\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"structopt\",\"req\":\"^0.3\"},{\"name\":\"webrtc-audio-processing-config\",\"req\":\"^2.0.3\"},{\"name\":\"webrtc-audio-processing-sys\",\"req\":\"^2.0.3\"}],\"features\":{\"bundled\":[\"webrtc-audio-processing-sys/bundled\"],\"experimental-aec3-config\":[\"webrtc-audio-processing-sys/experimental-aec3-config\"],\"schemars\":[\"webrtc-audio-processing-config/schemars\"],\"serde\":[\"webrtc-audio-processing-config/serde\",\"webrtc-audio-processing-sys/serde\",\"dep:serde\"],\"strum\":[\"webrtc-audio-processing-config/strum\"]}}",
|
||||
"webrtc-data_0.17.1": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.28\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.3\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"portable-atomic\",\"req\":\"^1.6\"},{\"name\":\"sctp\",\"package\":\"webrtc-sctp\",\"req\":\"^0.17.1\"},{\"name\":\"thiserror\",\"req\":\"^1\"},{\"features\":[\"fs\",\"io-util\",\"io-std\",\"macros\",\"net\",\"parking_lot\",\"rt\",\"rt-multi-thread\",\"sync\",\"time\"],\"name\":\"tokio\",\"req\":\"^1.32.0\"},{\"default_features\":false,\"features\":[\"conn\",\"marshal\"],\"name\":\"util\",\"package\":\"webrtc-util\",\"req\":\"^0.17.1\"}],\"features\":{}}",
|
||||
"webrtc-ice_0.17.1": "{\"dependencies\":[{\"name\":\"arc-swap\",\"req\":\"^1\"},{\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.28\"},{\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^3\"},{\"name\":\"crc\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.3\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^0.14.27\"},{\"kind\":\"dev\",\"name\":\"ipnet\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"mdns\",\"package\":\"webrtc-mdns\",\"req\":\"^0.17.1\"},{\"name\":\"portable-atomic\",\"req\":\"^1.6\"},{\"name\":\"rand\",\"req\":\"^0.9\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.9.5\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"sha1\",\"req\":\"^0.10\"},{\"name\":\"stun\",\"req\":\"^0.17.0\"},{\"name\":\"thiserror\",\"req\":\"^1\"},{\"features\":[\"fs\",\"io-util\",\"io-std\",\"macros\",\"net\",\"parking_lot\",\"rt\",\"rt-multi-thread\",\"sync\",\"time\"],\"name\":\"tokio\",\"req\":\"^1.32.0\"},{\"name\":\"turn\",\"req\":\"^0.17.1\"},{\"name\":\"url\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"conn\",\"vnet\",\"sync\"],\"name\":\"util\",\"package\":\"webrtc-util\",\"req\":\"^0.17.1\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"req\":\"^1\"},{\"name\":\"waitgroup\",\"req\":\"^0.1\"}],\"features\":{}}",
|
||||
"webrtc-mdns_0.17.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.28\"},{\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.3\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"features\":[\"all\"],\"name\":\"socket2\",\"req\":\"^0.5\"},{\"name\":\"thiserror\",\"req\":\"^1\"},{\"features\":[\"fs\",\"io-util\",\"io-std\",\"macros\",\"net\",\"parking_lot\",\"rt\",\"rt-multi-thread\",\"sync\",\"time\"],\"name\":\"tokio\",\"req\":\"^1.32.0\"},{\"default_features\":false,\"features\":[\"ifaces\"],\"name\":\"util\",\"package\":\"webrtc-util\",\"req\":\"^0.17.1\"}],\"features\":{\"default\":[\"reuse_port\"],\"reuse_port\":[]}}",
|
||||
|
||||
41
codex-rs/Cargo.lock
generated
41
codex-rs/Cargo.lock
generated
@@ -790,6 +790,15 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "autotools"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
version = "1.16.2"
|
||||
@@ -2943,6 +2952,7 @@ dependencies = [
|
||||
"uuid",
|
||||
"vt100",
|
||||
"webbrowser",
|
||||
"webrtc-audio-processing",
|
||||
"which 8.0.0",
|
||||
"windows-sys 0.52.0",
|
||||
"winsplit",
|
||||
@@ -11879,6 +11889,37 @@ dependencies = [
|
||||
"webrtc-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webrtc-audio-processing"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31cc7cbf143d9c3de985b113ddc3fddb4a60c7746635a69114f94a5b5af8f42"
|
||||
dependencies = [
|
||||
"webrtc-audio-processing-config",
|
||||
"webrtc-audio-processing-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webrtc-audio-processing-config"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb5a1fcf911c54bf3d0e022020f1626ae99add5728c65d345bacfbf3adbf2ef2"
|
||||
|
||||
[[package]]
|
||||
name = "webrtc-audio-processing-sys"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbd9fcfbedf79b0c7bc0f627d4e9cd5efcb648dcfc271c15503194482b39b0e8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"autotools",
|
||||
"bindgen",
|
||||
"cc",
|
||||
"fs_extra",
|
||||
"pkg-config",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webrtc-data"
|
||||
version = "0.17.1"
|
||||
|
||||
@@ -352,6 +352,7 @@ vt100 = "0.16.2"
|
||||
walkdir = "2.5.0"
|
||||
webbrowser = "1.0"
|
||||
webrtc = "0.17.1"
|
||||
webrtc-audio-processing = { version = "~2.0", features = ["bundled"] }
|
||||
which = "8"
|
||||
wildmatch = "2.6.1"
|
||||
zip = "2.4.2"
|
||||
|
||||
@@ -113,6 +113,7 @@ tokio-util = { workspace = true, features = ["time"] }
|
||||
|
||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||
cpal = "0.15"
|
||||
webrtc-audio-processing = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { workspace = true }
|
||||
|
||||
@@ -155,11 +155,6 @@ impl AppServerSession {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_remote_cwd_override(mut self, remote_cwd_override: Option<PathBuf>) -> Self {
|
||||
self.remote_cwd_override = remote_cwd_override;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn remote_cwd_override(&self) -> Option<&std::path::Path> {
|
||||
self.remote_cwd_override.as_deref()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use super::*;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
use crate::realtime_audio_processing::RealtimeAudioProcessor;
|
||||
use codex_protocol::protocol::ConversationStartParams;
|
||||
use codex_protocol::protocol::RealtimeAudioFrame;
|
||||
use codex_protocol::protocol::RealtimeConversationClosedEvent;
|
||||
@@ -30,6 +32,8 @@ pub(super) struct RealtimeConversationUiState {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
capture_stop_flag: Option<Arc<AtomicBool>>,
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
audio_processor: Option<RealtimeAudioProcessor>,
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
capture: Option<crate::voice::VoiceCapture>,
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
audio_player: Option<crate::voice::RealtimeAudioPlayer>,
|
||||
@@ -331,9 +335,26 @@ impl ChatWidget {
|
||||
fn enqueue_realtime_audio_out(&mut self, frame: &RealtimeAudioFrame) {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
if !self.realtime_conversation.is_active() {
|
||||
return;
|
||||
}
|
||||
if self.realtime_conversation.audio_player.is_none() {
|
||||
self.realtime_conversation.audio_player =
|
||||
crate::voice::RealtimeAudioPlayer::start(&self.config).ok();
|
||||
let Some(audio_processor) = self.realtime_conversation.audio_processor.clone()
|
||||
else {
|
||||
self.fail_realtime_conversation(
|
||||
"Realtime audio processor was unavailable".to_string(),
|
||||
);
|
||||
return;
|
||||
};
|
||||
match crate::voice::RealtimeAudioPlayer::start(&self.config, audio_processor) {
|
||||
Ok(player) => self.realtime_conversation.audio_player = Some(player),
|
||||
Err(err) => {
|
||||
self.fail_realtime_conversation(format!(
|
||||
"Failed to start speaker output: {err}"
|
||||
));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(player) = &self.realtime_conversation.audio_player
|
||||
&& let Err(err) = player.enqueue_frame(frame)
|
||||
@@ -367,12 +388,42 @@ impl ChatWidget {
|
||||
self.realtime_conversation.meter_placeholder_id = Some(placeholder_id.clone());
|
||||
self.request_redraw();
|
||||
|
||||
let audio_processor = match RealtimeAudioProcessor::new() {
|
||||
Ok(audio_processor) => audio_processor,
|
||||
Err(err) => {
|
||||
self.realtime_conversation.meter_placeholder_id = None;
|
||||
self.remove_recording_meter_placeholder(&placeholder_id);
|
||||
self.fail_realtime_conversation(format!(
|
||||
"Failed to start realtime audio processor: {err}"
|
||||
));
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.realtime_conversation.audio_processor = Some(audio_processor.clone());
|
||||
|
||||
let audio_player =
|
||||
match crate::voice::RealtimeAudioPlayer::start(&self.config, audio_processor.clone()) {
|
||||
Ok(player) => player,
|
||||
Err(err) => {
|
||||
self.realtime_conversation.audio_processor = None;
|
||||
self.realtime_conversation.meter_placeholder_id = None;
|
||||
self.remove_recording_meter_placeholder(&placeholder_id);
|
||||
self.fail_realtime_conversation(format!(
|
||||
"Failed to start speaker output: {err}"
|
||||
));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let capture = match crate::voice::VoiceCapture::start_realtime(
|
||||
&self.config,
|
||||
self.app_event_tx.clone(),
|
||||
audio_processor,
|
||||
) {
|
||||
Ok(capture) => capture,
|
||||
Err(err) => {
|
||||
drop(audio_player);
|
||||
self.realtime_conversation.audio_processor = None;
|
||||
self.realtime_conversation.meter_placeholder_id = None;
|
||||
self.remove_recording_meter_placeholder(&placeholder_id);
|
||||
self.fail_realtime_conversation(format!(
|
||||
@@ -389,10 +440,7 @@ impl ChatWidget {
|
||||
|
||||
self.realtime_conversation.capture_stop_flag = Some(stop_flag.clone());
|
||||
self.realtime_conversation.capture = Some(capture);
|
||||
if self.realtime_conversation.audio_player.is_none() {
|
||||
self.realtime_conversation.audio_player =
|
||||
crate::voice::RealtimeAudioPlayer::start(&self.config).ok();
|
||||
}
|
||||
self.realtime_conversation.audio_player = Some(audio_player);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut meter = crate::voice::RecordingMeterState::new();
|
||||
@@ -423,23 +471,10 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
match kind {
|
||||
RealtimeAudioDeviceKind::Microphone => {
|
||||
self.stop_realtime_microphone();
|
||||
RealtimeAudioDeviceKind::Microphone | RealtimeAudioDeviceKind::Speaker => {
|
||||
self.stop_realtime_local_audio();
|
||||
self.start_realtime_local_audio();
|
||||
}
|
||||
RealtimeAudioDeviceKind::Speaker => {
|
||||
self.stop_realtime_speaker();
|
||||
match crate::voice::RealtimeAudioPlayer::start(&self.config) {
|
||||
Ok(player) => {
|
||||
self.realtime_conversation.audio_player = Some(player);
|
||||
}
|
||||
Err(err) => {
|
||||
self.fail_realtime_conversation(format!(
|
||||
"Failed to start speaker output: {err}"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.request_redraw();
|
||||
}
|
||||
@@ -453,6 +488,7 @@ impl ChatWidget {
|
||||
fn stop_realtime_local_audio(&mut self) {
|
||||
self.stop_realtime_microphone();
|
||||
self.stop_realtime_speaker();
|
||||
self.realtime_conversation.audio_processor = None;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
|
||||
@@ -130,6 +130,8 @@ pub mod onboarding;
|
||||
mod oss_selection;
|
||||
mod pager_overlay;
|
||||
pub mod public_widgets;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
mod realtime_audio_processing;
|
||||
mod render;
|
||||
mod resume_picker;
|
||||
mod selection_list;
|
||||
@@ -577,42 +579,15 @@ fn latest_session_lookup_params(
|
||||
source_kinds: (!include_non_interactive)
|
||||
.then_some(vec![ThreadSourceKind::Cli, ThreadSourceKind::VsCode]),
|
||||
archived: Some(false),
|
||||
cwd: cwd_filter.map(|cwd| cwd.to_string_lossy().to_string()),
|
||||
cwd: if is_remote {
|
||||
None
|
||||
} else {
|
||||
cwd_filter.map(|cwd| cwd.to_string_lossy().to_string())
|
||||
},
|
||||
search_term: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn config_cwd_for_app_server_target(
|
||||
cwd: Option<&Path>,
|
||||
app_server_target: &AppServerTarget,
|
||||
) -> std::io::Result<AbsolutePathBuf> {
|
||||
if matches!(app_server_target, AppServerTarget::Remote { .. }) {
|
||||
return AbsolutePathBuf::current_dir();
|
||||
}
|
||||
|
||||
match cwd {
|
||||
Some(path) => AbsolutePathBuf::from_absolute_path(path.canonicalize()?),
|
||||
None => AbsolutePathBuf::current_dir(),
|
||||
}
|
||||
}
|
||||
|
||||
fn latest_session_cwd_filter<'a>(
|
||||
remote_mode: bool,
|
||||
remote_cwd_override: Option<&'a Path>,
|
||||
config: &'a Config,
|
||||
show_all: bool,
|
||||
) -> Option<&'a Path> {
|
||||
if show_all {
|
||||
return None;
|
||||
}
|
||||
|
||||
if remote_mode {
|
||||
remote_cwd_override
|
||||
} else {
|
||||
Some(config.cwd.as_path())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_main(
|
||||
mut cli: Cli,
|
||||
arg0_paths: Arg0DispatchPaths,
|
||||
@@ -631,10 +606,6 @@ pub async fn run_main(
|
||||
auth_token: remote_auth_token.clone(),
|
||||
})
|
||||
.unwrap_or(AppServerTarget::Embedded);
|
||||
let remote_cwd_override = cli
|
||||
.cwd
|
||||
.clone()
|
||||
.filter(|_| matches!(app_server_target, AppServerTarget::Remote { .. }));
|
||||
let (sandbox_mode, approval_policy) = if cli.full_auto {
|
||||
(
|
||||
Some(SandboxMode::WorkspaceWrite),
|
||||
@@ -685,7 +656,10 @@ pub async fn run_main(
|
||||
};
|
||||
|
||||
let cwd = cli.cwd.clone();
|
||||
let config_cwd = config_cwd_for_app_server_target(cwd.as_deref(), &app_server_target)?;
|
||||
let config_cwd = match cwd.as_deref() {
|
||||
Some(path) => AbsolutePathBuf::from_absolute_path(path.canonicalize()?)?,
|
||||
None => AbsolutePathBuf::current_dir()?,
|
||||
};
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
let config_toml = match load_config_as_toml_with_cli_overrides(
|
||||
@@ -773,11 +747,7 @@ pub async fn run_main(
|
||||
model,
|
||||
approval_policy,
|
||||
sandbox_mode,
|
||||
cwd: if matches!(app_server_target, AppServerTarget::Remote { .. }) {
|
||||
None
|
||||
} else {
|
||||
cwd
|
||||
},
|
||||
cwd,
|
||||
model_provider: model_provider_override.clone(),
|
||||
config_profile: cli.config_profile.clone(),
|
||||
codex_self_exe: arg0_paths.codex_self_exe.clone(),
|
||||
@@ -939,7 +909,6 @@ pub async fn run_main(
|
||||
arg0_paths,
|
||||
loader_overrides,
|
||||
app_server_target,
|
||||
remote_cwd_override,
|
||||
config,
|
||||
overrides,
|
||||
cli_kv_overrides,
|
||||
@@ -958,7 +927,6 @@ async fn run_ratatui_app(
|
||||
arg0_paths: Arg0DispatchPaths,
|
||||
loader_overrides: LoaderOverrides,
|
||||
app_server_target: AppServerTarget,
|
||||
remote_cwd_override: Option<PathBuf>,
|
||||
initial_config: Config,
|
||||
overrides: ConfigOverrides,
|
||||
cli_kv_overrides: Vec<(String, toml::Value)>,
|
||||
@@ -1017,21 +985,18 @@ async fn run_ratatui_app(
|
||||
let needs_onboarding_app_server =
|
||||
should_show_trust_screen_flag || initial_config.model_provider.requires_openai_auth;
|
||||
let mut onboarding_app_server = if needs_onboarding_app_server {
|
||||
Some(
|
||||
AppServerSession::new(
|
||||
start_app_server(
|
||||
&app_server_target,
|
||||
arg0_paths.clone(),
|
||||
initial_config.clone(),
|
||||
cli_kv_overrides.clone(),
|
||||
loader_overrides.clone(),
|
||||
cloud_requirements.clone(),
|
||||
feedback.clone(),
|
||||
)
|
||||
.await?,
|
||||
Some(AppServerSession::new(
|
||||
start_app_server(
|
||||
&app_server_target,
|
||||
arg0_paths.clone(),
|
||||
initial_config.clone(),
|
||||
cli_kv_overrides.clone(),
|
||||
loader_overrides.clone(),
|
||||
cloud_requirements.clone(),
|
||||
feedback.clone(),
|
||||
)
|
||||
.with_remote_cwd_override(remote_cwd_override.clone()),
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -1134,21 +1099,18 @@ async fn run_ratatui_app(
|
||||
|| cli.resume_picker
|
||||
|| cli.fork_picker;
|
||||
let mut session_lookup_app_server = if needs_app_server_session_lookup {
|
||||
Some(
|
||||
AppServerSession::new(
|
||||
start_app_server(
|
||||
&app_server_target,
|
||||
arg0_paths.clone(),
|
||||
config.clone(),
|
||||
cli_kv_overrides.clone(),
|
||||
loader_overrides.clone(),
|
||||
cloud_requirements.clone(),
|
||||
feedback.clone(),
|
||||
)
|
||||
.await?,
|
||||
Some(AppServerSession::new(
|
||||
start_app_server(
|
||||
&app_server_target,
|
||||
arg0_paths.clone(),
|
||||
config.clone(),
|
||||
cli_kv_overrides.clone(),
|
||||
loader_overrides.clone(),
|
||||
cloud_requirements.clone(),
|
||||
feedback.clone(),
|
||||
)
|
||||
.with_remote_cwd_override(remote_cwd_override.clone()),
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -1167,21 +1129,12 @@ async fn run_ratatui_app(
|
||||
}
|
||||
}
|
||||
} else if cli.fork_last {
|
||||
let filter_cwd = if remote_mode {
|
||||
latest_session_cwd_filter(
|
||||
remote_mode,
|
||||
remote_cwd_override.as_deref(),
|
||||
&config,
|
||||
cli.fork_show_all,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let Some(app_server) = session_lookup_app_server.as_mut() else {
|
||||
unreachable!("session lookup app server should be initialized for --fork --last");
|
||||
};
|
||||
match lookup_latest_session_target_with_app_server(
|
||||
app_server, &config, filter_cwd, /*include_non_interactive*/ false,
|
||||
app_server, &config, /*cwd_filter*/ None,
|
||||
/*include_non_interactive*/ false,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
@@ -1228,12 +1181,11 @@ async fn run_ratatui_app(
|
||||
}
|
||||
}
|
||||
} else if cli.resume_last {
|
||||
let filter_cwd = latest_session_cwd_filter(
|
||||
remote_mode,
|
||||
remote_cwd_override.as_deref(),
|
||||
&config,
|
||||
cli.resume_show_all,
|
||||
);
|
||||
let filter_cwd = if cli.resume_show_all {
|
||||
None
|
||||
} else {
|
||||
Some(config.cwd.as_path())
|
||||
};
|
||||
let Some(app_server) = session_lookup_app_server.as_mut() else {
|
||||
unreachable!("session lookup app server should be initialized for --resume --last");
|
||||
};
|
||||
@@ -1384,7 +1336,7 @@ async fn run_ratatui_app(
|
||||
|
||||
let app_result = App::run(
|
||||
&mut tui,
|
||||
AppServerSession::new(app_server).with_remote_cwd_override(remote_cwd_override),
|
||||
AppServerSession::new(app_server),
|
||||
config,
|
||||
cli_kv_overrides.clone(),
|
||||
overrides.clone(),
|
||||
@@ -1845,9 +1797,12 @@ mod tests {
|
||||
-> std::io::Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let config = build_config(&temp_dir).await?;
|
||||
let cwd = temp_dir.path().join("project");
|
||||
|
||||
let params = latest_session_lookup_params(
|
||||
/*is_remote*/ true, &config, /*cwd_filter*/ None,
|
||||
/*is_remote*/ true,
|
||||
&config,
|
||||
Some(cwd.as_path()),
|
||||
/*include_non_interactive*/ false,
|
||||
);
|
||||
|
||||
@@ -1856,58 +1811,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn latest_session_lookup_params_keep_explicit_cwd_filter_for_remote_sessions()
|
||||
-> std::io::Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let config = build_config(&temp_dir).await?;
|
||||
let cwd = Path::new("repo/on/server");
|
||||
|
||||
let params = latest_session_lookup_params(
|
||||
/*is_remote*/ true,
|
||||
&config,
|
||||
Some(cwd),
|
||||
/*include_non_interactive*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(params.model_providers, None);
|
||||
assert_eq!(params.cwd.as_deref(), Some("repo/on/server"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_cwd_for_app_server_target_uses_current_dir_for_remote_sessions() -> std::io::Result<()>
|
||||
{
|
||||
let remote_only_cwd = if cfg!(windows) {
|
||||
Path::new(r"C:\definitely\not\local\to\this\test")
|
||||
} else {
|
||||
Path::new("/definitely/not/local/to/this/test")
|
||||
};
|
||||
let target = AppServerTarget::Remote {
|
||||
websocket_url: "ws://127.0.0.1:1234/".to_string(),
|
||||
auth_token: None,
|
||||
};
|
||||
|
||||
let config_cwd = config_cwd_for_app_server_target(Some(remote_only_cwd), &target)?;
|
||||
|
||||
assert_eq!(config_cwd, AbsolutePathBuf::current_dir()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_cwd_for_app_server_target_canonicalizes_embedded_cli_cwd() -> std::io::Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
let target = AppServerTarget::Embedded;
|
||||
|
||||
let config_cwd = config_cwd_for_app_server_target(Some(temp_dir.path()), &target)?;
|
||||
|
||||
assert_eq!(
|
||||
config_cwd,
|
||||
AbsolutePathBuf::from_absolute_path(temp_dir.path().canonicalize()?)?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_session_cwd_returns_none_without_sqlite_or_rollout_path() -> std::io::Result<()> {
|
||||
let temp_dir = TempDir::new()?;
|
||||
|
||||
237
codex-rs/tui/src/realtime_audio_processing.rs
Normal file
237
codex-rs/tui/src/realtime_audio_processing.rs
Normal file
@@ -0,0 +1,237 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
use tracing::warn;
|
||||
use webrtc_audio_processing::Config as AudioProcessingConfig;
|
||||
use webrtc_audio_processing::Processor;
|
||||
use webrtc_audio_processing::config::EchoCanceller;
|
||||
use webrtc_audio_processing::config::GainController;
|
||||
use webrtc_audio_processing::config::HighPassFilter;
|
||||
use webrtc_audio_processing::config::NoiseSuppression;
|
||||
use webrtc_audio_processing::config::NoiseSuppressionLevel;
|
||||
use webrtc_audio_processing::config::Pipeline;
|
||||
|
||||
pub(crate) const AUDIO_PROCESSING_SAMPLE_RATE: u32 = 24_000;
|
||||
pub(crate) const AUDIO_PROCESSING_CHANNELS: u16 = 1;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RealtimeAudioProcessor {
|
||||
processor: Arc<Processor>,
|
||||
}
|
||||
|
||||
impl RealtimeAudioProcessor {
|
||||
pub(crate) fn new() -> Result<Self, String> {
|
||||
let processor = Processor::new(AUDIO_PROCESSING_SAMPLE_RATE)
|
||||
.map_err(|err| format!("failed to initialize realtime audio processor: {err}"))?;
|
||||
processor.set_config(AudioProcessingConfig {
|
||||
pipeline: Pipeline {
|
||||
multi_channel_capture: false,
|
||||
multi_channel_render: false,
|
||||
..Default::default()
|
||||
},
|
||||
echo_canceller: Some(EchoCanceller::Full {
|
||||
stream_delay_ms: None,
|
||||
}),
|
||||
noise_suppression: Some(NoiseSuppression {
|
||||
level: NoiseSuppressionLevel::High,
|
||||
..Default::default()
|
||||
}),
|
||||
gain_controller: Some(GainController::GainController2(Default::default())),
|
||||
high_pass_filter: Some(HighPassFilter::default()),
|
||||
..Default::default()
|
||||
});
|
||||
processor.set_output_will_be_muted(true);
|
||||
Ok(Self {
|
||||
processor: Arc::new(processor),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn capture_stage(
|
||||
&self,
|
||||
input_sample_rate: u32,
|
||||
input_channels: u16,
|
||||
) -> RealtimeCaptureAudioProcessor {
|
||||
RealtimeCaptureAudioProcessor {
|
||||
processor: self.processor.clone(),
|
||||
input_sample_rate,
|
||||
input_channels,
|
||||
pending_samples: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_stage(
|
||||
&self,
|
||||
output_sample_rate: u32,
|
||||
output_channels: u16,
|
||||
) -> RealtimeRenderAudioProcessor {
|
||||
RealtimeRenderAudioProcessor {
|
||||
processor: self.processor.clone(),
|
||||
output_sample_rate,
|
||||
output_channels,
|
||||
pending_samples: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_output_will_be_muted(&self, muted: bool) {
|
||||
self.processor.set_output_will_be_muted(muted);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RealtimeCaptureAudioProcessor {
|
||||
processor: Arc<Processor>,
|
||||
input_sample_rate: u32,
|
||||
input_channels: u16,
|
||||
pending_samples: VecDeque<i16>,
|
||||
}
|
||||
|
||||
impl RealtimeCaptureAudioProcessor {
|
||||
pub(crate) fn process_samples(&mut self, samples: &[i16]) -> Vec<i16> {
|
||||
let converted = convert_pcm16(
|
||||
samples,
|
||||
self.input_sample_rate,
|
||||
self.input_channels,
|
||||
AUDIO_PROCESSING_SAMPLE_RATE,
|
||||
AUDIO_PROCESSING_CHANNELS,
|
||||
);
|
||||
self.pending_samples.extend(converted);
|
||||
|
||||
let mut processed = Vec::new();
|
||||
while self.pending_samples.len() >= self.processor.num_samples_per_frame() {
|
||||
let mut frame = self.pop_pending_frame();
|
||||
if let Err(err) = self.processor.process_capture_frame([frame.as_mut_slice()]) {
|
||||
warn!("failed to process realtime capture audio: {err}");
|
||||
continue;
|
||||
}
|
||||
processed.extend(frame.into_iter().map(f32_to_i16));
|
||||
}
|
||||
processed
|
||||
}
|
||||
|
||||
fn pop_pending_frame(&mut self) -> Vec<f32> {
|
||||
self.pending_samples
|
||||
.drain(..self.processor.num_samples_per_frame())
|
||||
.map(i16_to_f32)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RealtimeRenderAudioProcessor {
|
||||
processor: Arc<Processor>,
|
||||
output_sample_rate: u32,
|
||||
output_channels: u16,
|
||||
pending_samples: VecDeque<i16>,
|
||||
}
|
||||
|
||||
impl RealtimeRenderAudioProcessor {
|
||||
pub(crate) fn process_samples(&mut self, samples: &[i16]) {
|
||||
self.processor
|
||||
.set_output_will_be_muted(samples.iter().all(|sample| *sample == 0));
|
||||
|
||||
let converted = convert_pcm16(
|
||||
samples,
|
||||
self.output_sample_rate,
|
||||
self.output_channels,
|
||||
AUDIO_PROCESSING_SAMPLE_RATE,
|
||||
AUDIO_PROCESSING_CHANNELS,
|
||||
);
|
||||
self.pending_samples.extend(converted);
|
||||
|
||||
while self.pending_samples.len() >= self.processor.num_samples_per_frame() {
|
||||
let mut frame = self.pop_pending_frame();
|
||||
if let Err(err) = self.processor.process_render_frame([frame.as_mut_slice()]) {
|
||||
warn!("failed to process realtime render audio: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_pending_frame(&mut self) -> Vec<f32> {
|
||||
self.pending_samples
|
||||
.drain(..self.processor.num_samples_per_frame())
|
||||
.map(i16_to_f32)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn convert_pcm16(
|
||||
input: &[i16],
|
||||
input_sample_rate: u32,
|
||||
input_channels: u16,
|
||||
output_sample_rate: u32,
|
||||
output_channels: u16,
|
||||
) -> Vec<i16> {
|
||||
if input.is_empty() || input_channels == 0 || output_channels == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let in_channels = input_channels as usize;
|
||||
let out_channels = output_channels as usize;
|
||||
let in_frames = input.len() / in_channels;
|
||||
if in_frames == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let out_frames = if input_sample_rate == output_sample_rate {
|
||||
in_frames
|
||||
} else {
|
||||
(((in_frames as u64) * (output_sample_rate as u64)) / (input_sample_rate as u64)).max(1)
|
||||
as usize
|
||||
};
|
||||
|
||||
let mut out = Vec::with_capacity(out_frames.saturating_mul(out_channels));
|
||||
for out_frame_idx in 0..out_frames {
|
||||
let src_frame_idx = if out_frames <= 1 || in_frames <= 1 {
|
||||
0
|
||||
} else {
|
||||
((out_frame_idx as u64) * ((in_frames - 1) as u64) / ((out_frames - 1) as u64)) as usize
|
||||
};
|
||||
let src_start = src_frame_idx.saturating_mul(in_channels);
|
||||
let src = &input[src_start..src_start + in_channels];
|
||||
match (in_channels, out_channels) {
|
||||
(1, 1) => out.push(src[0]),
|
||||
(1, n) => {
|
||||
for _ in 0..n {
|
||||
out.push(src[0]);
|
||||
}
|
||||
}
|
||||
(n, 1) if n >= 2 => {
|
||||
let sum: i32 = src.iter().map(|s| *s as i32).sum();
|
||||
out.push((sum / (n as i32)) as i16);
|
||||
}
|
||||
(n, m) if n == m => out.extend_from_slice(src),
|
||||
(n, m) if n > m => out.extend_from_slice(&src[..m]),
|
||||
(n, m) => {
|
||||
out.extend_from_slice(src);
|
||||
let last = *src.last().unwrap_or(&0);
|
||||
for _ in n..m {
|
||||
out.push(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn i16_to_f32(sample: i16) -> f32 {
|
||||
(sample as f32) / (i16::MAX as f32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn f32_to_i16(sample: f32) -> i16 {
|
||||
(sample.clamp(-1.0, 1.0) * i16::MAX as f32) as i16
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::convert_pcm16;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn convert_pcm16_downmixes_and_resamples_for_model_input() {
|
||||
let input = vec![100, 300, 200, 400, 500, 700, 600, 800];
|
||||
let converted = convert_pcm16(
|
||||
&input, /*input_sample_rate*/ 48_000, /*input_channels*/ 2,
|
||||
/*output_sample_rate*/ 24_000, /*output_channels*/ 1,
|
||||
);
|
||||
assert_eq!(converted, vec![200, 700]);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
use crate::realtime_audio_processing::AUDIO_PROCESSING_CHANNELS;
|
||||
use crate::realtime_audio_processing::AUDIO_PROCESSING_SAMPLE_RATE;
|
||||
use crate::realtime_audio_processing::RealtimeAudioProcessor;
|
||||
use crate::realtime_audio_processing::RealtimeCaptureAudioProcessor;
|
||||
use crate::realtime_audio_processing::RealtimeRenderAudioProcessor;
|
||||
use crate::realtime_audio_processing::convert_pcm16;
|
||||
use base64::Engine;
|
||||
use codex_core::config::Config;
|
||||
use codex_protocol::protocol::ConversationAudioParams;
|
||||
@@ -23,7 +29,11 @@ pub struct VoiceCapture {
|
||||
}
|
||||
|
||||
impl VoiceCapture {
|
||||
pub fn start_realtime(config: &Config, tx: AppEventSender) -> Result<Self, String> {
|
||||
pub fn start_realtime(
|
||||
config: &Config,
|
||||
tx: AppEventSender,
|
||||
audio_processor: RealtimeAudioProcessor,
|
||||
) -> Result<Self, String> {
|
||||
let (device, config) = select_realtime_input_device_and_config(config)?;
|
||||
|
||||
let sample_rate = config.sample_rate().0;
|
||||
@@ -34,9 +44,8 @@ impl VoiceCapture {
|
||||
let stream = build_realtime_input_stream(
|
||||
&device,
|
||||
&config,
|
||||
sample_rate,
|
||||
channels,
|
||||
tx,
|
||||
audio_processor.capture_stage(sample_rate, channels),
|
||||
last_peak.clone(),
|
||||
)?;
|
||||
stream
|
||||
@@ -138,50 +147,76 @@ fn select_realtime_input_device_and_config(
|
||||
fn build_realtime_input_stream(
|
||||
device: &cpal::Device,
|
||||
config: &cpal::SupportedStreamConfig,
|
||||
sample_rate: u32,
|
||||
channels: u16,
|
||||
tx: AppEventSender,
|
||||
capture_processor: RealtimeCaptureAudioProcessor,
|
||||
last_peak: Arc<AtomicU16>,
|
||||
) -> Result<cpal::Stream, String> {
|
||||
match config.sample_format() {
|
||||
cpal::SampleFormat::F32 => device
|
||||
.build_input_stream(
|
||||
&config.clone().into(),
|
||||
move |input: &[f32], _| {
|
||||
let peak = peak_f32(input);
|
||||
last_peak.store(peak, Ordering::Relaxed);
|
||||
let samples = input.iter().copied().map(f32_to_i16).collect::<Vec<_>>();
|
||||
send_realtime_audio_chunk(&tx, samples, sample_rate, channels);
|
||||
},
|
||||
move |err| error!("audio input error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build input stream: {e}")),
|
||||
cpal::SampleFormat::I16 => device
|
||||
.build_input_stream(
|
||||
&config.clone().into(),
|
||||
move |input: &[i16], _| {
|
||||
let peak = peak_i16(input);
|
||||
last_peak.store(peak, Ordering::Relaxed);
|
||||
send_realtime_audio_chunk(&tx, input.to_vec(), sample_rate, channels);
|
||||
},
|
||||
move |err| error!("audio input error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build input stream: {e}")),
|
||||
cpal::SampleFormat::U16 => device
|
||||
.build_input_stream(
|
||||
&config.clone().into(),
|
||||
move |input: &[u16], _| {
|
||||
let mut samples = Vec::with_capacity(input.len());
|
||||
let peak = convert_u16_to_i16_and_peak(input, &mut samples);
|
||||
last_peak.store(peak, Ordering::Relaxed);
|
||||
send_realtime_audio_chunk(&tx, samples, sample_rate, channels);
|
||||
},
|
||||
move |err| error!("audio input error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build input stream: {e}")),
|
||||
cpal::SampleFormat::F32 => {
|
||||
let mut capture_processor = capture_processor;
|
||||
device
|
||||
.build_input_stream(
|
||||
&config.clone().into(),
|
||||
move |input: &[f32], _| {
|
||||
let peak = peak_f32(input);
|
||||
last_peak.store(peak, Ordering::Relaxed);
|
||||
let samples = input.iter().copied().map(f32_to_i16).collect::<Vec<_>>();
|
||||
let samples = capture_processor.process_samples(&samples);
|
||||
send_realtime_audio_chunk(
|
||||
&tx,
|
||||
samples,
|
||||
AUDIO_PROCESSING_SAMPLE_RATE,
|
||||
AUDIO_PROCESSING_CHANNELS,
|
||||
);
|
||||
},
|
||||
move |err| error!("audio input error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build input stream: {e}"))
|
||||
}
|
||||
cpal::SampleFormat::I16 => {
|
||||
let mut capture_processor = capture_processor;
|
||||
device
|
||||
.build_input_stream(
|
||||
&config.clone().into(),
|
||||
move |input: &[i16], _| {
|
||||
let peak = peak_i16(input);
|
||||
last_peak.store(peak, Ordering::Relaxed);
|
||||
let samples = capture_processor.process_samples(input);
|
||||
send_realtime_audio_chunk(
|
||||
&tx,
|
||||
samples,
|
||||
AUDIO_PROCESSING_SAMPLE_RATE,
|
||||
AUDIO_PROCESSING_CHANNELS,
|
||||
);
|
||||
},
|
||||
move |err| error!("audio input error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build input stream: {e}"))
|
||||
}
|
||||
cpal::SampleFormat::U16 => {
|
||||
let mut capture_processor = capture_processor;
|
||||
device
|
||||
.build_input_stream(
|
||||
&config.clone().into(),
|
||||
move |input: &[u16], _| {
|
||||
let mut samples = Vec::with_capacity(input.len());
|
||||
let peak = convert_u16_to_i16_and_peak(input, &mut samples);
|
||||
last_peak.store(peak, Ordering::Relaxed);
|
||||
let samples = capture_processor.process_samples(&samples);
|
||||
send_realtime_audio_chunk(
|
||||
&tx,
|
||||
samples,
|
||||
AUDIO_PROCESSING_SAMPLE_RATE,
|
||||
AUDIO_PROCESSING_CHANNELS,
|
||||
);
|
||||
},
|
||||
move |err| error!("audio input error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build input stream: {e}"))
|
||||
}
|
||||
_ => Err("unsupported input sample format".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -283,24 +318,34 @@ fn convert_u16_to_i16_and_peak(input: &[u16], out: &mut Vec<i16>) -> u16 {
|
||||
pub(crate) struct RealtimeAudioPlayer {
|
||||
_stream: cpal::Stream,
|
||||
queue: Arc<Mutex<VecDeque<i16>>>,
|
||||
audio_processor: RealtimeAudioProcessor,
|
||||
output_sample_rate: u32,
|
||||
output_channels: u16,
|
||||
}
|
||||
|
||||
impl RealtimeAudioPlayer {
|
||||
pub(crate) fn start(config: &Config) -> Result<Self, String> {
|
||||
pub(crate) fn start(
|
||||
config: &Config,
|
||||
audio_processor: RealtimeAudioProcessor,
|
||||
) -> Result<Self, String> {
|
||||
let (device, config) =
|
||||
crate::audio_device::select_configured_output_device_and_config(config)?;
|
||||
let output_sample_rate = config.sample_rate().0;
|
||||
let output_channels = config.channels();
|
||||
let queue = Arc::new(Mutex::new(VecDeque::new()));
|
||||
let stream = build_output_stream(&device, &config, Arc::clone(&queue))?;
|
||||
let stream = build_output_stream(
|
||||
&device,
|
||||
&config,
|
||||
Arc::clone(&queue),
|
||||
audio_processor.render_stage(output_sample_rate, output_channels),
|
||||
)?;
|
||||
stream
|
||||
.play()
|
||||
.map_err(|e| format!("failed to start output stream: {e}"))?;
|
||||
Ok(Self {
|
||||
_stream: stream,
|
||||
queue,
|
||||
audio_processor,
|
||||
output_sample_rate,
|
||||
output_channels,
|
||||
})
|
||||
@@ -336,6 +381,8 @@ impl RealtimeAudioPlayer {
|
||||
.map_err(|_| "failed to lock output audio queue".to_string())?;
|
||||
// TODO(aibrahim): Cap or trim this queue if we observe producer bursts outrunning playback.
|
||||
guard.extend(converted);
|
||||
drop(guard);
|
||||
self.audio_processor.set_output_will_be_muted(false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -343,6 +390,7 @@ impl RealtimeAudioPlayer {
|
||||
if let Ok(mut guard) = self.queue.lock() {
|
||||
guard.clear();
|
||||
}
|
||||
self.audio_processor.set_output_will_be_muted(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,140 +398,94 @@ fn build_output_stream(
|
||||
device: &cpal::Device,
|
||||
config: &cpal::SupportedStreamConfig,
|
||||
queue: Arc<Mutex<VecDeque<i16>>>,
|
||||
render_processor: RealtimeRenderAudioProcessor,
|
||||
) -> Result<cpal::Stream, String> {
|
||||
let config_any: cpal::StreamConfig = config.clone().into();
|
||||
match config.sample_format() {
|
||||
cpal::SampleFormat::F32 => device
|
||||
.build_output_stream(
|
||||
&config_any,
|
||||
move |output: &mut [f32], _| fill_output_f32(output, &queue),
|
||||
move |err| error!("audio output error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build f32 output stream: {e}")),
|
||||
cpal::SampleFormat::I16 => device
|
||||
.build_output_stream(
|
||||
&config_any,
|
||||
move |output: &mut [i16], _| fill_output_i16(output, &queue),
|
||||
move |err| error!("audio output error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build i16 output stream: {e}")),
|
||||
cpal::SampleFormat::U16 => device
|
||||
.build_output_stream(
|
||||
&config_any,
|
||||
move |output: &mut [u16], _| fill_output_u16(output, &queue),
|
||||
move |err| error!("audio output error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build u16 output stream: {e}")),
|
||||
cpal::SampleFormat::F32 => {
|
||||
let mut render_processor = render_processor;
|
||||
device
|
||||
.build_output_stream(
|
||||
&config_any,
|
||||
move |output: &mut [f32], _| {
|
||||
fill_output_f32(output, &queue, &mut render_processor)
|
||||
},
|
||||
move |err| error!("audio output error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build f32 output stream: {e}"))
|
||||
}
|
||||
cpal::SampleFormat::I16 => {
|
||||
let mut render_processor = render_processor;
|
||||
device
|
||||
.build_output_stream(
|
||||
&config_any,
|
||||
move |output: &mut [i16], _| {
|
||||
fill_output_i16(output, &queue, &mut render_processor)
|
||||
},
|
||||
move |err| error!("audio output error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build i16 output stream: {e}"))
|
||||
}
|
||||
cpal::SampleFormat::U16 => {
|
||||
let mut render_processor = render_processor;
|
||||
device
|
||||
.build_output_stream(
|
||||
&config_any,
|
||||
move |output: &mut [u16], _| {
|
||||
fill_output_u16(output, &queue, &mut render_processor)
|
||||
},
|
||||
move |err| error!("audio output error: {err}"),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("failed to build u16 output stream: {e}"))
|
||||
}
|
||||
other => Err(format!("unsupported output sample format: {other:?}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_output_i16(output: &mut [i16], queue: &Arc<Mutex<VecDeque<i16>>>) {
|
||||
if let Ok(mut guard) = queue.lock() {
|
||||
for sample in output {
|
||||
*sample = guard.pop_front().unwrap_or(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
output.fill(0);
|
||||
fn fill_output_i16(
|
||||
output: &mut [i16],
|
||||
queue: &Arc<Mutex<VecDeque<i16>>>,
|
||||
render_processor: &mut RealtimeRenderAudioProcessor,
|
||||
) {
|
||||
let samples = drain_output_samples(output.len(), queue);
|
||||
output.copy_from_slice(&samples);
|
||||
render_processor.process_samples(output);
|
||||
}
|
||||
|
||||
fn fill_output_f32(output: &mut [f32], queue: &Arc<Mutex<VecDeque<i16>>>) {
|
||||
if let Ok(mut guard) = queue.lock() {
|
||||
for sample in output {
|
||||
let v = guard.pop_front().unwrap_or(0);
|
||||
*sample = (v as f32) / (i16::MAX as f32);
|
||||
}
|
||||
return;
|
||||
fn fill_output_f32(
|
||||
output: &mut [f32],
|
||||
queue: &Arc<Mutex<VecDeque<i16>>>,
|
||||
render_processor: &mut RealtimeRenderAudioProcessor,
|
||||
) {
|
||||
let samples = drain_output_samples(output.len(), queue);
|
||||
for (output_sample, sample) in output.iter_mut().zip(samples.iter()) {
|
||||
*output_sample = (*sample as f32) / (i16::MAX as f32);
|
||||
}
|
||||
output.fill(0.0);
|
||||
render_processor.process_samples(&samples);
|
||||
}
|
||||
|
||||
fn fill_output_u16(output: &mut [u16], queue: &Arc<Mutex<VecDeque<i16>>>) {
|
||||
if let Ok(mut guard) = queue.lock() {
|
||||
for sample in output {
|
||||
let v = guard.pop_front().unwrap_or(0);
|
||||
*sample = (v as i32 + 32768).clamp(0, u16::MAX as i32) as u16;
|
||||
}
|
||||
return;
|
||||
fn fill_output_u16(
|
||||
output: &mut [u16],
|
||||
queue: &Arc<Mutex<VecDeque<i16>>>,
|
||||
render_processor: &mut RealtimeRenderAudioProcessor,
|
||||
) {
|
||||
let samples = drain_output_samples(output.len(), queue);
|
||||
for (output_sample, sample) in output.iter_mut().zip(samples.iter()) {
|
||||
*output_sample = (*sample as i32 + 32768).clamp(0, u16::MAX as i32) as u16;
|
||||
}
|
||||
output.fill(32768);
|
||||
render_processor.process_samples(&samples);
|
||||
}
|
||||
|
||||
fn convert_pcm16(
|
||||
input: &[i16],
|
||||
input_sample_rate: u32,
|
||||
input_channels: u16,
|
||||
output_sample_rate: u32,
|
||||
output_channels: u16,
|
||||
) -> Vec<i16> {
|
||||
if input.is_empty() || input_channels == 0 || output_channels == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let in_channels = input_channels as usize;
|
||||
let out_channels = output_channels as usize;
|
||||
let in_frames = input.len() / in_channels;
|
||||
if in_frames == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let out_frames = if input_sample_rate == output_sample_rate {
|
||||
in_frames
|
||||
} else {
|
||||
(((in_frames as u64) * (output_sample_rate as u64)) / (input_sample_rate as u64)).max(1)
|
||||
as usize
|
||||
fn drain_output_samples(output_len: usize, queue: &Arc<Mutex<VecDeque<i16>>>) -> Vec<i16> {
|
||||
let mut samples = vec![0; output_len];
|
||||
let Ok(mut guard) = queue.lock() else {
|
||||
return samples;
|
||||
};
|
||||
|
||||
let mut out = Vec::with_capacity(out_frames.saturating_mul(out_channels));
|
||||
for out_frame_idx in 0..out_frames {
|
||||
let src_frame_idx = if out_frames <= 1 || in_frames <= 1 {
|
||||
0
|
||||
} else {
|
||||
((out_frame_idx as u64) * ((in_frames - 1) as u64) / ((out_frames - 1) as u64)) as usize
|
||||
};
|
||||
let src_start = src_frame_idx.saturating_mul(in_channels);
|
||||
let src = &input[src_start..src_start + in_channels];
|
||||
match (in_channels, out_channels) {
|
||||
(1, 1) => out.push(src[0]),
|
||||
(1, n) => {
|
||||
for _ in 0..n {
|
||||
out.push(src[0]);
|
||||
}
|
||||
}
|
||||
(n, 1) if n >= 2 => {
|
||||
let sum: i32 = src.iter().map(|s| *s as i32).sum();
|
||||
out.push((sum / (n as i32)) as i16);
|
||||
}
|
||||
(n, m) if n == m => out.extend_from_slice(src),
|
||||
(n, m) if n > m => out.extend_from_slice(&src[..m]),
|
||||
(n, m) => {
|
||||
out.extend_from_slice(src);
|
||||
let last = *src.last().unwrap_or(&0);
|
||||
for _ in n..m {
|
||||
out.push(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::convert_pcm16;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn convert_pcm16_downmixes_and_resamples_for_model_input() {
|
||||
let input = vec![100, 300, 200, 400, 500, 700, 600, 800];
|
||||
let converted = convert_pcm16(
|
||||
&input, /*input_sample_rate*/ 48_000, /*input_channels*/ 2,
|
||||
/*output_sample_rate*/ 24_000, /*output_channels*/ 1,
|
||||
);
|
||||
assert_eq!(converted, vec![200, 700]);
|
||||
for sample in &mut samples {
|
||||
*sample = guard.pop_front().unwrap_or(0);
|
||||
}
|
||||
samples
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ exports_files([
|
||||
"v8_bazel_rules.patch",
|
||||
"v8_module_deps.patch",
|
||||
"v8_source_portability.patch",
|
||||
"webrtc-audio-processing-sys_logging_macos_sdk.patch",
|
||||
"webrtc-audio-processing-sys_meson_env.patch",
|
||||
"windows-link.patch",
|
||||
"zstd-sys_windows_msvc_include_dirs.patch",
|
||||
])
|
||||
|
||||
12
patches/webrtc-audio-processing-sys_logging_macos_sdk.patch
Normal file
12
patches/webrtc-audio-processing-sys_logging_macos_sdk.patch
Normal file
@@ -0,0 +1,12 @@
|
||||
diff --git a/webrtc-audio-processing/webrtc/rtc_base/logging.cc b/webrtc-audio-processing/webrtc/rtc_base/logging.cc
|
||||
--- a/webrtc-audio-processing/webrtc/rtc_base/logging.cc
|
||||
+++ b/webrtc-audio-processing/webrtc/rtc_base/logging.cc
|
||||
@@ -24,6 +24,6 @@
|
||||
#undef ERROR // wingdi.h
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
-#include <CoreServices/CoreServices.h>
|
||||
+#include <CoreFoundation/CoreFoundation.h>
|
||||
#elif defined(WEBRTC_ANDROID)
|
||||
#include <android/log.h>
|
||||
401
patches/webrtc-audio-processing-sys_meson_env.patch
Normal file
401
patches/webrtc-audio-processing-sys_meson_env.patch
Normal file
@@ -0,0 +1,401 @@
|
||||
diff --git a/build.rs b/build.rs
|
||||
index 8578e9220a..bd5c9ba382 100644
|
||||
--- a/build.rs
|
||||
+++ b/build.rs
|
||||
@@ -1,10 +1,14 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use bindgen::callbacks::{AttributeInfo, DeriveInfo, ParseCallbacks};
|
||||
+#[cfg(not(windows))]
|
||||
+use std::os::unix::fs::symlink;
|
||||
+#[cfg(windows)]
|
||||
+use std::os::windows::fs::symlink_dir as symlink;
|
||||
use std::{
|
||||
env,
|
||||
- fs::File,
|
||||
+ fs::{self, File},
|
||||
io::{BufWriter, Write},
|
||||
- path::PathBuf,
|
||||
+ path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
@@ -203,8 +207,136 @@ mod webrtc {
|
||||
let webrtc_build_dir = build_dir.join(BUNDLED_SOURCE_PATH);
|
||||
eprintln!("Building webrtc-audio-processing in {}", webrtc_build_dir.display());
|
||||
|
||||
- let mut meson = Command::new("meson");
|
||||
+ let meson_launcher = env::var_os("MESON").expect("MESON is set by Bazel");
|
||||
+ let cargo_manifest_dir =
|
||||
+ env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is set by Bazel");
|
||||
+ let runfiles_dir = Path::new(&cargo_manifest_dir)
|
||||
+ .parent()
|
||||
+ .expect("CARGO_MANIFEST_DIR points inside a Bazel runfiles tree");
|
||||
+ let staged_meson = build_dir.join("meson");
|
||||
+ fs::copy(&meson_launcher, &staged_meson).context("Failed to stage Meson launcher")?;
|
||||
+ let mut meson_runfiles = staged_meson.as_os_str().to_owned();
|
||||
+ meson_runfiles.push(".runfiles");
|
||||
+ let _ = fs::remove_file(&meson_runfiles);
|
||||
+ symlink(runfiles_dir, &meson_runfiles).context("Failed to link Meson runfiles")?;
|
||||
+
|
||||
+ let mut meson = Command::new(staged_meson);
|
||||
+ let mut meson_cross_file = None;
|
||||
+ if let (Ok(host), Ok(target)) = (env::var("HOST"), env::var("TARGET")) {
|
||||
+ if host != target {
|
||||
+ let mut target_parts = target.split('-');
|
||||
+ let cpu_family = target_parts
|
||||
+ .next()
|
||||
+ .expect("TARGET triple has an architecture");
|
||||
+ let system = target_parts
|
||||
+ .next_back()
|
||||
+ .expect("TARGET triple has an operating system");
|
||||
+ let endian = match cpu_family {
|
||||
+ "aarch64" | "x86_64" => "little",
|
||||
+ _ => bail!("Unsupported TARGET architecture for Meson cross file: {cpu_family}"),
|
||||
+ };
|
||||
+ let cross_file = build_dir.join("meson-cross-file.ini");
|
||||
+ fs::write(
|
||||
+ &cross_file,
|
||||
+ format!(
|
||||
+ concat!(
|
||||
+ "[host_machine]\n",
|
||||
+ "system = '{system}'\n",
|
||||
+ "cpu_family = '{cpu_family}'\n",
|
||||
+ "cpu = '{cpu_family}'\n",
|
||||
+ "endian = '{endian}'\n",
|
||||
+ "\n",
|
||||
+ "[properties]\n",
|
||||
+ "needs_exe_wrapper = true\n",
|
||||
+ ),
|
||||
+ system = system,
|
||||
+ cpu_family = cpu_family,
|
||||
+ endian = endian,
|
||||
+ ),
|
||||
+ )
|
||||
+ .context("Failed to write Meson cross file")?;
|
||||
+ meson_cross_file = Some(cross_file);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ let mut ninja_launcher = env::var_os("NINJA");
|
||||
+ if ninja_launcher.is_none() {
|
||||
+ for candidate in ["ninja", "ninja-build"] {
|
||||
+ let can_execute = Command::new(candidate)
|
||||
+ .arg("--version")
|
||||
+ .status()
|
||||
+ .map(|status| status.success())
|
||||
+ .unwrap_or(false);
|
||||
+ if can_execute {
|
||||
+ ninja_launcher = Some(candidate.into());
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if let Some(ninja_real) = env::var_os("NINJA_REAL") {
|
||||
+ let mut temp_dirs = Vec::new();
|
||||
+ if let Some(dir) = env::var_os("CODEX_NINJA_TMPDIR") {
|
||||
+ temp_dirs.push(PathBuf::from(dir));
|
||||
+ }
|
||||
+ temp_dirs.extend([
|
||||
+ PathBuf::from("/private/var/tmp"),
|
||||
+ PathBuf::from("/var/tmp"),
|
||||
+ env::temp_dir(),
|
||||
+ PathBuf::from("/tmp"),
|
||||
+ ]);
|
||||
+ if let Some(home) = env::var_os("HOME") {
|
||||
+ temp_dirs.push(PathBuf::from(home));
|
||||
+ }
|
||||
+
|
||||
+ let mut staged_ninja = None;
|
||||
+ for (attempt, temp_dir) in temp_dirs.into_iter().enumerate() {
|
||||
+ if !temp_dir.is_dir() {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let stage_dir =
|
||||
+ temp_dir.join(format!("codex-ninja-{}-{attempt}", std::process::id()));
|
||||
+ let _ = fs::remove_dir_all(&stage_dir);
|
||||
+ if fs::create_dir_all(&stage_dir).is_err() {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let candidate_ninja = stage_dir.join("ninja");
|
||||
+ let stage_result =
|
||||
+ fs::copy(&ninja_real, &candidate_ninja).and_then(|_| ensure_executable(&candidate_ninja));
|
||||
+ if stage_result.is_ok() {
|
||||
+ staged_ninja = Some(candidate_ninja.into_os_string());
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ ninja_launcher = staged_ninja.or_else(|| Some(ninja_real));
|
||||
+ }
|
||||
+ let ninja_launcher =
|
||||
+ ninja_launcher.ok_or_else(|| anyhow::anyhow!("Failed to locate ninja launcher"))?;
|
||||
+
|
||||
+ if let Some(ldflags) = env::var("LDFLAGS").ok() {
|
||||
+ let execroot = determine_execroot(runfiles_dir)?;
|
||||
+ let ldflags = ldflags
|
||||
+ .split_whitespace()
|
||||
+ .map(|token| {
|
||||
+ if let Some(path) = token.strip_prefix("-Lbazel-out") {
|
||||
+ format!("-L{}", execroot.join(format!("bazel-out{path}")).display())
|
||||
+ } else if token.starts_with("bazel-out") {
|
||||
+ execroot.join(token).display().to_string()
|
||||
+ } else {
|
||||
+ token.to_owned()
|
||||
+ }
|
||||
+ })
|
||||
+ .collect::<Vec<_>>()
|
||||
+ .join(" ");
|
||||
+ meson.env("LDFLAGS", ldflags);
|
||||
+ }
|
||||
meson.args(["setup", "--prefix", install_dir.to_str().unwrap()]);
|
||||
+ meson.env("NINJA", &ninja_launcher);
|
||||
+ if let Some(cross_file) = meson_cross_file {
|
||||
+ meson.arg("--cross-file");
|
||||
+ meson.arg(cross_file);
|
||||
+ }
|
||||
meson.arg("--reconfigure");
|
||||
|
||||
if cfg!(target_os = "macos") {
|
||||
@@ -221,14 +353,14 @@ mod webrtc {
|
||||
.context("Failed to execute meson. Do you have it installed?")?;
|
||||
assert!(status.success(), "Command failed: {:?}", &meson);
|
||||
|
||||
- let mut ninja = Command::new("ninja");
|
||||
+ let mut ninja = Command::new(ninja_launcher.clone());
|
||||
let status = ninja
|
||||
.current_dir(&webrtc_build_dir)
|
||||
.status()
|
||||
.context("Failed to execute ninja. Do you have it installed?")?;
|
||||
assert!(status.success(), "Command failed: {:?}", &ninja);
|
||||
|
||||
- let mut install = Command::new("ninja");
|
||||
+ let mut install = Command::new(ninja_launcher);
|
||||
let status = install
|
||||
.current_dir(&webrtc_build_dir)
|
||||
.arg("install")
|
||||
@@ -267,15 +399,32 @@ mod webrtc {
|
||||
|
||||
/// Extract defined (non-external) symbols from a static library using nm.
|
||||
fn get_defined_symbols(archive_path: &std::path::Path) -> Result<Vec<String>> {
|
||||
- let output = Command::new("nm")
|
||||
+ let nm = determine_nm_path();
|
||||
+ let output = match Command::new(&nm)
|
||||
.arg("--defined-only")
|
||||
.arg("--format=posix")
|
||||
.arg(archive_path)
|
||||
.output()
|
||||
- .context("Failed to execute nm")?;
|
||||
+ {
|
||||
+ Ok(output) => output,
|
||||
+ Err(error)
|
||||
+ if env::var("CARGO_CFG_TARGET_OS")
|
||||
+ .map(|target_os| target_os == "macos")
|
||||
+ .or_else(|_| env::var("TARGET").map(|target| target.contains("apple-darwin")))
|
||||
+ .unwrap_or(false)
|
||||
+ && error.kind() == std::io::ErrorKind::NotFound =>
|
||||
+ {
|
||||
+ println!(
|
||||
+ "cargo:warning=Skipping symbol prefixing because {:?} is unavailable",
|
||||
+ nm
|
||||
+ );
|
||||
+ return Ok(Vec::new());
|
||||
+ }
|
||||
+ Err(error) => return Err(error).context(format!("Failed to execute {:?}", nm)),
|
||||
+ };
|
||||
|
||||
if !output.status.success() {
|
||||
- anyhow::bail!("nm failed: {}", String::from_utf8_lossy(&output.stderr));
|
||||
+ anyhow::bail!("{:?} failed: {}", nm, String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
@@ -400,6 +549,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
let binding_file = out_dir().join("bindings.rs");
|
||||
+ maybe_set_libclang_path();
|
||||
let mut builder = bindgen::Builder::default()
|
||||
.header("src/wrapper.hpp")
|
||||
.clang_args(&["-x", "c++", "-std=c++17", "-fparse-all-comments"])
|
||||
@@ -461,3 +611,180 @@ fn determine_objcopy_path() -> Result<PathBuf> {
|
||||
|
||||
Ok(objcopy)
|
||||
}
|
||||
+
|
||||
+fn determine_execroot(path: &Path) -> Result<PathBuf> {
|
||||
+ for ancestor in path.ancestors() {
|
||||
+ if ancestor.file_name().and_then(|name| name.to_str()) == Some("bazel-out") {
|
||||
+ return ancestor
|
||||
+ .parent()
|
||||
+ .map(Path::to_path_buf)
|
||||
+ .context("bazel-out directory is missing its execroot parent");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ env::current_dir().context("Failed to determine execroot")
|
||||
+}
|
||||
+
|
||||
+#[cfg(not(windows))]
|
||||
+fn ensure_executable(path: &Path) -> std::io::Result<()> {
|
||||
+ use std::os::unix::fs::PermissionsExt;
|
||||
+
|
||||
+ let mut permissions = fs::metadata(path)?.permissions();
|
||||
+ permissions.set_mode(0o755);
|
||||
+ fs::set_permissions(path, permissions)
|
||||
+}
|
||||
+
|
||||
+#[cfg(windows)]
|
||||
+fn ensure_executable(_path: &Path) -> std::io::Result<()> {
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
+fn determine_nm_path() -> PathBuf {
|
||||
+ if let Some(nm) = env::var_os("NM") {
|
||||
+ return nm.into();
|
||||
+ }
|
||||
+
|
||||
+ if env::var("CARGO_CFG_TARGET_OS")
|
||||
+ .map(|target_os| target_os == "macos")
|
||||
+ .or_else(|_| env::var("TARGET").map(|target| target.contains("apple-darwin")))
|
||||
+ .unwrap_or(false)
|
||||
+ {
|
||||
+ let xcrun_output = Command::new("/usr/bin/xcrun")
|
||||
+ .args(["--find", "llvm-nm"])
|
||||
+ .output();
|
||||
+ if let Ok(output) = xcrun_output {
|
||||
+ if output.status.success() {
|
||||
+ let path = String::from_utf8_lossy(&output.stdout).trim().to_owned();
|
||||
+ if !path.is_empty() {
|
||||
+ return PathBuf::from(path);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for tool_env in ["AR", "CC", "CXX"] {
|
||||
+ if let Some(tool_path) = env::var_os(tool_env) {
|
||||
+ let tool_path = PathBuf::from(tool_path);
|
||||
+ if let Some(parent) = tool_path.parent() {
|
||||
+ for candidate_name in ["llvm-nm", "nm"] {
|
||||
+ let candidate = parent.join(candidate_name);
|
||||
+ if candidate.exists() {
|
||||
+ return candidate;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if let Some(file_name) = tool_path.file_name().and_then(|name| name.to_str()) {
|
||||
+ let candidate_name = file_name.replace("llvm-ar", "llvm-nm");
|
||||
+ if candidate_name != file_name {
|
||||
+ let candidate = tool_path.with_file_name(candidate_name);
|
||||
+ if candidate.exists() {
|
||||
+ return candidate;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if file_name != "llvm-nm" {
|
||||
+ let candidate = tool_path.with_file_name("llvm-nm");
|
||||
+ if candidate.exists() {
|
||||
+ return candidate;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for candidate in ["llvm-nm", "nm"] {
|
||||
+ if Command::new(candidate)
|
||||
+ .arg("--version")
|
||||
+ .status()
|
||||
+ .map(|status| status.success())
|
||||
+ .unwrap_or(false)
|
||||
+ {
|
||||
+ return PathBuf::from(candidate);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ PathBuf::from("nm")
|
||||
+}
|
||||
+
|
||||
+fn maybe_set_libclang_path() {
|
||||
+ if env::var_os("LIBCLANG_PATH").is_some() {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for tool_env in ["CC", "CXX"] {
|
||||
+ let Some(tool_value) = env::var_os(tool_env) else {
|
||||
+ continue;
|
||||
+ };
|
||||
+ let tool_path = PathBuf::from(tool_value);
|
||||
+ let tool_path = if tool_path.is_absolute() {
|
||||
+ tool_path
|
||||
+ } else if let Ok(current_dir) = env::current_dir() {
|
||||
+ let current_dir_candidate = current_dir.join(&tool_path);
|
||||
+ if current_dir_candidate.exists() {
|
||||
+ current_dir_candidate
|
||||
+ } else if let Some(manifest_dir) = env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from) {
|
||||
+ let mut resolved = tool_path.clone();
|
||||
+ for ancestor in manifest_dir.ancestors() {
|
||||
+ let ancestor_candidate = ancestor.join(&tool_path);
|
||||
+ if ancestor_candidate.exists() {
|
||||
+ resolved = ancestor_candidate;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ resolved
|
||||
+ } else {
|
||||
+ tool_path
|
||||
+ }
|
||||
+ } else {
|
||||
+ tool_path
|
||||
+ };
|
||||
+
|
||||
+ let Some(tool_dir) = tool_path.parent() else {
|
||||
+ continue;
|
||||
+ };
|
||||
+ let Some(tool_root) = tool_dir.parent() else {
|
||||
+ continue;
|
||||
+ };
|
||||
+
|
||||
+ let mut directories = vec![tool_root.join("lib"), tool_root.join("lib64")];
|
||||
+ if let Ok(read_dir) = fs::read_dir(tool_root) {
|
||||
+ directories.extend(
|
||||
+ read_dir
|
||||
+ .filter_map(std::result::Result::ok)
|
||||
+ .map(|entry| entry.path())
|
||||
+ .filter(|path| path.is_dir()),
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ while let Some(directory) = directories.pop() {
|
||||
+ let read_dir = match fs::read_dir(&directory) {
|
||||
+ Ok(read_dir) => read_dir,
|
||||
+ Err(_) => continue,
|
||||
+ };
|
||||
+
|
||||
+ for entry in read_dir.filter_map(std::result::Result::ok) {
|
||||
+ let path = entry.path();
|
||||
+ if path.is_dir() {
|
||||
+ directories.push(path);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else {
|
||||
+ continue;
|
||||
+ };
|
||||
+ let is_libclang = matches!(file_name, "libclang.so" | "libclang.dll" | "libclang.dylib")
|
||||
+ || file_name.starts_with("libclang.so.")
|
||||
+ || file_name.starts_with("libclang-");
|
||||
+ if !is_libclang {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if let Some(parent) = path.parent() {
|
||||
+ env::set_var("LIBCLANG_PATH", parent);
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
4
tools/ninja_wrapper/BUILD.bazel
Normal file
4
tools/ninja_wrapper/BUILD.bazel
Normal file
@@ -0,0 +1,4 @@
|
||||
exports_files(
|
||||
["ninja_wrapper.sh"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
30
tools/ninja_wrapper/ninja_wrapper.sh
Executable file
30
tools/ninja_wrapper/ninja_wrapper.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
real_ninja="${NINJA_REAL:?NINJA_REAL must point to the downloaded ninja binary}"
|
||||
|
||||
try_exec() {
|
||||
candidate_dir="$1"
|
||||
[ -n "${candidate_dir}" ] || return 1
|
||||
[ -d "${candidate_dir}" ] || return 1
|
||||
|
||||
staged_dir="$(mktemp -d "${candidate_dir%/}/codex-ninja.XXXXXX" 2>/dev/null)" || return 1
|
||||
staged_ninja="${staged_dir}/ninja"
|
||||
|
||||
if cp "${real_ninja}" "${staged_ninja}" 2>/dev/null \
|
||||
&& chmod +x "${staged_ninja}" 2>/dev/null \
|
||||
&& "${staged_ninja}" --version >/dev/null 2>&1; then
|
||||
exec "${staged_ninja}" "$@"
|
||||
fi
|
||||
|
||||
rm -rf "${staged_dir}"
|
||||
return 1
|
||||
}
|
||||
|
||||
for candidate_dir in "${CODEX_NINJA_TMPDIR:-}" /private/var/tmp /var/tmp "${TMPDIR:-}" /tmp "${HOME:-}"; do
|
||||
try_exec "${candidate_dir}"
|
||||
done
|
||||
|
||||
echo "codex ninja wrapper: unable to stage an executable Ninja binary" >&2
|
||||
exit 1
|
||||
Reference in New Issue
Block a user