src: drop a lot of unwraps()
This commit is contained in:
@@ -12,7 +12,6 @@ pub struct ArtifactRef<'a> {
|
|||||||
archive: &'a Archive
|
archive: &'a Archive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct ArtifactRefMut<'a> {
|
pub struct ArtifactRefMut<'a> {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
archive: &'a mut Archive
|
archive: &'a mut Archive
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub struct BandcampSource;
|
|||||||
|
|
||||||
impl DataSource for BandcampSource {
|
impl DataSource for BandcampSource {
|
||||||
type Args = BandcampQueryArgs;
|
type Args = BandcampQueryArgs;
|
||||||
type Error = ();
|
type Error = bandcamp::Error;
|
||||||
|
|
||||||
async fn synchronize(&self, _artifact: &mut Artifact) -> Result<Vec<Artifact>, Self::Error> {
|
async fn synchronize(&self, _artifact: &mut Artifact) -> Result<Vec<Artifact>, Self::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
@@ -47,11 +47,11 @@ impl DataSource for BandcampSource {
|
|||||||
match result {
|
match result {
|
||||||
SearchResultItem::Artist(data) => {
|
SearchResultItem::Artist(data) => {
|
||||||
// TODO: The artist and album detailed fetchers should also be separate args
|
// TODO: The artist and album detailed fetchers should also be separate args
|
||||||
let result = bandcamp::fetch_artist(data.artist_id).await.unwrap().into();
|
let result = bandcamp::fetch_artist(data.artist_id).await?.into();
|
||||||
json_results.push(result);
|
json_results.push(result);
|
||||||
},
|
},
|
||||||
SearchResultItem::Album(data) => {
|
SearchResultItem::Album(data) => {
|
||||||
let result = bandcamp::fetch_album(data.band_id, data.album_id).await.unwrap().into();
|
let result = bandcamp::fetch_album(data.band_id, data.album_id).await?.into();
|
||||||
json_results.push(result);
|
json_results.push(result);
|
||||||
},
|
},
|
||||||
SearchResultItem::Track(data) => {
|
SearchResultItem::Track(data) => {
|
||||||
|
|||||||
+28
-24
@@ -56,10 +56,30 @@ impl Into<Artifact> for BeetsTrack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub enum BeetsError {
|
||||||
|
Command(std::io::Error),
|
||||||
|
Json(serde_json::Error),
|
||||||
|
EmptyQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for BeetsError {
|
||||||
|
fn from(value: std::io::Error) -> Self {
|
||||||
|
Self::Command(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Error> for BeetsError {
|
||||||
|
fn from(value: serde_json::Error) -> Self {
|
||||||
|
Self::Json(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct BeetsDB;
|
pub struct BeetsDB;
|
||||||
|
|
||||||
impl BeetsDB {
|
impl BeetsDB {
|
||||||
async fn query_multi(&self, args: &BeatsQueryMultiArgs) -> Result<Vec<Artifact>, ()> {
|
async fn query_multi(&self, args: &BeatsQueryMultiArgs) -> Result<Vec<Artifact>, BeetsError> {
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
for arg in &args.args {
|
for arg in &args.args {
|
||||||
for artifact in self.query_single(arg).await.unwrap_or_default() {
|
for artifact in self.query_single(arg).await.unwrap_or_default() {
|
||||||
@@ -69,7 +89,7 @@ impl BeetsDB {
|
|||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn query_single(&self, args: &BeatsQueryArgs) -> Result<Vec<Artifact>, ()> {
|
async fn query_single(&self, args: &BeatsQueryArgs) -> Result<Vec<Artifact>, BeetsError> {
|
||||||
let mut beets_cmd = Command::new("beet");
|
let mut beets_cmd = Command::new("beet");
|
||||||
beets_cmd.args(["export", "-f", "json", "-i", "title,label,year,genres,album,artist,mb_trackid"]);
|
beets_cmd.args(["export", "-f", "json", "-i", "title,label,year,genres,album,artist,mb_trackid"]);
|
||||||
let mut valid = false;
|
let mut valid = false;
|
||||||
@@ -100,29 +120,20 @@ impl BeetsDB {
|
|||||||
|
|
||||||
if !valid {
|
if !valid {
|
||||||
log::warn!("Tried to execute an empty beets query");
|
log::warn!("Tried to execute an empty beets query");
|
||||||
return Err(())
|
return Err(BeetsError::EmptyQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug!("Executing beets: {:?}", beets_cmd);
|
log::debug!("Executing beets: {:?}", beets_cmd);
|
||||||
if let Ok(output) = beets_cmd.stdout(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap().wait_with_output().await {
|
let output = beets_cmd.stdout(Stdio::piped()).stderr(Stdio::null()).spawn()?.wait_with_output().await?;
|
||||||
match serde_json::from_str::<Vec<BeetsTrack>>(str::from_utf8(&output.stdout).unwrap()) {
|
let track = serde_json::from_str::<Vec<BeetsTrack>>(str::from_utf8(&output.stdout).unwrap())?;
|
||||||
Ok(track) => Ok(track.into_iter().map(|t| { t.into()}).collect()),
|
Ok(track.into_iter().map(|t| { t.into()}).collect())
|
||||||
Err(err) => {
|
|
||||||
log::error!("Failed to decode beets json: {:?}", err);
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::error!("Unable to execute query!");
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataSource for BeetsDB {
|
impl DataSource for BeetsDB {
|
||||||
type Args = BeatsQueryMultiArgs;
|
type Args = BeatsQueryMultiArgs;
|
||||||
|
|
||||||
type Error = ();
|
type Error = BeetsError;
|
||||||
|
|
||||||
async fn synchronize(&self, artifact: &mut Artifact) -> Result<Vec<Artifact>, Self::Error> {
|
async fn synchronize(&self, artifact: &mut Artifact) -> Result<Vec<Artifact>, Self::Error> {
|
||||||
match artifact.contents {
|
match artifact.contents {
|
||||||
@@ -134,7 +145,7 @@ impl DataSource for BeetsDB {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let results = self.query(&BeatsQueryMultiArgs { args: vec![args] }).await.unwrap();
|
let results = self.query(&BeatsQueryMultiArgs { args: vec![args] }).await?;
|
||||||
|
|
||||||
if let Some(first) = results.first() {
|
if let Some(first) = results.first() {
|
||||||
artifact.merge(first.clone());
|
artifact.merge(first.clone());
|
||||||
@@ -150,13 +161,6 @@ impl DataSource for BeetsDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn query(&self, args: &Self::Args) -> impl Future<Output = Result<Vec<Artifact>, Self::Error>> {
|
fn query(&self, args: &Self::Args) -> impl Future<Output = Result<Vec<Artifact>, Self::Error>> {
|
||||||
/*let mut ret = vec![];
|
|
||||||
for arg in args.0 {
|
|
||||||
for artifact in self.query_single(&arg).await.unwrap_or_default() {
|
|
||||||
ret.push(artifact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
futures::ready!(Ok(ret))*/
|
|
||||||
self.query_multi(args)
|
self.query_multi(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+98
-46
@@ -5,6 +5,41 @@ use oximedia_metering::vu_meter::VuMeter;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::*;
|
use tokio::sync::*;
|
||||||
|
|
||||||
|
use crate::events::AudioRecordRequest;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub enum AudioError {
|
||||||
|
Jack(jack::Error),
|
||||||
|
AudioBufferSend(mpsc::error::SendError<Vec<f32>>),
|
||||||
|
AudioBufferRecv(mpsc::error::TryRecvError),
|
||||||
|
AudioRequestSend(watch::error::SendError<AudioRecordRequest>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<jack::Error> for AudioError {
|
||||||
|
fn from(value: jack::Error) -> Self {
|
||||||
|
Self::Jack(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mpsc::error::SendError<Vec<f32>>> for AudioError {
|
||||||
|
fn from(value: mpsc::error::SendError<Vec<f32>>) -> Self {
|
||||||
|
Self::AudioBufferSend(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mpsc::error::TryRecvError> for AudioError {
|
||||||
|
fn from(value: mpsc::error::TryRecvError) -> Self {
|
||||||
|
Self::AudioBufferRecv(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<watch::error::SendError<AudioRecordRequest>> for AudioError {
|
||||||
|
fn from(value: watch::error::SendError<AudioRecordRequest>) -> Self {
|
||||||
|
Self::AudioRequestSend(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct JackClientRef {
|
pub struct JackClientRef {
|
||||||
killswitch: Option<oneshot::Sender<()>>
|
killswitch: Option<oneshot::Sender<()>>
|
||||||
@@ -12,7 +47,7 @@ pub struct JackClientRef {
|
|||||||
|
|
||||||
impl Drop for JackClientRef {
|
impl Drop for JackClientRef {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.killswitch.take().unwrap().send(()).unwrap();
|
self.killswitch.take().expect("Killswitch was already dropped!").send(()).expect("Cannot fire Jack killswitch");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,9 +58,9 @@ pub struct AudioInputControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AudioInputControl {
|
impl AudioInputControl {
|
||||||
pub async fn next(&mut self) -> f64 {
|
pub async fn next(&mut self) -> Result<f64, watch::error::RecvError> {
|
||||||
self.volume_src.changed().await.unwrap();
|
self.volume_src.changed().await?;
|
||||||
*self.volume_src.borrow_and_update()
|
Ok(*self.volume_src.borrow_and_update())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,28 +111,28 @@ struct AudioSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AudioSource {
|
impl AudioSource {
|
||||||
fn new(client: &jack::Client, name: &str) -> (Self, AudioInStream) {
|
fn new(client: &jack::Client, name: &str) -> Result<(Self, AudioInStream), AudioError> {
|
||||||
let (sample_sink, receiver) = mpsc::channel(32);
|
let (sample_sink, receiver) = mpsc::channel(32);
|
||||||
let port = client.register_port(name, AudioIn::default()).unwrap();
|
let port = client.register_port(name, AudioIn::default())?;
|
||||||
(AudioSource {
|
Ok((AudioSource {
|
||||||
port,
|
port,
|
||||||
sample_sink,
|
sample_sink,
|
||||||
meter: VuMeter::new(client.sample_rate().into(), 1, None)
|
meter: VuMeter::new(client.sample_rate().into(), 1, None)
|
||||||
}, AudioInStream {
|
}, AudioInStream {
|
||||||
sample_rate: client.sample_rate(),
|
sample_rate: client.sample_rate(),
|
||||||
src: receiver
|
src: receiver
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(&mut self, scope: &ProcessScope) -> Option<f64> {
|
fn process(&mut self, scope: &ProcessScope) -> Result<Option<f64>, AudioError> {
|
||||||
if self.port.connected_count().unwrap() > 0 {
|
if self.port.connected_count()? > 0 {
|
||||||
let buf: Vec<_> = self.port.as_slice(scope).iter().copied().collect();
|
let buf: Vec<_> = self.port.as_slice(scope).iter().copied().collect();
|
||||||
self.meter.process_interleaved(&buf);
|
self.meter.process_interleaved(&buf);
|
||||||
self.sample_sink.blocking_send(buf).unwrap();
|
self.sample_sink.blocking_send(buf)?;
|
||||||
|
|
||||||
self.meter.channel_vu(0)
|
Ok(self.meter.channel_vu(0))
|
||||||
} else {
|
} else {
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,25 +145,24 @@ struct AudioSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AudioSink {
|
impl AudioSink {
|
||||||
fn new(client: &jack::Client, name: &str) -> (Self, AudioOutStream) {
|
fn new(client: &jack::Client, name: &str) -> Result<(Self, AudioOutStream), AudioError> {
|
||||||
let (sender, sample_src) = mpsc::channel(32);
|
let (sender, sample_src) = mpsc::channel(32);
|
||||||
let port = client.register_port(name, AudioOut::default()).unwrap();
|
let port = client.register_port(name, AudioOut::default())?;
|
||||||
(AudioSink {
|
Ok((AudioSink {
|
||||||
output_buf: Vec::with_capacity(1024),
|
output_buf: Vec::with_capacity(1024),
|
||||||
port,
|
port,
|
||||||
sample_src
|
sample_src
|
||||||
}, AudioOutStream {
|
}, AudioOutStream {
|
||||||
sample_rate: client.sample_rate(),
|
sample_rate: client.sample_rate(),
|
||||||
sink: sender,
|
sink: sender,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(&mut self, scope: &ProcessScope) {
|
fn process(&mut self, scope: &ProcessScope) -> Result<(), AudioError> {
|
||||||
if let Ok(mut next_outbuf) = self.sample_src.try_recv() {
|
let mut next_outbuf = self.sample_src.try_recv()?;
|
||||||
self.output_buf.append(&mut next_outbuf);
|
self.output_buf.append(&mut next_outbuf);
|
||||||
}
|
|
||||||
|
|
||||||
if self.port.connected_count().unwrap() > 0 && !self.output_buf.is_empty() {
|
if self.port.connected_count()? > 0 && !self.output_buf.is_empty() {
|
||||||
let outbuf = self.port.as_mut_slice(scope);
|
let outbuf = self.port.as_mut_slice(scope);
|
||||||
let mut next_segment: Vec<f32> = self.output_buf.drain(0..(outbuf.len()).min(self.output_buf.len())).collect();
|
let mut next_segment: Vec<f32> = self.output_buf.drain(0..(outbuf.len()).min(self.output_buf.len())).collect();
|
||||||
let underrun = outbuf.len() - next_segment.len();
|
let underrun = outbuf.len() - next_segment.len();
|
||||||
@@ -140,6 +174,8 @@ impl AudioSink {
|
|||||||
|
|
||||||
outbuf.copy_from_slice(&next_segment);
|
outbuf.copy_from_slice(&next_segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +187,13 @@ struct AudioConfig {
|
|||||||
impl AudioConfig {
|
impl AudioConfig {
|
||||||
pub fn load() -> Self {
|
pub fn load() -> Self {
|
||||||
if let Ok(contents) = std::fs::read_to_string("audio.json") {
|
if let Ok(contents) = std::fs::read_to_string("audio.json") {
|
||||||
serde_json::from_str(contents.as_str()).unwrap()
|
match serde_json::from_str(contents.as_str()) {
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to load audio.json: {:?}", err);
|
||||||
|
Default::default()
|
||||||
|
},
|
||||||
|
Ok(ret) => ret
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
@@ -186,10 +228,7 @@ impl NotificationHandler for Notify {
|
|||||||
}).next();
|
}).next();
|
||||||
|
|
||||||
if let Some((role, Ok(target_port))) = port_match {
|
if let Some((role, Ok(target_port))) = port_match {
|
||||||
if !self.config.connections.contains_key(role) {
|
let cfg_slot = self.config.connections.entry(*role).or_insert_with(|| Default::default());
|
||||||
self.config.connections.insert(*role, Default::default());
|
|
||||||
}
|
|
||||||
let cfg_slot = self.config.connections.get_mut(role).unwrap();
|
|
||||||
|
|
||||||
if are_connected {
|
if are_connected {
|
||||||
log::info!("{} connected to {}", role, target_port);
|
log::info!("{} connected to {}", role, target_port);
|
||||||
@@ -200,7 +239,9 @@ impl NotificationHandler for Notify {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let save_data = serde_json::to_string_pretty(&self.config).unwrap();
|
let save_data = serde_json::to_string_pretty(&self.config).unwrap();
|
||||||
std::fs::write("audio.json", save_data).unwrap();
|
if let Err(err) = std::fs::write("audio.json", save_data) {
|
||||||
|
log::error!("Failed to write audio.json: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,11 +254,11 @@ pub async fn start_audio_input() -> (AudioInputControl, AudioInStream, AudioOutS
|
|||||||
|
|
||||||
let (volume_sink, volume_src) = watch::channel(0.);
|
let (volume_sink, volume_src) = watch::channel(0.);
|
||||||
|
|
||||||
let (client, _status) = jack::Client::new("Eva-Cohost", ClientOptions::default() | ClientOptions::SESSION_ID).unwrap();
|
let (client, _status) = jack::Client::new("Eva-Cohost", ClientOptions::default() | ClientOptions::SESSION_ID).expect("Could not create JACK client!");
|
||||||
|
|
||||||
let (mut tts_sink, tts_stream) = AudioSink::new(&client, "tts-out");
|
let (mut tts_sink, tts_stream) = AudioSink::new(&client, "tts-out").expect("Could not create TTS sink");
|
||||||
let (mut sfx_sink, sfx_stream) = AudioSink::new(&client, "sfx-out");
|
let (mut sfx_sink, sfx_stream) = AudioSink::new(&client, "sfx-out").expect("Could not create SFX sink");
|
||||||
let (mut mic_src, mic_stream) = AudioSource::new(&client, "microphone-in");
|
let (mut mic_src, mic_stream) = AudioSource::new(&client, "microphone-in").expect("Could not create microphone source");
|
||||||
|
|
||||||
let notifier = Notify {
|
let notifier = Notify {
|
||||||
config,
|
config,
|
||||||
@@ -248,30 +289,41 @@ pub async fn start_audio_input() -> (AudioInputControl, AudioInStream, AudioOutS
|
|||||||
}
|
}
|
||||||
|
|
||||||
let handler = jack::contrib::ClosureProcessHandler::new(move |_client, scope| {
|
let handler = jack::contrib::ClosureProcessHandler::new(move |_client, scope| {
|
||||||
if let Some(next_vu) = mic_src.process(scope) {
|
match mic_src.process(scope) {
|
||||||
volume_sink.send_if_modified(|v| {
|
Ok(Some(next_vu)) => {
|
||||||
let next_vu = (next_vu * 100.0).round() / 100.0;
|
volume_sink.send_if_modified(|v| {
|
||||||
if *v != next_vu {
|
let next_vu = (next_vu * 100.0).round() / 100.0;
|
||||||
*v = next_vu;
|
if *v != next_vu {
|
||||||
true
|
*v = next_vu;
|
||||||
} else {
|
true
|
||||||
false
|
} else {
|
||||||
}
|
false
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Ok(None) => (),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Error while processing mic source: {:?}", err);
|
||||||
|
return jack::Control::Quit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for sink in [&mut tts_sink, &mut sfx_sink] {
|
for sink in [&mut tts_sink, &mut sfx_sink] {
|
||||||
sink.process(scope);
|
if let Err(err) = sink.process(scope) {
|
||||||
|
log::error!("Error while processing {:?} audio sink: {:?}", sink, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
jack::Control::Continue
|
jack::Control::Continue
|
||||||
});
|
});
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let async_client = client.activate_async(notifier, handler).unwrap();
|
let async_client = client.activate_async(notifier, handler).expect("Unable to start jack client!");
|
||||||
|
|
||||||
exit_rx.await.unwrap();
|
if let Err(err) = exit_rx.await {
|
||||||
|
log::warn!("Premature killswitch triggered: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
async_client.deactivate().unwrap();
|
async_client.deactivate().expect("Unable to stop the jack client");
|
||||||
});
|
});
|
||||||
|
|
||||||
(AudioInputControl {
|
(AudioInputControl {
|
||||||
|
|||||||
+2
-2
@@ -61,9 +61,9 @@ pub struct SaveData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SaveData {
|
impl SaveData {
|
||||||
fn save(&self) {
|
fn save(&self) -> Result<(), std::io::Error> {
|
||||||
let save_data = serde_json::to_string_pretty(self).unwrap();
|
let save_data = serde_json::to_string_pretty(self).unwrap();
|
||||||
std::fs::write("save.json", save_data).unwrap();
|
std::fs::write("save.json", save_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use async_openai::{Client, config::OpenAIConfig, types::{InputSource, audio::{Au
|
|||||||
use tempfile::SpooledData;
|
use tempfile::SpooledData;
|
||||||
use tokio::sync::{mpsc, watch};
|
use tokio::sync::{mpsc, watch};
|
||||||
|
|
||||||
use crate::{audio::AudioInStream, events::AudioRecordRequest};
|
use crate::{audio::{AudioError, AudioInStream}, events::AudioRecordRequest};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TranscriptionControl {
|
pub struct TranscriptionControl {
|
||||||
@@ -13,16 +13,16 @@ pub struct TranscriptionControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TranscriptionControl {
|
impl TranscriptionControl {
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) -> Result<(), AudioError> {
|
||||||
self.record_state_sink.send(AudioRecordRequest::Start).unwrap();
|
Ok(self.record_state_sink.send(AudioRecordRequest::Start)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) -> Result<(), AudioError> {
|
||||||
self.record_state_sink.send(AudioRecordRequest::Finish).unwrap();
|
Ok(self.record_state_sink.send(AudioRecordRequest::Finish)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn next(&mut self) -> String {
|
pub async fn next(&mut self) -> Option<String> {
|
||||||
self.transcription_result_src.recv().await.unwrap()
|
self.transcription_result_src.recv().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ pub struct TtsControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TtsControl {
|
impl TtsControl {
|
||||||
pub async fn speak(&self, text: String) {
|
pub async fn speak(&self, text: String) -> Result<(), tokio::sync::mpsc::error::SendError<String>> {
|
||||||
self.request_sink.send(text).await.unwrap();
|
self.request_sink.send(text).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -299,10 +299,10 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
next_volume = self.audio.next() => {
|
next_volume = self.audio.next() => {
|
||||||
self.audio_level = next_volume
|
self.audio_level = next_volume.expect("Audio volume event stream has died")
|
||||||
},
|
},
|
||||||
transcription_result = self.transcription.next() => {
|
transcription_result = self.transcription.next() => {
|
||||||
self.predictions.insert(PredictionAction::ConversationAppend(ConversationEntry::Spoken(Speaker::User, transcription_result))).await;
|
self.predictions.insert(PredictionAction::ConversationAppend(ConversationEntry::Spoken(Speaker::User, transcription_result.expect("Transcription stream has died")))).await;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user