config files
This commit is contained in:
@@ -8,10 +8,12 @@ use bevy::{
|
||||
reflect::{std_traits::ReflectDefault, Reflect},
|
||||
utils::AHasher,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Reflect, Resource, Clone)]
|
||||
#[derive(Reflect, Resource, Clone, Deserialize, Serialize)]
|
||||
#[reflect(Resource, Default, Debug)]
|
||||
pub struct GrpcBaseUrl {
|
||||
#[serde(skip)]
|
||||
hash: u64,
|
||||
url: Cow<'static, str>,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ mod player;
|
||||
mod position;
|
||||
mod side;
|
||||
mod srs_socket_addr;
|
||||
mod stt_base_url;
|
||||
mod unit_type;
|
||||
|
||||
pub use awacs::*;
|
||||
@@ -20,4 +21,5 @@ pub use player::*;
|
||||
pub use position::*;
|
||||
pub use side::*;
|
||||
pub use srs_socket_addr::*;
|
||||
pub use stt_base_url::*;
|
||||
pub use unit_type::*;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use bevy::ecs::system::Resource;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Resource, Clone)]
|
||||
#[derive(Debug, Resource, Clone, Deserialize, Serialize)]
|
||||
pub struct SrsSocketAddr(SocketAddr);
|
||||
|
||||
impl Default for SrsSocketAddr {
|
||||
|
||||
81
crates/guardian_core/src/components/stt_base_url.rs
Normal file
81
crates/guardian_core/src/components/stt_base_url.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
use bevy::{
|
||||
ecs::{reflect::ReflectResource, system::Resource},
|
||||
reflect::{std_traits::ReflectDefault, Reflect},
|
||||
utils::AHasher,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Reflect, Resource, Clone, Deserialize, Serialize)]
|
||||
#[reflect(Resource, Default, Debug)]
|
||||
pub struct SttBaseUrl {
|
||||
#[serde(skip)]
|
||||
hash: u64,
|
||||
url: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl Default for SttBaseUrl {
|
||||
fn default() -> Self {
|
||||
SttBaseUrl::new("http://127.0.0.1:3000/")
|
||||
}
|
||||
}
|
||||
|
||||
impl SttBaseUrl {
|
||||
/// Creates a new [`SttBaseUrl`] from any string-like type.
|
||||
///
|
||||
/// The internal hash will be computed immediately.
|
||||
pub fn new(url: impl Into<Cow<'static, str>>) -> Self {
|
||||
let url = url.into();
|
||||
let mut url = SttBaseUrl { url, hash: 0 };
|
||||
url.update_hash();
|
||||
url
|
||||
}
|
||||
|
||||
/// Sets the entity's url.
|
||||
///
|
||||
/// The internal hash will be re-computed.
|
||||
#[inline(always)]
|
||||
pub fn set(&mut self, url: impl Into<Cow<'static, str>>) {
|
||||
*self = SttBaseUrl::new(url);
|
||||
}
|
||||
|
||||
/// Updates the url of the entity in place.
|
||||
///
|
||||
/// This will allocate a new string if the url was previously
|
||||
/// created from a borrow.
|
||||
#[inline(always)]
|
||||
pub fn mutate<F: FnOnce(&mut String)>(&mut self, f: F) {
|
||||
f(self.url.to_mut());
|
||||
self.update_hash();
|
||||
}
|
||||
|
||||
/// Gets the url of the entity as a `&str`.
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.url
|
||||
}
|
||||
|
||||
fn update_hash(&mut self) {
|
||||
let mut hasher = AHasher::default();
|
||||
self.url.hash(&mut hasher);
|
||||
self.hash = hasher.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SttBaseUrl {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self.url, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SttBaseUrl {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(&self.url, f)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ mod voice_codec;
|
||||
mod voice_command;
|
||||
|
||||
use guardian_commands::{call::parse_call, ConsoleCommandEntered, ConsoleConfiguration};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use simsearch::SimSearch;
|
||||
pub use voice_command::VoiceCommand;
|
||||
|
||||
@@ -104,6 +105,7 @@ fn listen_srs(
|
||||
>,
|
||||
tokio: Res<TokioResource>,
|
||||
addr: Res<SrsSocketAddr>,
|
||||
stt_url: Res<SttBaseUrl>,
|
||||
) {
|
||||
for (ent, id, callsign, position, mut radio, red, blue) in units.iter_mut() {
|
||||
let addr: SocketAddr = addr.clone().into();
|
||||
@@ -159,6 +161,8 @@ fn listen_srs(
|
||||
|
||||
let frequency = radio.frequency;
|
||||
let id = *id;
|
||||
let voice = radio.voice;
|
||||
let stt_url = stt_url.as_str().to_string();
|
||||
radio.handle = Some(tokio.0.spawn(async move {
|
||||
let tcp = TcpStream::connect(addr).await?;
|
||||
|
||||
@@ -246,7 +250,7 @@ fn listen_srs(
|
||||
}
|
||||
}
|
||||
Some(data) = voice_handle.recv() => {
|
||||
frames.push(synthesize(data.as_str()).await?).await;
|
||||
frames.push(synthesize(data.as_str(), &voice).await?).await;
|
||||
}
|
||||
Some(Ok(data)) = voice_stream.next() => {
|
||||
// Collect voice packets
|
||||
@@ -280,7 +284,7 @@ fn listen_srs(
|
||||
}
|
||||
}
|
||||
|
||||
let Some(message) = whisper("http://192.168.0.2:3000/", &wav_data).await else {
|
||||
let Some(message) = whisper(&stt_url, &wav_data).await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -487,6 +491,22 @@ fn handle_voice_command(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub enum Voice {
|
||||
#[default]
|
||||
David,
|
||||
Zira,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Voice {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::David => write!(f, "David"),
|
||||
Self::Zira => write!(f, "Zira"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct ClientHandler(Receiver<Client>);
|
||||
|
||||
@@ -494,6 +514,7 @@ pub struct ClientHandler(Receiver<Client>);
|
||||
pub struct Radio {
|
||||
pub frequency: u64, // the way srs wants it
|
||||
pub modulation: Modulation,
|
||||
pub voice: Voice,
|
||||
sguid: String,
|
||||
voice_sink: Option<Sender<VoiceMessage>>,
|
||||
message_sink: Option<Sender<MessageRequest>>,
|
||||
@@ -501,10 +522,12 @@ pub struct Radio {
|
||||
}
|
||||
|
||||
impl Radio {
|
||||
pub fn new(frequency: u64, modulation: Modulation) -> Self {
|
||||
pub fn new(frequency: u64, modulation: impl Into<Modulation>, voice: Voice) -> Self {
|
||||
let modulation = modulation.into();
|
||||
Self {
|
||||
frequency,
|
||||
modulation,
|
||||
voice,
|
||||
sguid: create_sguid(),
|
||||
voice_sink: None,
|
||||
message_sink: None,
|
||||
|
||||
@@ -129,9 +129,8 @@ impl Default for Radio {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum Modulation {
|
||||
Am = 0,
|
||||
Fm = 1,
|
||||
|
||||
@@ -7,14 +7,15 @@ use windows::core::HSTRING;
|
||||
use windows::Media::SpeechSynthesis::SpeechSynthesizer;
|
||||
use windows::Storage::Streams::DataReader;
|
||||
|
||||
use crate::srs::Voice;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WinConfig {
|
||||
pub voice: Option<String>,
|
||||
}
|
||||
|
||||
impl WinConfig {
|
||||
pub fn new() -> Self {
|
||||
let voice = "David"; // for now
|
||||
pub fn new(voice: &str) -> Self {
|
||||
Self {
|
||||
voice: Some(voice.to_string()),
|
||||
}
|
||||
@@ -23,8 +24,8 @@ impl WinConfig {
|
||||
|
||||
static MUTEX: Mutex<()> = Mutex::const_new(());
|
||||
|
||||
pub async fn synthesize(text: &str) -> Result<Vec<Vec<u8>>, WinError> {
|
||||
let config = WinConfig::new();
|
||||
pub async fn synthesize(text: &str, voice: &Voice) -> Result<Vec<Vec<u8>>, WinError> {
|
||||
let config = WinConfig::new(&voice.to_string());
|
||||
// Note, there does not seem to be a way to explicitly set 16000kHz, 16 audio bits per
|
||||
// sample and mono channel.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user