basic TTS

This commit is contained in:
Scott Pruett 2022-06-05 16:58:45 -04:00
parent 05fca55c73
commit 9e4015c53c
10 changed files with 541 additions and 30 deletions

358
Cargo.lock generated
View File

@ -41,6 +41,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "android_glue"
version = "0.2.3"
@ -76,6 +85,17 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi 0.3.9",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -88,6 +108,29 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bindgen"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c0310d795615ac3541fa6312cf5b26b520852118e66165fcd23af4d544dc90a"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"clap",
"env_logger",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"which",
]
[[package]]
name = "bit_field"
version = "0.10.1"
@ -178,6 +221,15 @@ 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 = "0.1.10"
@ -199,6 +251,41 @@ dependencies = [
"libc",
]
[[package]]
name = "clang-sys"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "3.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
dependencies = [
"atty",
"bitflags",
"clap_lex",
"indexmap",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "clipboard-win"
version = "4.4.1"
@ -486,6 +573,33 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "dyn-clonable"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4"
dependencies = [
"dyn-clonable-impl",
"dyn-clone",
]
[[package]]
name = "dyn-clonable-impl"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dyn-clone"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28"
[[package]]
name = "eframe"
version = "0.18.0"
@ -573,6 +687,19 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "env_logger"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "epaint"
version = "0.18.1"
@ -726,6 +853,12 @@ dependencies = [
"xml-rs",
]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "glow"
version = "0.11.2"
@ -816,6 +949,12 @@ version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -825,6 +964,12 @@ dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "ident_case"
version = "1.0.1"
@ -889,6 +1034,16 @@ dependencies = [
"transpose 0.2.1",
]
[[package]]
name = "indexmap"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "inflate"
version = "0.4.5"
@ -972,6 +1127,12 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lebe"
version = "0.5.1"
@ -1316,6 +1477,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
"objc_exception",
]
[[package]]
@ -1329,6 +1491,15 @@ dependencies = [
"objc_id",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
dependencies = [
"cc",
]
[[package]]
name = "objc_id"
version = "0.1.1"
@ -1344,6 +1515,12 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b10983b38c53aebdf33f542c6275b0f58a238129d00c4ae0e6fb59738d783ca"
[[package]]
name = "os_str_bytes"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
[[package]]
name = "osmesa-sys"
version = "0.1.2"
@ -1410,6 +1587,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -1566,12 +1749,35 @@ dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustdct"
version = "0.4.0"
@ -1680,6 +1886,12 @@ dependencies = [
"libc",
]
[[package]]
name = "shlex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]]
name = "slotmap"
version = "1.0.6"
@ -1714,6 +1926,25 @@ dependencies = [
"wayland-protocols",
]
[[package]]
name = "speech-dispatcher"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "011a4f56750a1a31b41df25e27e55a4d46b3190bfa8f8d2f4c097446ad9702af"
dependencies = [
"lazy_static",
"speech-dispatcher-sys",
]
[[package]]
name = "speech-dispatcher-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c3e8acdf2b1f4bb13f1813b40b52f3edf4cc94d8a55fe713a584f672a10388d"
dependencies = [
"bindgen",
]
[[package]]
name = "spin"
version = "0.9.3"
@ -1756,6 +1987,7 @@ dependencies = [
"serde",
"serde_json",
"time",
"tts",
]
[[package]]
@ -1769,6 +2001,21 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.31"
@ -1820,6 +2067,12 @@ dependencies = [
"num_threads",
]
[[package]]
name = "tinystr"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -1898,6 +2151,47 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c74c96594835e10fa545e2a51e8709f30b173a092bfd6036ef2cec53376244f3"
[[package]]
name = "tts"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35f58408d8e9315bb8b4de7be684f638c666380576d184542dbb794f5ce39fdf"
dependencies = [
"cocoa-foundation",
"core-foundation 0.9.3",
"dyn-clonable",
"jni",
"lazy_static",
"libc",
"log",
"ndk-glue 0.6.2",
"objc",
"speech-dispatcher",
"thiserror",
"unic-langid",
"wasm-bindgen",
"web-sys",
"windows",
]
[[package]]
name = "unic-langid"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5"
dependencies = [
"unic-langid-impl",
]
[[package]]
name = "unic-langid-impl"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d"
dependencies = [
"tinystr",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"
@ -2139,6 +2433,17 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4"
[[package]]
name = "which"
version = "4.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
dependencies = [
"either",
"lazy_static",
"libc",
]
[[package]]
name = "widestring"
version = "0.5.1"
@ -2191,17 +2496,30 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
dependencies = [
"windows_aarch64_msvc 0.37.0",
"windows_i686_gnu 0.37.0",
"windows_i686_msvc 0.37.0",
"windows_x86_64_gnu 0.37.0",
"windows_x86_64_msvc 0.37.0",
]
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
"windows_aarch64_msvc 0.36.1",
"windows_i686_gnu 0.36.1",
"windows_i686_msvc 0.36.1",
"windows_x86_64_gnu 0.36.1",
"windows_x86_64_msvc 0.36.1",
]
[[package]]
@ -2210,30 +2528,60 @@ version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_aarch64_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_gnu"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_i686_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "windows_x86_64_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
[[package]]
name = "winit"
version = "0.26.1"

View File

@ -21,4 +21,6 @@ img_hash = "3"
csv = "1"
time = { version = "0.3", features = ["formatting", "local-offset"] }
rand = "0.8"
rand = "0.8"
tts = "0.22"

View File

@ -91,5 +91,6 @@
"track_recognition_threshold": 10,
"dump_frame_fraction": null,
"light_mode": false,
"font_scale": 1.2
"font_scale": 1.2,
"tts": {}
}

