Compare commits

..

14 Commits
v0.7.0 ... main

Author SHA1 Message Date
a6c22b4eb0 Release
All checks were successful
Test / test (ubuntu-latest) (push) Successful in 2m17s
2024-12-05 16:17:26 -06:00
a1c7c48003 Update CHANGELOG. 2024-12-05 16:17:05 -06:00
3891f289d8 feat: Tts can now be used as a component, and multiple TTS instances are supported. 2024-12-05 16:16:23 -06:00
0ef7579f91 chore: Upgrade to Bevy 0.15. 2024-12-05 15:25:34 -06:00
5c682ce6ee Fix workflow.
All checks were successful
Test / test (ubuntu-latest) (push) Successful in 2m7s
2024-07-07 09:57:55 -05:00
8d45d0a804 Release
All checks were successful
Test / test (ubuntu-latest) (push) Successful in 2m16s
2024-07-06 21:13:00 -05:00
66c1522576 Update CHANGELOG. 2024-07-06 21:12:37 -05:00
7fbf5f12f0 Fix workflows.
All checks were successful
Test / test (ubuntu-latest) (push) Successful in 1m59s
2024-07-06 19:58:16 -05:00
a6de8e190c chore: Update to Bevy 0.14. 2024-07-06 19:46:23 -05:00
7ad2070fa9 chore: Integrate pre-commit. 2024-07-06 19:27:50 -05:00
3ef32f30b9 chore: Switch to Gitea Actions. 2024-07-06 19:27:14 -05:00
947144357c Release 2024-03-14 12:51:44 -05:00
4eab6322f7 Update changelog. 2024-03-14 12:51:35 -05:00
8ac16ee97a chore: Upgrade to Bevy v0.13. 2024-03-14 12:51:17 -05:00
8 changed files with 203 additions and 79 deletions

View File

