commit
63100dc2bb
6 changed files with 140 additions and 8 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,6 +5,7 @@ target/
|
||||||
.vscode/
|
.vscode/
|
||||||
dist/
|
dist/
|
||||||
certificates/
|
certificates/
|
||||||
|
audios/
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
|
|
@ -12,4 +12,6 @@ rand = "0.8.5"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.114"
|
serde_json = "1.0.114"
|
||||||
tokio = { version = "1.36.0", features = ["full"] }
|
tokio = { version = "1.36.0", features = ["full"] }
|
||||||
|
#tokio-stream = { version = "0.1.15", features = ["full"] }
|
||||||
|
tokio-util = { version = "0.7.10", features = ["full"] }
|
||||||
tower-http = { version = "0.5.2", features = ["full"] }
|
tower-http = { version = "0.5.2", features = ["full"] }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod routing;
|
pub mod routing;
|
||||||
pub mod read;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AppState{
|
pub struct AppState{
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::{AppState, ServerStatus, CoinStatus};
|
use crate::{AppState, ServerStatus, CoinStatus};
|
||||||
use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::get, Json, Router};
|
use axum::{body::Body, extract::State, http::StatusCode, response::IntoResponse, routing::get, Json, Router};
|
||||||
|
use tokio::fs::File;
|
||||||
|
use tokio_util::io::ReaderStream;
|
||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
|
||||||
|
@ -7,6 +9,7 @@ pub async fn routing(State(state): State<AppState>) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(alive))
|
.route("/", get(alive))
|
||||||
.route("/coin", get(flip_coin))
|
.route("/coin", get(flip_coin))
|
||||||
|
.route("/stream", get(stream))
|
||||||
.layer(CorsLayer::permissive())
|
.layer(CorsLayer::permissive())
|
||||||
.with_state(state.clone())
|
.with_state(state.clone())
|
||||||
}
|
}
|
||||||
|
@ -32,3 +35,9 @@ async fn flip_coin() -> impl IntoResponse {
|
||||||
println!("{}", coin_json);
|
println!("{}", coin_json);
|
||||||
(StatusCode::OK, Json(coin_json))
|
(StatusCode::OK, Json(coin_json))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn stream() -> impl IntoResponse {
|
||||||
|
let file = File::open("audios/audio.mp3").await.unwrap();
|
||||||
|
let stream = ReaderStream::new(file);
|
||||||
|
Body::from_stream(stream)
|
||||||
|
}
|
|
@ -6,9 +6,11 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-std = "1.12.0"
|
anyhow = "1.0.81"
|
||||||
|
cpal = { version = "0.15.3", features = ["wasm-bindgen"] }
|
||||||
dioxus = { version = "0.5.0-alpha.0", features = ["web"] }
|
dioxus = { version = "0.5.0-alpha.0", features = ["web"] }
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
reqwest = { version = "0.11.24", features = ["json"] }
|
reqwest = { version = "0.11.24", features = ["json"] }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
tokio_with_wasm = "0.4.3"
|
||||||
wasm-logger = "0.2.0"
|
wasm-logger = "0.2.0"
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use async_std::task;
|
use tokio_with_wasm::tokio;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
|
use cpal::{FromSample, Sample, SizedSample};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
const SERVER_ADDRESS: &str = "https://localhost:2323";
|
|
||||||
|
const SERVER_ADDRESS: &str = "https://tahinli.com.tr:2323";
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||||
enum Server{
|
enum Server{
|
||||||
|
@ -75,6 +78,14 @@ async fn coin_status_check() -> Result<CoinStatus, reqwest::Error> {
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
page_base {}
|
page_base {}
|
||||||
|
audio_stream_renderer {}
|
||||||
|
div {
|
||||||
|
button {
|
||||||
|
onclick: move |_| record(),
|
||||||
|
"style":"width: 80px; height: 50px;",
|
||||||
|
"Sinusoidal"
|
||||||
|
}
|
||||||
|
}
|
||||||
coin_status_renderer {}
|
coin_status_renderer {}
|
||||||
server_status_renderer {}
|
server_status_renderer {}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +106,101 @@ fn page_base() ->Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
|
||||||
|
where
|
||||||
|
T: SizedSample + FromSample<f32>,
|
||||||
|
{
|
||||||
|
let sample_rate = config.sample_rate.0 as f32;
|
||||||
|
let channels = config.channels as usize;
|
||||||
|
|
||||||
|
// Produce a sinusoid of maximum amplitude.
|
||||||
|
let mut sample_clock = 0f32;
|
||||||
|
let mut next_value = move || {
|
||||||
|
sample_clock = (sample_clock + 1.0) % sample_rate;
|
||||||
|
(sample_clock * 440.0 * 2.0 * std::f32::consts::PI / sample_rate).sin()
|
||||||
|
};
|
||||||
|
|
||||||
|
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
|
||||||
|
|
||||||
|
let stream = device.build_output_stream(
|
||||||
|
config,
|
||||||
|
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
|
||||||
|
write_data(data, channels, &mut next_value)
|
||||||
|
},
|
||||||
|
err_fn,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
stream.play()?;
|
||||||
|
|
||||||
|
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32)
|
||||||
|
where
|
||||||
|
T: Sample + FromSample<f32>,
|
||||||
|
{
|
||||||
|
for frame in output.chunks_mut(channels) {
|
||||||
|
let value: T = T::from_sample(next_sample());
|
||||||
|
for sample in frame.iter_mut() {
|
||||||
|
*sample = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn record() {
|
||||||
|
log::info!("mic");
|
||||||
|
let host = cpal::default_host();
|
||||||
|
let devices = host.devices().unwrap();
|
||||||
|
for (_derive_index, device) in devices.enumerate() {
|
||||||
|
log::info!("{:?}", device.name());
|
||||||
|
}
|
||||||
|
let device = host.default_output_device().unwrap();
|
||||||
|
|
||||||
|
let mut supported_config = device.supported_output_configs().unwrap();
|
||||||
|
let config = supported_config.next().unwrap().with_max_sample_rate();
|
||||||
|
log::info!("{:?}", config);
|
||||||
|
match config.sample_format() {
|
||||||
|
cpal::SampleFormat::I8 => {log::info!("i8")},
|
||||||
|
cpal::SampleFormat::I16 => {log::info!("i16")},
|
||||||
|
//cpal::SampleFormat::I24 => {log::info!("i24")},
|
||||||
|
cpal::SampleFormat::I32 => {log::info!("i32")},
|
||||||
|
//cpal::SampleFormat::I48 => {log::info!("i48")},
|
||||||
|
cpal::SampleFormat::I64 => {log::info!("i64")},
|
||||||
|
cpal::SampleFormat::U8 => {log::info!("u8")},
|
||||||
|
cpal::SampleFormat::U16 => {log::info!("u16")},
|
||||||
|
//cpal::SampleFormat::U24 => {log::info!("u24")},
|
||||||
|
cpal::SampleFormat::U32 => {log::info!("u32")},
|
||||||
|
//cpal::SampleFormat::U48 => {log::info!("u48")},
|
||||||
|
cpal::SampleFormat::U64 => {log::info!("u64")},
|
||||||
|
cpal::SampleFormat::F32 => {log::info!("f32");
|
||||||
|
run::<f32>(&device, &config.clone().into()).await.unwrap();},
|
||||||
|
cpal::SampleFormat::F64 => {log::info!("f64")},
|
||||||
|
sample_format => panic!("Unsupported sample format '{sample_format}'"),
|
||||||
|
}
|
||||||
|
/*let config:StreamConfig = config.into();
|
||||||
|
let stream = device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
||||||
|
|
||||||
|
log::info!("{:?}", data);
|
||||||
|
//I need to do something here, I think
|
||||||
|
},
|
||||||
|
move |_err| {
|
||||||
|
|
||||||
|
},
|
||||||
|
None).unwrap();
|
||||||
|
|
||||||
|
stream.play().unwrap();
|
||||||
|
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||||
|
stream.pause().unwrap();*/
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
fn server_status_renderer() -> Element {
|
fn server_status_renderer() -> Element {
|
||||||
let server_check_time = 1_u64;
|
let server_check_time = 1_u64;
|
||||||
let mut server_status = use_signal(move || ServerStatus{status:Server::Unstable,});
|
let mut server_status = use_signal(move || ServerStatus{status:Server::Unstable,});
|
||||||
|
@ -102,7 +208,7 @@ fn server_status_renderer() -> Element {
|
||||||
let mut server_status_unstable = use_signal(move|| false);
|
let mut server_status_unstable = use_signal(move|| false);
|
||||||
let _server_status_task:Coroutine<()> = use_coroutine(|_| async move {
|
let _server_status_task:Coroutine<()> = use_coroutine(|_| async move {
|
||||||
loop {
|
loop {
|
||||||
task::sleep(Duration::from_secs(server_check_time)).await;
|
tokio::time::sleep(Duration::from_secs(server_check_time)).await;
|
||||||
*server_status_watchdog.write() = true;
|
*server_status_watchdog.write() = true;
|
||||||
*server_status.write() = server_status_check(server_status).await;
|
*server_status.write() = server_status_check(server_status).await;
|
||||||
*server_status_watchdog.write() = false;
|
*server_status_watchdog.write() = false;
|
||||||
|
@ -111,13 +217,13 @@ fn server_status_renderer() -> Element {
|
||||||
let _server_status_watchdog_timer:Coroutine<()> = use_coroutine(|_| async move {
|
let _server_status_watchdog_timer:Coroutine<()> = use_coroutine(|_| async move {
|
||||||
let mut watchdog_counter = 0_i8;
|
let mut watchdog_counter = 0_i8;
|
||||||
loop {
|
loop {
|
||||||
task::sleep(Duration::from_secs(2*server_check_time+1)).await;
|
tokio::time::sleep(Duration::from_secs(2*server_check_time+1)).await;
|
||||||
if !server_status_watchdog() {
|
if !server_status_watchdog() {
|
||||||
*server_status_unstable.write() = false;
|
*server_status_unstable.write() = false;
|
||||||
}
|
}
|
||||||
if server_status_watchdog() {
|
if server_status_watchdog() {
|
||||||
for _i in 0..5 {
|
for _i in 0..5 {
|
||||||
task::sleep(Duration::from_secs(1)).await;
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
if server_status_watchdog() {
|
if server_status_watchdog() {
|
||||||
watchdog_counter += 1;
|
watchdog_counter += 1;
|
||||||
}
|
}
|
||||||
|
@ -179,6 +285,19 @@ fn coin_status_renderer() -> Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn audio_stream_renderer() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
audio{
|
||||||
|
src:"https://tahinli.com.tr:2323/stream",
|
||||||
|
controls:true,
|
||||||
|
autoplay: true,
|
||||||
|
muted:false,
|
||||||
|
r#loop:true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[component]
|
#[component]
|
||||||
fn ShowServerStatus(server_status: ServerStatus) -> Element {
|
fn ShowServerStatus(server_status: ServerStatus) -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue