diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 9a0d12c..5fda9d4 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -6,11 +6,22 @@ fn main() { .add_plugins((DefaultPlugins, bevy_tts::TtsPlugin)) .add_systems(Startup, setup) .add_systems(Update, (event_poll, greet)) + .add_observer(|trigger: Trigger| { + println!("{:?}", trigger.event()); + }) .run(); } // Speaks a bunch of messages and changes TTS properties. -fn setup(mut tts: ResMut) { +fn setup(mut commands: Commands, mut tts: ResMut) { + let mut other_tts = Tts::default(); + let Features { voice, .. } = other_tts.supported_features(); + if voice { + let voices = other_tts.voices().unwrap(); + let v = voices.last().unwrap(); + other_tts.set_voice(&v.clone()).unwrap(); + } + commands.spawn(other_tts); tts.speak("Hello, world.", false).unwrap(); let Features { rate, .. } = tts.supported_features(); if rate { @@ -57,6 +68,11 @@ fn setup(mut tts: ResMut) { tts.set_volume(original_volume).unwrap(); } tts.speak("Press G for a greeting.", false).unwrap(); + tts.speak( + "Press S to speak with a second voice, if you're lucky.", + false, + ) + .unwrap(); } // Reports events from TTS subsystem. @@ -67,8 +83,15 @@ fn event_poll(mut events: EventReader) { } // Shows how to output speech in response to a keypress. -fn greet(input: Res>, mut tts: ResMut) { +fn greet(input: Res>, mut tts: ResMut, mut speaker: Query<&mut Tts>) { if input.just_pressed(KeyCode::KeyG) { tts.speak("Hey there!", true).unwrap(); } + if input.just_pressed(KeyCode::KeyS) { + if let Ok(mut speaker) = speaker.get_single_mut() { + speaker + .speak("Hey there from the TTS component!", true) + .unwrap(); + } + } } diff --git a/src/lib.rs b/src/lib.rs index 5dab707..73c868f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,20 @@ -use bevy::prelude::*; +use bevy::{ + ecs::{component::ComponentId, world::DeferredWorld}, + prelude::*, +}; use crossbeam_channel::{unbounded, Receiver}; pub use tts::{self, Backends, Error, Features, UtteranceId}; -#[derive(Resource, Clone, Deref, DerefMut)] +#[derive(Component, Resource, Clone, Deref, DerefMut)] +#[component(on_add = on_tts_added, on_remove=on_tts_removed)] pub struct Tts(pub tts::Tts); +impl Default for Tts { + fn default() -> Self { + Self(tts::Tts::default().unwrap()) + } +} + impl Tts { pub fn screen_reader_available() -> bool { tts::Tts::screen_reader_available() @@ -18,45 +28,68 @@ pub enum TtsEvent { UtteranceStop(UtteranceId), } -#[derive(Resource)] +#[derive(Component, Resource)] struct TtsChannel(Receiver); -fn poll_callbacks(channel: Res, mut events: EventWriter) { +fn on_tts_added(mut world: DeferredWorld, entity: Entity, _: ComponentId) { + let tts = &world.get::(entity).unwrap(); + let channel = setup_tts(tts); + world.commands().entity(entity).insert(channel); +} + +fn on_tts_removed(mut world: DeferredWorld, entity: Entity, _: ComponentId) { + world.commands().entity(entity).remove::(); +} + +fn poll_callbacks( + mut commands: Commands, + channel: Res, + mut events: EventWriter, + speakers: Query<(Entity, &TtsChannel), With>, +) { if let Ok(msg) = channel.0.try_recv() { events.send(msg); } + for (entity, channel) in &speakers { + if let Ok(msg) = channel.0.try_recv() { + commands.entity(entity).trigger(msg); + } + } } +fn setup_tts(tts: &Tts) -> TtsChannel { + let (tx, rx) = unbounded(); + let tx_begin = tx.clone(); + let tx_end = tx.clone(); + let tx_stop = tx; + let Features { + utterance_callbacks, + .. + } = tts.supported_features(); + if utterance_callbacks { + tts.on_utterance_begin(Some(Box::new(move |utterance| { + tx_begin.send(TtsEvent::UtteranceBegin(utterance)).unwrap(); + }))) + .unwrap(); + tts.on_utterance_end(Some(Box::new(move |utterance| { + tx_end.send(TtsEvent::UtteranceEnd(utterance)).unwrap(); + }))) + .unwrap(); + tts.on_utterance_stop(Some(Box::new(move |utterance| { + tx_stop.send(TtsEvent::UtteranceStop(utterance)).unwrap(); + }))) + .unwrap(); + } + TtsChannel(rx) +} pub struct TtsPlugin; impl Plugin for TtsPlugin { fn build(&self, app: &mut App) { - let tts = tts::Tts::default().unwrap(); - let (tx, rx) = unbounded(); - let tx_begin = tx.clone(); - let tx_end = tx.clone(); - let tx_stop = tx; - let Features { - utterance_callbacks, - .. - } = tts.supported_features(); - if utterance_callbacks { - tts.on_utterance_begin(Some(Box::new(move |utterance| { - tx_begin.send(TtsEvent::UtteranceBegin(utterance)).unwrap(); - }))) - .unwrap(); - tts.on_utterance_end(Some(Box::new(move |utterance| { - tx_end.send(TtsEvent::UtteranceEnd(utterance)).unwrap(); - }))) - .unwrap(); - tts.on_utterance_stop(Some(Box::new(move |utterance| { - tx_stop.send(TtsEvent::UtteranceStop(utterance)).unwrap(); - }))) - .unwrap(); - } - let tts = Tts(tts); + let tts = Tts::default(); + let channel = setup_tts(&tts); app.add_event::() - .insert_resource(TtsChannel(rx)) + .insert_resource(channel) .insert_resource(tts) .add_systems(Update, poll_callbacks); }