@ -1,42 +0,0 @@
kind: pipeline
type: docker
name: default
steps:
- name: test
image: rust
pull: always
commands:
- rustup component add clippy rustfmt
- apt-get update -qq
- apt-get install -qqy llvm-dev libclang-dev clang libspeechd-dev pkg-config libx11-dev libasound2-dev libudev-dev libxcb-xfixes0-dev libwayland-dev libxkbcommon-dev libvulkan-dev libpulse-dev
- cargo fmt --check
- cargo test --no-default-features --features=speech_dispatcher_0_10
- cargo clippy --no-default-features --features=speech_dispatcher_0_10
- name: release
image: rust
pull: always
commands:
- cargo publish --no-verify
when:
ref:
- refs/tags/v*
environment:
CARGO_REGISTRY_TOKEN:
from_secret: cargo_registry_token
- name: discord notification
image: appleboy/drone-discord
when:
status: [success, failure]
settings:
webhook_id:
from_secret: discord_webhook_id
webhook_token:
from_secret: discord_webhook_token
tts: true
message: >
{{#success build.status}}
{{repo.name}} build {{build.number}} succeeded: <{{build.link}}>
{{else}}
{{repo.name}} build {{build.number}} failed: <{{build.link}}>
{{/success}}

View File

@ -0,0 +1,39 @@
name: Release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+*"
workflow_dispatch:
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: rustfmt, clippy
- name: install Linux build dependencies
run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libasound2-dev libudev-dev libwayland-dev libclang-dev libspeechd-dev
if: runner.os == 'linux'
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.1
- name: Publish
run: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

37
.gitea/workflows/test.yml Normal file
View File

@ -0,0 +1,37 @@
name: Test
on:
pull_request:
push:
jobs:
test:
strategy:
matrix:
# os: [windows-latest, ubuntu-latest, macos-latest]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: rustfmt, clippy
- name: install Linux build dependencies
run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libasound2-dev libudev-dev libwayland-dev libclang-dev libspeechd-dev
if: runner.os == 'linux'
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.1

10
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,10 @@
fail_fast: true
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: fmt
args: [--, --check]
- id: cargo-check
args: [--bins, --examples]
- id: clippy

View File

@ -2,6 +2,30 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## Version 0.10.0 - 2024-12-05
### Features
- `Tts` can now be used as a component, and multiple TTS instances are supported.
### Miscellaneous Tasks
- Upgrade to Bevy 0.15.
## Version 0.9.0 - 2024-07-07
### Miscellaneous Tasks
- Switch to Gitea Actions.
- Integrate pre-commit.
- Update to Bevy 0.14.
## Version 0.8.0 - 2024-03-14
### Miscellaneous Tasks
- Upgrade to Bevy v0.13.
## Version 0.7.0 - 2024-02-09 ## Version 0.7.0 - 2024-02-09
### Miscellaneous Tasks ### Miscellaneous Tasks

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_tts" name = "bevy_tts"
version = "0.7.0" version = "0.10.0"
description = "Text-to-speech for the Bevy game engine" description = "Text-to-speech for the Bevy game engine"
repository = "https://labs.lightsout.games/projects/bevy_tts" repository = "https://labs.lightsout.games/projects/bevy_tts"
authors = ["Nolan Darilek <nolan@thewordnerd.info>"] authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
@ -17,12 +17,12 @@ speech_dispatcher_0_11 = ["tts/speech_dispatcher_0_11"]
tolk = ["tts/tolk"] tolk = ["tts/tolk"]
[dependencies] [dependencies]
bevy = { version = "0.12", default-features = false } bevy = { version = "0.15", default-features = false }
crossbeam-channel = "0.5" crossbeam-channel = "0.5"
tts = { version = "0.26", default-features = false } tts = { version = "0.26", default-features = false }
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.12", default-features = true } bevy = { version = "0.15", default-features = true }
[package.metadata.release] [package.metadata.release]
publish = false publish = false

View File

@ -5,12 +5,23 @@ fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, bevy_tts::TtsPlugin)) .add_plugins((DefaultPlugins, bevy_tts::TtsPlugin))
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, (bevy::window::close_on_esc, event_poll, greet)) .add_systems(Update, (event_poll, greet))
.add_observer(|trigger: Trigger<TtsEvent>| {
println!("{:?}", trigger.event());
})
.run(); .run();
} }
// Speaks a bunch of messages and changes TTS properties. // Speaks a bunch of messages and changes TTS properties.
fn setup(mut tts: ResMut<Tts>) { fn setup(mut commands: Commands, mut tts: ResMut<Tts>) {
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(); tts.speak("Hello, world.", false).unwrap();
let Features { rate, .. } = tts.supported_features(); let Features { rate, .. } = tts.supported_features();
if rate { if rate {
@ -57,18 +68,30 @@ fn setup(mut tts: ResMut<Tts>) {
tts.set_volume(original_volume).unwrap(); tts.set_volume(original_volume).unwrap();
} }
tts.speak("Press G for a greeting.", false).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. // Reports events from TTS subsystem.
fn event_poll(mut events: EventReader<TtsEvent>) { fn event_poll(mut events: EventReader<TtsEvent>) {
for event in events.iter() { for event in events.read() {
println!("{:?}", event); println!("{:?}", event);
} }
} }
// Shows how to output speech in response to a keypress. // Shows how to output speech in response to a keypress.
fn greet(input: Res<Input<KeyCode>>, mut tts: ResMut<Tts>) { fn greet(input: Res<ButtonInput<KeyCode>>, mut tts: ResMut<Tts>, mut speaker: Query<&mut Tts>) {
if input.just_pressed(KeyCode::G) { if input.just_pressed(KeyCode::KeyG) {
tts.speak("Hey there!", true).unwrap(); 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();
}
}
} }

View File

@ -1,10 +1,20 @@
use bevy::prelude::*; use bevy::{
ecs::{component::ComponentId, world::DeferredWorld},
prelude::*,
};
use crossbeam_channel::{unbounded, Receiver}; use crossbeam_channel::{unbounded, Receiver};
pub use tts::{self, Backends, Error, Features, UtteranceId}; 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); pub struct Tts(pub tts::Tts);
impl Default for Tts {
fn default() -> Self {
Self(tts::Tts::default().unwrap())
}
}
impl Tts { impl Tts {
pub fn screen_reader_available() -> bool { pub fn screen_reader_available() -> bool {
tts::Tts::screen_reader_available() tts::Tts::screen_reader_available()
@ -18,20 +28,36 @@ pub enum TtsEvent {
UtteranceStop(UtteranceId), UtteranceStop(UtteranceId),
} }
#[derive(Resource)] #[derive(Component, Resource)]
struct TtsChannel(Receiver<TtsEvent>); struct TtsChannel(Receiver<TtsEvent>);
fn poll_callbacks(channel: Res<TtsChannel>, mut events: EventWriter<TtsEvent>) { fn on_tts_added(mut world: DeferredWorld, entity: Entity, _: ComponentId) {
let tts = &world.get::<Tts>(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::<TtsChannel>();
}
fn poll_callbacks(
mut commands: Commands,
channel: Res<TtsChannel>,
mut events: EventWriter<TtsEvent>,
speakers: Query<(Entity, &TtsChannel), With<Tts>>,
) {
if let Ok(msg) = channel.0.try_recv() { if let Ok(msg) = channel.0.try_recv() {
events.send(msg); events.send(msg);
} }
for (entity, channel) in &speakers {
if let Ok(msg) = channel.0.try_recv() {
commands.entity(entity).trigger(msg);
}
}
} }
pub struct TtsPlugin; fn setup_tts(tts: &Tts) -> TtsChannel {
impl Plugin for TtsPlugin {
fn build(&self, app: &mut App) {
let tts = tts::Tts::default().unwrap();
let (tx, rx) = unbounded(); let (tx, rx) = unbounded();
let tx_begin = tx.clone(); let tx_begin = tx.clone();
let tx_end = tx.clone(); let tx_end = tx.clone();
@ -54,9 +80,16 @@ impl Plugin for TtsPlugin {
}))) })))
.unwrap(); .unwrap();
} }
let tts = Tts(tts); TtsChannel(rx)
}
pub struct TtsPlugin;
impl Plugin for TtsPlugin {
fn build(&self, app: &mut App) {
let tts = Tts::default();
let channel = setup_tts(&tts);
app.add_event::<TtsEvent>() app.add_event::<TtsEvent>()
.insert_resource(TtsChannel(rx)) .insert_resource(channel)
.insert_resource(tts) .insert_resource(tts)
.add_systems(Update, poll_callbacks); .add_systems(Update, poll_callbacks);
} }