Sound works

This commit is contained in:
2026-04-19 22:56:58 +03:00
parent f0379925f1
commit 7a7ec13cb2
5 changed files with 528 additions and 1 deletions
+1
View File
@@ -1,4 +1,5 @@
pub(crate) mod words;
pub(crate) mod card_sets;
pub(crate) mod card_stats;
pub(crate) mod voice;
+39
View File
@@ -0,0 +1,39 @@
use crate::dictionary::app_data_dir;
use rodio::Decoder;
use sha2::{Digest, Sha256};
use std::fs::File;
use std::io::BufReader;
use reqwest::Client;
pub async fn get_voice(text: &str) -> BufReader<File> {
let mut path = app_data_dir();
path.push("voice");
if !path.exists() {
std::fs::create_dir(path.clone()).unwrap();
}
let hash = format!("{}.wav", hex::encode(Sha256::digest(text.as_bytes())));
path.push(hash);
if !path.exists() {
let engine_url = "http://127.0.0.1:50021";
let client = Client::new();
let query = client
.post(format!("{engine_url}/audio_query?text={text}&speaker=11"))
.send()
.await;
let query = query.unwrap().text().await.unwrap();
// Synthesis
let audio = client
.post(format!("{engine_url}/synthesis?speaker=11"))
.header("Content-Type", "application/json")
.body(query)
.send()
.await.unwrap()
.bytes()
.await.unwrap();
tokio::fs::write(&path, &audio).await.unwrap();
}
BufReader::new(File::open(path).unwrap())
}
+42
View File
@@ -1,3 +1,4 @@
use crate::data_provider::voice::get_voice;
use crate::lang::{CardSet, DictionaryElement, WordOpenMode};
use crate::repetitions::CardSetSettings;
use crate::Page::PreviousPage;
@@ -6,7 +7,9 @@ use iced::alignment::Horizontal::Center;
use iced::keyboard::key::Physical::Code;
use iced::widget::{button, column, container, row, rule, space, text};
use iced::{alignment, keyboard, Element, Fill, Left, Task};
use rodio::MixerDeviceSink;
use std::sync::{Arc, Mutex};
use tokio::task::spawn_blocking;
pub struct RepetitionState {
pub settings: CardSetSettings,
@@ -14,6 +17,8 @@ pub struct RepetitionState {
pub state: Arc<Mutex<AppState>>,
current_word: DictionaryElement,
open: bool,
can_play: bool,
sink: Arc<MixerDeviceSink>,
}
impl NavigatedPage<RepetitionMessage> for RepetitionState {
@@ -30,12 +35,16 @@ impl RepetitionState {
pub(crate) fn new(set: CardSetSettings, state: Arc<Mutex<AppState>>) -> RepetitionState {
let mut card_set = CardSet::new(&set, state.clone());
let word = card_set.next();
let sink_handle = rodio::DeviceSinkBuilder::open_default_sink().unwrap();
RepetitionState {
settings: set,
set: card_set,
state,
current_word: word,
open: false,
can_play: true,
sink: Arc::new(sink_handle)
}
}
}
@@ -46,11 +55,26 @@ impl RepetitionState {
RepetitionMessage::Back => {}
RepetitionMessage::Next => self.next(),
RepetitionMessage::Answer(m) => self.answer(m),
RepetitionMessage::Play => {
if !self.can_play {
return Task::none()
}
self.can_play = false;
let value = self.current_word.key.clone();
return Task::perform(play_sound(self.sink.clone(), value), |_| RootMessage::Repetition(RepetitionMessage::PlayFinished));
},
RepetitionMessage::PlayFinished => {
self.can_play = true;
}
}
Task::none()
}
fn next(&mut self) {
if self.open {
self.set.open(WordOpenMode::None);
@@ -109,6 +133,7 @@ impl RepetitionState {
match self.settings.forward.as_str() {
"key" => self.draw_key(word),
"value" => self.draw_value(word),
"speech" => self.draw_voice(word),
_ => space().into(),
}
}
@@ -122,6 +147,7 @@ impl RepetitionState {
match self.settings.backward.as_str() {
"key" => self.draw_key(word),
"value" => self.draw_value(word),
"speech" => self.draw_voice(word),
_ => space().into(),
}
}
@@ -147,6 +173,12 @@ impl RepetitionState {
fn draw_value(&self, word: &DictionaryElement) -> Element<'_, RepetitionMessage> {
text!("{}", word.value).size(24).into()
}
fn draw_voice(&self, word: &DictionaryElement) -> Element<'_, RepetitionMessage> {
button("Воспроизвести")
.on_press(RepetitionMessage::Play)
.into()
}
}
impl KeyPressedPage for RepetitionState {
@@ -180,4 +212,14 @@ pub enum RepetitionMessage {
Next,
Back,
Answer(WordOpenMode),
Play,
PlayFinished,
}
async fn play_sound(sink: Arc<MixerDeviceSink>, text: String) {
let data = get_voice(text.as_str()).await;
spawn_blocking(move || {
rodio::play(&sink.mixer(), data).unwrap().sleep_until_end();
}).await.unwrap();
}