feat: support reading recordings, address some warnings
This commit is contained in:
15
src/call.rs
15
src/call.rs
@@ -1,20 +1,17 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
OneToManyUniqueBTreeMap, UserDataManager,
|
OneToManyUniqueBTreeMap, UserDataManager,
|
||||||
option_ext::OptionExt as _,
|
option_ext::OptionExt as _,
|
||||||
recording_data::{Clip, Recording, RecordingDataManager},
|
recording_data::{Clip, Recording, RecordingData, RecordingDataManager},
|
||||||
user_capnp::user::Consent,
|
user_capnp::user::Consent,
|
||||||
user_data::RECORD_IF_CONSENT_UNSPECIFIED,
|
user_data::RECORD_IF_CONSENT_UNSPECIFIED,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use hound::{SampleFormat, WavSpec};
|
|
||||||
use opendal::Operator;
|
|
||||||
use songbird::{
|
use songbird::{
|
||||||
CoreEvent, Event, EventContext, EventHandler, Songbird,
|
CoreEvent, Event, EventContext, EventHandler, Songbird,
|
||||||
driver::{Channels, SampleRate},
|
driver::{Channels, SampleRate},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
io::Cursor,
|
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
@@ -137,11 +134,17 @@ impl EventHandler for Handler {
|
|||||||
tracing::info!("going to write the audio shortly");
|
tracing::info!("going to write the audio shortly");
|
||||||
|
|
||||||
let recording_data_manager = self.recording_data_manager.clone();
|
let recording_data_manager = self.recording_data_manager.clone();
|
||||||
let pcm = pcm.clone();
|
let samples = pcm.clone();
|
||||||
|
|
||||||
|
let recording_data = RecordingData {
|
||||||
|
channels,
|
||||||
|
sample_rate,
|
||||||
|
samples,
|
||||||
|
};
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
recording_data_manager
|
recording_data_manager
|
||||||
.write(&recording, &pcm, sample_rate, channels)
|
.write(&recording, recording_data)
|
||||||
.await
|
.await
|
||||||
.expect("TODO");
|
.expect("TODO");
|
||||||
tracing::info!(?recording, "successfully wrote the audio!");
|
tracing::info!(?recording, "successfully wrote the audio!");
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl RecordingDataManager {
|
|||||||
.and_then(|entry| {
|
.and_then(|entry| {
|
||||||
std::future::ready(
|
std::future::ready(
|
||||||
take(entry.name())
|
take(entry.name())
|
||||||
.map(|(day, rest)| day)
|
.map(|(day, _rest)| day)
|
||||||
.context(ParseSnafu),
|
.context(ParseSnafu),
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use futures::TryStream;
|
use std::{convert::Infallible, io::Cursor};
|
||||||
|
|
||||||
|
use hound::{SampleFormat, WavReader, WavSpec};
|
||||||
use opendal::Operator;
|
use opendal::Operator;
|
||||||
use snafu::Snafu;
|
use snafu::Snafu;
|
||||||
|
|
||||||
@@ -40,6 +42,87 @@ impl RecordingDataManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RecordingData {
|
||||||
|
pub channels: u16,
|
||||||
|
pub sample_rate: u32,
|
||||||
|
pub samples: Vec<i16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingDataManager {
|
||||||
|
pub async fn write(
|
||||||
|
&self,
|
||||||
|
recording: &Recording,
|
||||||
|
RecordingData {
|
||||||
|
channels,
|
||||||
|
sample_rate,
|
||||||
|
samples,
|
||||||
|
}: RecordingData,
|
||||||
|
) -> Result<
|
||||||
|
(),
|
||||||
|
Infallible, // TODO: a real error type
|
||||||
|
> {
|
||||||
|
let wav_spec = WavSpec {
|
||||||
|
channels,
|
||||||
|
sample_rate,
|
||||||
|
bits_per_sample: 16,
|
||||||
|
sample_format: SampleFormat::Int,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
let writer = Cursor::new(&mut buffer);
|
||||||
|
|
||||||
|
let mut wav_writer = hound::WavWriter::new(writer, wav_spec).expect("TODO");
|
||||||
|
|
||||||
|
let mut sample_writer = wav_writer.get_i16_writer(samples.len() as u32);
|
||||||
|
|
||||||
|
for sample in samples {
|
||||||
|
sample_writer.write_sample(sample);
|
||||||
|
}
|
||||||
|
sample_writer.flush().expect("TODO");
|
||||||
|
|
||||||
|
wav_writer.finalize().expect("TODO");
|
||||||
|
|
||||||
|
let path = recording.to_string();
|
||||||
|
|
||||||
|
self.operator.write(&path, buffer).await.expect("TODO");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordingDataManager {
|
||||||
|
pub async fn read(
|
||||||
|
&self,
|
||||||
|
recording: &Recording,
|
||||||
|
) -> Result<
|
||||||
|
RecordingData,
|
||||||
|
Infallible, // TODO: a real error type
|
||||||
|
> {
|
||||||
|
let path = recording.to_string();
|
||||||
|
|
||||||
|
let buffer = self.operator.read(&path).await.expect("TODO");
|
||||||
|
|
||||||
|
let bytes = buffer.to_bytes();
|
||||||
|
let cursor = Cursor::new(bytes);
|
||||||
|
|
||||||
|
let wav_reader = WavReader::new(cursor).expect("TODO");
|
||||||
|
|
||||||
|
let wav_spec = wav_reader.spec();
|
||||||
|
let channels = wav_spec.channels;
|
||||||
|
let sample_rate = wav_spec.sample_rate;
|
||||||
|
|
||||||
|
let samples = wav_reader.into_samples();
|
||||||
|
let samples = Result::from_iter(samples).expect("TODO");
|
||||||
|
|
||||||
|
Ok(RecordingData {
|
||||||
|
channels,
|
||||||
|
sample_rate,
|
||||||
|
samples,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
pub enum ListError {
|
pub enum ListError {
|
||||||
/// error creating a lister through the storage operator
|
/// error creating a lister through the storage operator
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ impl RecordingDataManager {
|
|||||||
.and_then(|entry| {
|
.and_then(|entry| {
|
||||||
std::future::ready(
|
std::future::ready(
|
||||||
take(entry.name())
|
take(entry.name())
|
||||||
.map(|(month, rest)| month)
|
.map(|(month, _rest)| month)
|
||||||
.context(ParseSnafu),
|
.context(ParseSnafu),
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,17 +1,7 @@
|
|||||||
use futures::{TryStream, TryStreamExt as _};
|
|
||||||
use hound::{SampleFormat, WavSpec};
|
|
||||||
use snafu::{ResultExt as _, Snafu};
|
use snafu::{ResultExt as _, Snafu};
|
||||||
use std::{convert::Infallible, fmt::Display, io::Cursor, str::FromStr};
|
use std::{fmt::Display, str::FromStr};
|
||||||
use time::UtcDateTime;
|
|
||||||
use twilight_model::id::{
|
|
||||||
Id,
|
|
||||||
marker::{ChannelMarker, GuildMarker},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{Clip, Day, Hour, Minute, Month, Year, clip, day, hour, minute, month, year};
|
||||||
Clip, Day, Hour, ListError, Minute, Month, RecordingDataManager, Year, clip, day, hour, minute,
|
|
||||||
month, year,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Recording {
|
pub struct Recording {
|
||||||
@@ -84,60 +74,3 @@ impl Display for Recording {
|
|||||||
write!(f, "{year}/{month}/{day}/{hour}/{minute}/{clip}")
|
write!(f, "{year}/{month}/{day}/{hour}/{minute}/{clip}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecordingDataManager {
|
|
||||||
pub async fn write(
|
|
||||||
&self,
|
|
||||||
recording: &Recording,
|
|
||||||
samples: &[i16],
|
|
||||||
sample_rate: u32,
|
|
||||||
channels: u16,
|
|
||||||
) -> Result<
|
|
||||||
(),
|
|
||||||
Infallible, // TODO: a real error type
|
|
||||||
> {
|
|
||||||
let wav_spec = WavSpec {
|
|
||||||
channels,
|
|
||||||
sample_rate,
|
|
||||||
bits_per_sample: 16,
|
|
||||||
sample_format: SampleFormat::Int,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
let writer = Cursor::new(&mut buffer);
|
|
||||||
|
|
||||||
let mut wav_writer = hound::WavWriter::new(writer, wav_spec).expect("TODO");
|
|
||||||
|
|
||||||
let mut sample_writer = wav_writer.get_i16_writer(samples.len() as u32);
|
|
||||||
|
|
||||||
for sample in samples {
|
|
||||||
sample_writer.write_sample(*sample);
|
|
||||||
}
|
|
||||||
sample_writer.flush().expect("TODO");
|
|
||||||
|
|
||||||
wav_writer.finalize().expect("TODO");
|
|
||||||
|
|
||||||
let path = recording.to_string();
|
|
||||||
|
|
||||||
self.operator.write(&path, buffer).await.expect("TODO");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecordingDataManager {
|
|
||||||
pub async fn read(&self, recording: &Recording, sample_rate: u32, channels: u16) -> Vec<i16> {
|
|
||||||
let path = recording.to_string();
|
|
||||||
|
|
||||||
let buffer = self.operator.read(&path).await.expect("TODO");
|
|
||||||
|
|
||||||
let wav_spec = WavSpec {
|
|
||||||
channels,
|
|
||||||
sample_rate,
|
|
||||||
bits_per_sample: 16,
|
|
||||||
sample_format: SampleFormat::Int,
|
|
||||||
};
|
|
||||||
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use snafu::{OptionExt as _, ResultExt as _, Snafu};
|
use snafu::{OptionExt as _, ResultExt as _, Snafu};
|
||||||
use std::num::ParseIntError;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use twilight_model::id::Id;
|
use twilight_model::id::Id;
|
||||||
use twilight_model::id::marker::ChannelMarker;
|
use twilight_model::id::marker::ChannelMarker;
|
||||||
|
|||||||
Reference in New Issue
Block a user