use audio resampler on the write_to_disk example (#68)
This commit is contained in:
@@ -2,6 +2,7 @@ use bytes::{BufMut, BytesMut};
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use livekit::prelude::*;
|
use livekit::prelude::*;
|
||||||
use livekit::webrtc::audio_stream::native::NativeAudioStream;
|
use livekit::webrtc::audio_stream::native::NativeAudioStream;
|
||||||
|
use livekit::webrtc::native::audio_resampler;
|
||||||
use std::env;
|
use std::env;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::{AsyncWriteExt, BufWriter};
|
use tokio::io::{AsyncWriteExt, BufWriter};
|
||||||
@@ -13,7 +14,7 @@ const FILE_PATH: &str = "record.wav";
|
|||||||
pub struct WavHeader {
|
pub struct WavHeader {
|
||||||
pub sample_rate: u32,
|
pub sample_rate: u32,
|
||||||
pub bit_depth: u16,
|
pub bit_depth: u16,
|
||||||
pub num_channels: u16,
|
pub num_channels: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WavWriter {
|
pub struct WavWriter {
|
||||||
@@ -41,9 +42,9 @@ impl WavWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write_header(&mut self) -> Result<(), std::io::Error> {
|
fn write_header(&mut self) -> Result<(), std::io::Error> {
|
||||||
let byte_rate = (self.header.sample_rate
|
let byte_rate = self.header.sample_rate
|
||||||
* self.header.bit_depth as u32
|
* self.header.bit_depth as u32
|
||||||
* self.header.num_channels as u32);
|
* self.header.num_channels as u32;
|
||||||
|
|
||||||
let block_align = byte_rate as u16 / self.header.sample_rate as u16;
|
let block_align = byte_rate as u16 / self.header.sample_rate as u16;
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ impl WavWriter {
|
|||||||
self.data.put_slice(b"fmt ");
|
self.data.put_slice(b"fmt ");
|
||||||
self.data.put_u32_le(16); // Subchunk1Size (16 for PCM)
|
self.data.put_u32_le(16); // Subchunk1Size (16 for PCM)
|
||||||
self.data.put_u16_le(1); // AudioFormat (1 for PCM)
|
self.data.put_u16_le(1); // AudioFormat (1 for PCM)
|
||||||
self.data.put_u16_le(self.header.num_channels);
|
self.data.put_u16_le(self.header.num_channels as u16);
|
||||||
self.data.put_u32_le(self.header.sample_rate);
|
self.data.put_u32_le(self.header.sample_rate);
|
||||||
self.data.put_u32_le(byte_rate);
|
self.data.put_u32_le(byte_rate);
|
||||||
self.data.put_u16_le(block_align);
|
self.data.put_u16_le(block_align);
|
||||||
@@ -115,21 +116,30 @@ async fn record_track(audio_track: RemoteAudioTrack) -> Result<(), std::io::Erro
|
|||||||
println!("Recording track {:?}", audio_track.sid());
|
println!("Recording track {:?}", audio_track.sid());
|
||||||
let rtc_track = audio_track.rtc_track();
|
let rtc_track = audio_track.rtc_track();
|
||||||
|
|
||||||
// TODO(theomonnom): Remove hardcoded values
|
|
||||||
let header = WavHeader {
|
let header = WavHeader {
|
||||||
sample_rate: 48000,
|
sample_rate: 48000,
|
||||||
bit_depth: 16,
|
bit_depth: 16,
|
||||||
num_channels: 1,
|
num_channels: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut resampler = audio_resampler::AudioResampler::default();
|
||||||
let mut wav_writer = WavWriter::create(FILE_PATH, header).await?;
|
let mut wav_writer = WavWriter::create(FILE_PATH, header).await?;
|
||||||
let mut audio_stream = NativeAudioStream::new(rtc_track);
|
let mut audio_stream = NativeAudioStream::new(rtc_track);
|
||||||
|
|
||||||
let max_record = 5 * header.sample_rate * header.num_channels as u32;
|
let max_record = 5 * header.sample_rate * header.num_channels as u32;
|
||||||
let mut sample_count = 0;
|
let mut sample_count = 0;
|
||||||
'recv_loop: while let Some(frame) = audio_stream.next().await {
|
'recv_loop: while let Some(frame) = audio_stream.next().await {
|
||||||
for sample in frame.data {
|
let data = resampler.remix_and_resample(
|
||||||
wav_writer.write_sample(sample).await.unwrap();
|
&frame.data,
|
||||||
|
frame.samples_per_channel,
|
||||||
|
frame.num_channels,
|
||||||
|
frame.sample_rate,
|
||||||
|
header.num_channels,
|
||||||
|
header.sample_rate,
|
||||||
|
);
|
||||||
|
|
||||||
|
for sample in data {
|
||||||
|
wav_writer.write_sample(*sample).await.unwrap();
|
||||||
sample_count += 1;
|
sample_count += 1;
|
||||||
|
|
||||||
if sample_count >= max_record {
|
if sample_count >= max_record {
|
||||||
|
|||||||
@@ -611,7 +611,7 @@ impl FfiServer {
|
|||||||
|
|
||||||
fn new_audio_resampler(
|
fn new_audio_resampler(
|
||||||
&'static self,
|
&'static self,
|
||||||
new_resampler: proto::NewAudioResamplerRequest,
|
_: proto::NewAudioResamplerRequest,
|
||||||
) -> FfiResult<proto::NewAudioResamplerResponse> {
|
) -> FfiResult<proto::NewAudioResamplerResponse> {
|
||||||
let resampler = audio_resampler::AudioResampler::default();
|
let resampler = audio_resampler::AudioResampler::default();
|
||||||
let resampler = Arc::new(Mutex::new(resampler));
|
let resampler = Arc::new(Mutex::new(resampler));
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ impl AudioResampler {
|
|||||||
dst_sample_rate as i32,
|
dst_sample_rate as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
std::slice::from_raw_parts(self.sys_handle.data(), len)
|
std::slice::from_raw_parts(self.sys_handle.data(), len / 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ size_t AudioResampler::remix_and_resample(const int16_t* src,
|
|||||||
webrtc::voe::RemixAndResample(src, samples_per_channel, num_channels,
|
webrtc::voe::RemixAndResample(src, samples_per_channel, num_channels,
|
||||||
sample_rate, &resampler_, &frame_);
|
sample_rate, &resampler_, &frame_);
|
||||||
|
|
||||||
return frame_.num_channels() * frame_.samples_per_channel();
|
return frame_.num_channels() * frame_.samples_per_channel() * sizeof(int16_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int16_t* AudioResampler::data() const {
|
const int16_t* AudioResampler::data() const {
|
||||||
|
|||||||
Reference in New Issue
Block a user