View File

@ -38,6 +38,14 @@ fn check_penalty(image: &RgbImage, config: &Config) -> bool {
}
fn is_finished_lap(state: &AppState, frame: &LapState) -> bool {
let lap_time = match frame.lap_time {
Some(l) => l,
None => return false,
};
if lap_time < Duration::from_secs(5) {
return false;
}
if let Some(race) = &state.current_race {
if let Some(last_finish) = &race.last_lap_record_time {
let diff = Instant::now().duration_since(*last_finish);
@ -47,9 +55,7 @@ fn is_finished_lap(state: &AppState, frame: &LapState) -> bool {
}
}
if let Some(prev_frame) = state.buffered_frames.back() {
prev_frame.lap_time.is_some()
&& prev_frame.lap_time == frame.lap_time
&& prev_frame.lap_time.unwrap() > Duration::from_secs(5)
prev_frame.lap_time.is_some() && (prev_frame.lap_time == frame.lap_time || prev_frame.lap.unwrap_or(usize::MAX - 1) + 1 == frame.lap.unwrap_or_default())
} else {
false
}
@ -90,6 +96,7 @@ fn handle_new_frame(state: &mut AppState, lap_state: LapState, image: &RgbImage)
state.frames_without_lap = 0;
if state.current_race.is_none() {
crate::tts::pre_race_summary(&state.tts, state.config.as_ref(), &lap_state);
state.penalties_this_lap = 0;
let track_hash = get_track_hash(state.config.as_ref(), image);
@ -154,6 +161,9 @@ fn handle_new_frame(state: &mut AppState, lap_state: LapState, image: &RgbImage)
if merged.lap.is_none() {
merged.lap = Some(race.laps.len() + 1);
}
crate::tts::lap_summary(&state.tts, state.config.as_ref(), &merged, race);
race.laps.push(merged);
race.last_lap_record_time = Some(Instant::now());
}

View File

@ -14,6 +14,17 @@ fn default_ocr_interval_ms() -> u64 {
fn default_font_scale() -> f32 {
1.0
}
fn default_true() -> bool { true }
#[derive(Default, Deserialize, Serialize, Clone)]
pub struct TtsConfig {
#[serde(default = "default_true")]
pub pre_race: bool,
#[serde(default = "default_true")]
pub first_lap_summary: bool,
#[serde(default = "Default::default")]
pub every_lap_summary: bool,
}
#[derive(Default, Serialize, Deserialize, Clone)]
pub struct Config {
@ -35,6 +46,8 @@ pub struct Config {
#[serde(default = "default_font_scale")]
pub font_scale: f32,
pub tts: Option<TtsConfig>,
}
impl Config {

View File

@ -46,18 +46,22 @@
"use_ocr_cache": null
},
{
"name": "position",
"x": 50,
"y": 44,
"width": 250,
"height": 100
"name": "position",
"x": 50,
"y": 44,
"width": 250,
"height": 100,
"threshold": null,
"use_ocr_cache": null
},
{
"name": "lap",
"x": 2290,
"y": 44,
"width": 230,
"height": 100
"name": "lap",
"x": 2290,
"y": 44,
"width": 230,
"height": 100,
"threshold": null,
"use_ocr_cache": null
}
],
"track_region": {
@ -75,13 +79,17 @@
"y": 120,
"width": 50,
"height": 30,
"threshold": 0.90,
"threshold": 0.9,
"use_ocr_cache": null
},
"penalty_orange_color": [255, 177, 0],
"penalty_orange_color": [
255,
177,
0
],
"ocr_interval_ms": 500,
"track_recognition_threshold": 10,
"dump_frame_fraction": null,
"light_mode": false,
"font_scale": 1.3
"font_scale": 1.2
}

View File

@ -10,6 +10,7 @@ mod ocr_db;
mod state;
mod stats_writer;
mod training_ui;
mod tts;
use std::{
collections::HashMap,
@ -32,16 +33,20 @@ use learned_tracks::LearnedTracks;
use ocr_db::OcrDatabase;
use state::{AppState, DebugOcrFrame, LapState, RaceState, SharedAppState};
use stats_writer::export_race_stats;
use crate::tts::Tts;
fn main() -> anyhow::Result<()> {
let mode = std::env::args().nth(1).unwrap_or_default();
if mode == "train" {
return training_ui::training_ui();
}
let config = Arc::new(Config::load().unwrap());
let tts = Tts::load(config.as_ref());
let app_state = AppState {
config: Arc::new(Config::load().unwrap()),
config,
learned_tracks: Arc::new(LearnedTracks::load().unwrap()),
ocr_db: Arc::new(OcrDatabase::load().unwrap()),
tts,
..Default::default()
};
let state = Arc::new(Mutex::new(app_state));

View File

@ -8,7 +8,7 @@ use egui_extras::RetainedImage;
use image::RgbImage;
use time::{format_description, OffsetDateTime};
use crate::{config::Config, learned_tracks::LearnedTracks, ocr_db::OcrDatabase};
use crate::{config::Config, learned_tracks::LearnedTracks, ocr_db::OcrDatabase, tts::Tts};
#[derive(Debug, Clone, Default)]
pub struct LapState {
@ -88,15 +88,20 @@ impl LapState {
}
}
fn median_wear(values: Vec<Option<usize>>) -> Option<usize> {
fn wear_values(values: &[Option<usize>]) -> Vec<usize> {
let mut wear_values = Vec::new();
let mut last_value = 100;
for val in values.into_iter().flatten() {
if val < last_value {
for val in values.iter().flatten() {
if *val < last_value {
wear_values.push(last_value - val);
}
last_value = val;
last_value = *val;
}
wear_values
}
fn median_wear(values: Vec<Option<usize>>) -> Option<usize> {
let mut wear_values = wear_values(&values);
wear_values.sort_unstable();
wear_values.get(wear_values.len() / 2).cloned()
}
@ -148,6 +153,23 @@ impl RaceState {
pub fn gas_per_lap(&self) -> Option<usize> {
median_wear(self.laps.iter().map(|lap| lap.gas).collect())
}
pub fn latest_gas_diff(&self, to: usize) -> Option<usize> {
let last_gas = self.laps.iter().last().map(|l| l.gas).unwrap_or(Some(100))?;
if to < last_gas {
Some(last_gas - to)
} else {
None
}
}
pub fn latest_tyre_diff(&self, to: usize) -> Option<usize> {
let last_tyres = self.laps.iter().last().map(|l| l.tyres).unwrap_or(Some(100))?;
if to < last_tyres {
Some(last_tyres - to)
} else {
None
}
}
}
pub struct DebugOcrFrame {
@ -177,6 +199,7 @@ pub struct AppState {
pub config: Arc<Config>,
pub learned_tracks: Arc<LearnedTracks>,
pub ocr_db: Arc<OcrDatabase>,
pub tts: Tts,
}
pub type SharedAppState = Arc<Mutex<AppState>>;

101
src/tts.rs Normal file
View File

@ -0,0 +1,101 @@
use crate::{
config::Config,
state::{LapState, RaceState},
};
enum TtsKind {
PreRace,
FirstLap,
LapSummary,
}
impl TtsKind {
fn is_enabled(&self, config: &Config) -> bool {
if let Some(cfg) = &config.tts {
match self {
TtsKind::PreRace => cfg.pre_race,
TtsKind::FirstLap => cfg.first_lap_summary,
TtsKind::LapSummary => cfg.every_lap_summary,
}
} else {
false
}
}
}
#[derive(Clone, Default)]
pub struct Tts {
engine: Option<tts::Tts>,
}
impl Tts {
pub fn load(config: &Config) -> Self {
let engine = if config.tts.is_some() {
Some(tts::Tts::default().unwrap())
} else {
None
};
Self { engine }
}
fn speak(&self, config: &Config, kind: TtsKind, phrase: &str) {
if !kind.is_enabled(config) {
return;
}
if let Some(mut engine) = self.engine.clone() {
let _ignored_error = engine.speak(phrase, false);
}
}
}
pub fn pre_race_summary(tts: &Tts, config: &Config, lap_state: &LapState) {
if let Some(total_laps) = lap_state.total_laps {
if total_laps > 1 {
tts.speak(
config,
TtsKind::PreRace,
&format!(
"You are P {}. {} laps. For no pit {} gas per lap or less.",
lap_state.position.unwrap_or_default(),
total_laps,
100 / total_laps
),
);
}
}
}
pub fn lap_summary(tts: &Tts, config: &Config, lap_state: &LapState, race: &RaceState) {
let (lap, gas, tyres) = match (lap_state.lap, lap_state.gas, lap_state.tyres) {
(Some(l), Some(g), Some(t)) => (l, g, t),
_ => return,
};
if lap >= race.total_laps {
return;
}
let (gas_wear, tyre_wear) = match (race.latest_gas_diff(gas), race.latest_tyre_diff(tyres)) {
(Some(gw), Some(tw)) => (gw, tw),
_ => return,
};
if lap == 1 {
let laps_per_tank = 100 / gas_wear;
let pit_stops = race.total_laps / laps_per_tank;
tts.speak(
config,
TtsKind::FirstLap,
&format!(
"{} gas per lap, {} tires per lap, likely {} pits",
gas_wear, tyre_wear, pit_stops
),
)
} else {
tts.speak(
config,
TtsKind::LapSummary,
&format!("{} gas, {} tires", gas_wear, tyre_wear,),
)
}
}

View File

@ -4,8 +4,8 @@
- Editable lap stats
- Autocomplete for car/track
- Detect car name from load screen
- TTS for pit strategies
- Post race metric charts
- [PARTIAL] TTS for pit strategies
- [DONE] Show delta positions
- [DONE] Track penalties
- [DONE] Re-do debug/learn functionality