commit 22ae97b9b4c53545b7456d56386e0bccd00c513e
Author: Marcel <mtrnord1@gmail.com>
Date: Thu, 18 Jun 2020 22:16:18 +0200
Initial commit
Took 5 hours 32 minutes
Diffstat:
10 files changed, 303 insertions(+), 0 deletions(-)
diff --git a/.idea/.gitignore b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/misc.xml b/.idea/misc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectRootManager">
+ <output url="file://$PROJECT_DIR$/out" />
+ </component>
+</project>
+\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/ada.iml" filepath="$PROJECT_DIR$/ada.iml" />
+ </modules>
+ </component>
+</project>
+\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="" vcs="Git" />
+ </component>
+</project>
+\ No newline at end of file
diff --git a/ada.iml b/ada.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="RUST_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+\ No newline at end of file
diff --git a/src/analyzer_state.rs b/src/analyzer_state.rs
@@ -0,0 +1,41 @@
+use std::mem;
+use std::sync::{Arc, Mutex, Once};
+
+#[derive(Clone, Debug)]
+pub struct AnalyzerState {
+ running: Arc<Mutex<bool>>,
+}
+
+impl AnalyzerState {
+ pub(crate) fn singleton() -> Self {
+ // Initialize it to a null value
+ static mut ANALYZER_STATE_SINGLETON: *const AnalyzerState = 0 as *const AnalyzerState;
+ static ANALYZER_STATE_ONCE: Once = Once::new();
+
+ unsafe {
+ ANALYZER_STATE_ONCE.call_once(|| {
+ // Make it
+ let singleton = AnalyzerState {
+ running: Arc::new(Mutex::new(false)),
+ };
+
+ // Put it in the heap so it can outlive this call
+ ANALYZER_STATE_SINGLETON = mem::transmute(Box::new(singleton));
+ });
+
+ // Now we give out a copy of the data that is safe to use concurrently.
+ (*ANALYZER_STATE_SINGLETON).clone()
+ }
+ }
+
+ pub fn get(&self) -> bool {
+ let running = self.running.lock().unwrap();
+ let data = running.clone();
+ data
+ }
+
+ pub fn set(&mut self, new_value: bool) {
+ let mut state = self.running.lock().expect("Could not lock mutex");
+ mem::replace(&mut *state, new_value.clone());
+ }
+}
diff --git a/src/config.rs b/src/config.rs
@@ -0,0 +1,17 @@
+use crate::errors::ConfigError;
+use async_std::fs;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct Config {
+ pub model_path: String,
+ pub model_name: String,
+}
+
+impl Config {
+ pub async fn load(filepath: String) -> Result<Self, ConfigError> {
+ let contents = fs::read(filepath).await?;
+ let config: Self = serde_yaml::from_slice(&contents)?;
+ Ok(config)
+ }
+}
diff --git a/src/data.rs b/src/data.rs
@@ -0,0 +1,132 @@
+use std::mem;
+use std::pin::Pin;
+use std::sync::{Arc, Mutex, Once};
+
+use async_std::stream::Stream;
+use async_std::task::Context;
+use audrey::read::Reader;
+use audrey::sample::interpolate::{Converter, Linear};
+use audrey::sample::signal::{from_iter, Signal};
+use futures::task::Poll;
+
+use crate::start_time::StartTime;
+use crate::SAMPLE_RATE;
+use std::io::{Cursor, Seek, SeekFrom};
+
+#[derive(Clone, Debug)]
+pub struct Data {
+ buffer: Arc<Mutex<Vec<i16>>>,
+}
+
+fn packets_to_wav(data: Vec<i16>) -> Cursor<Vec<u8>> {
+ println!("Make WAV");
+ let wavspec = hound::WavSpec {
+ channels: 1,
+ sample_rate: 48_000,
+ bits_per_sample: 16,
+ sample_format: hound::SampleFormat::Int,
+ };
+
+ let mew_data = Vec::<u8>::new();
+ let mut cursor = Cursor::new(mew_data);
+
+ let mut writer = hound::WavWriter::new(&mut cursor, wavspec).unwrap();
+ let mut i16_writer = writer.get_i16_writer((data.clone().len()) as u32);
+
+ for sample in data.clone().into_iter().step_by(1) {
+ i16_writer.write_sample(sample);
+ }
+
+ i16_writer.flush().unwrap();
+ drop(writer);
+ cursor.seek(SeekFrom::Start(0)).unwrap();
+ println!("Return WAV");
+ cursor
+}
+
+/// Interpolate the wav to the sample rate used by the deepspeech model.
+fn interpolate<F>(wav: F) -> Vec<i16>
+where
+ F: std::io::Read,
+ F: std::io::Seek,
+{
+ println!("interpolate WAV");
+ let mut reader = Reader::new(wav).unwrap();
+ let description = reader.description();
+
+ let audio_buffer: Vec<_> = if description.sample_rate() == SAMPLE_RATE {
+ reader.samples().map(|s| s.unwrap()).collect()
+ } else {
+ let interpolator = Linear::new([0i16], [0]);
+ let conv = Converter::from_hz_to_hz(
+ from_iter(reader.samples::<i16>().map(|s| [s.unwrap()])),
+ interpolator,
+ description.sample_rate() as f64,
+ SAMPLE_RATE as f64,
+ );
+ conv.until_exhausted().map(|v| v[0]).collect()
+ };
+ println!("return interpolated WAV");
+ audio_buffer
+}
+
+impl Stream for Data {
+ type Item = Vec<i16>;
+
+ fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+ let buffer = self.buffer.lock().unwrap();
+ let data = buffer.clone();
+
+ let start_time_struct = StartTime::singleton();
+ let ready = start_time_struct.get();
+ if !data.is_empty() && ready {
+ let audio_buffer = packets_to_wav(data.clone());
+ let audio_buffer = interpolate(audio_buffer);
+ Poll::Ready(Some(audio_buffer))
+ } else {
+ // Hack to keep alive
+ Poll::Ready(Some(Vec::new()))
+ }
+ }
+}
+
+impl Data {
+ pub(crate) fn singleton() -> Data {
+ // Initialize it to a null value
+ static mut DATA_SINGLETON: *const Data = 0 as *const Data;
+ static DATA_ONCE: Once = Once::new();
+
+ unsafe {
+ DATA_ONCE.call_once(|| {
+ // Make it
+ let singleton = Data {
+ buffer: Arc::new(Mutex::new(Vec::new())),
+ };
+
+ // Put it in the heap so it can outlive this call
+ DATA_SINGLETON = mem::transmute(Box::new(singleton));
+ });
+
+ // Now we give out a copy of the data that is safe to use concurrently.
+ (*DATA_SINGLETON).clone()
+ }
+ }
+
+ pub fn reset(&mut self) {
+ let mut state = self.buffer.lock().expect("Could not lock mutex");
+ let data = Vec::new();
+ mem::replace(&mut *state, data.clone());
+ }
+
+ pub fn add(&mut self, buf: cpal::UnknownTypeInputBuffer) {
+ let mut state = self.buffer.lock().expect("Could not lock mutex");
+ match buf {
+ cpal::UnknownTypeInputBuffer::I16(buffer) => {
+ let mut local_state = state.clone();
+ local_state.extend_from_slice(&*buffer);
+ mem::replace(&mut *state, local_state.clone());
+ }
+ _ => panic!("Unexpected buffer type"),
+ }
+ }
+}
diff --git a/src/errors.rs b/src/errors.rs
@@ -0,0 +1,18 @@
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum ConfigError {
+ /// Represents all cases of `serde_yaml::Error`.
+ #[error(transparent)]
+ SerdeError(#[from] serde_yaml::Error),
+
+ /// Represents all other cases of `std::io::Error`.
+ #[error(transparent)]
+ IOError(#[from] std::io::Error),
+}
+
+#[derive(Error, Debug)]
+pub enum SpeechError {
+ #[error("Model couldn't be loaded")]
+ ModelNotFound,
+}
diff --git a/src/start_time.rs b/src/start_time.rs
@@ -0,0 +1,51 @@
+use std::mem;
+use std::sync::{Arc, Mutex, Once};
+use std::time::{Duration, Instant};
+
+#[derive(Clone, Debug)]
+pub struct StartTime {
+ start: Arc<Mutex<Option<Instant>>>,
+}
+
+impl StartTime {
+ pub(crate) fn singleton() -> StartTime {
+ // Initialize it to a null value
+ static mut START_TIME_SINGLETON: *const StartTime = 0 as *const StartTime;
+ static START_TIME_ONCE: Once = Once::new();
+
+ unsafe {
+ START_TIME_ONCE.call_once(|| {
+ // Make it
+ let singleton = StartTime {
+ start: Arc::new(Mutex::new(None)),
+ };
+
+ // Put it in the heap so it can outlive this call
+ START_TIME_SINGLETON = mem::transmute(Box::new(singleton));
+ });
+
+ // Now we give out a copy of the data that is safe to use concurrently.
+ (*START_TIME_SINGLETON).clone()
+ }
+ }
+
+ pub fn get(&self) -> bool {
+ let running = self.start.lock().unwrap();
+ let data = running.clone();
+ match data {
+ None => false,
+ Some(v) => {
+ if Instant::now().duration_since(v) >= Duration::from_secs(3) {
+ true
+ } else {
+ false
+ }
+ }
+ }
+ }
+
+ pub fn set(&mut self, time: Instant) {
+ let mut state = self.start.lock().expect("Could not lock mutex");
+ mem::replace(&mut *state, Some(time.clone()));
+ }
+}