191 lines
No EOL
6.1 KiB
Rust
191 lines
No EOL
6.1 KiB
Rust
/*
|
|
Abandoned temporary.
|
|
Problems with frame size.
|
|
*/
|
|
|
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
|
use cpal::{BufferSize, StreamConfig, SampleRate, Host, Device, Stream, SampleFormat};
|
|
use log::{info, warn, error};
|
|
|
|
use once_cell::sync::OnceCell;
|
|
use std::sync::Arc;
|
|
use arc_swap::ArcSwap;
|
|
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU32, Ordering};
|
|
|
|
use crate::tauri_commands::cpal_data_callback;
|
|
|
|
static HOST: OnceCell<Host> = OnceCell::new();
|
|
thread_local!(static RECORDER: OnceCell<ArcSwap<Stream>> = OnceCell::new());
|
|
static SELECTED_MICROPHONE_IDX: AtomicI32 = AtomicI32::new(0);
|
|
static FRAME_LENGTH: AtomicU32 = AtomicU32::new(0);
|
|
static IS_RECORDING: AtomicBool = AtomicBool::new(false);
|
|
|
|
pub fn init_microphone(device_index: i32, frame_length: u32) -> bool {
|
|
// init host & frame buffer for the callback
|
|
if HOST.get().is_none() {
|
|
HOST.set(cpal::default_host());
|
|
|
|
// FRAME_BUFFER.set(Mutex::new(vec![0; FRAME_LENGTH.load(Ordering::SeqCst) as usize]));
|
|
}
|
|
|
|
// init microphone
|
|
RECORDER.with(|recorder| {
|
|
match recorder.get().is_none() {
|
|
true => {
|
|
if let Some(device) = get_device(device_index as usize) {
|
|
// store
|
|
recorder.set(ArcSwap::from_pointee(create_stream(device, frame_length)));
|
|
|
|
// remember current configuration
|
|
SELECTED_MICROPHONE_IDX.store(device_index, Ordering::SeqCst);
|
|
FRAME_LENGTH.store(frame_length, Ordering::SeqCst);
|
|
|
|
// success
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
},
|
|
false => {
|
|
// check if re-initialization required (i.e. selecetd microphoneor frame-length was changed )
|
|
if SELECTED_MICROPHONE_IDX.load(Ordering::SeqCst) != device_index
|
|
||
|
|
FRAME_LENGTH.load(Ordering::SeqCst) != frame_length {
|
|
warn!("Selected microphone or frame length was changed, re-initializing ...");
|
|
// initialize again with new device index
|
|
if IS_RECORDING.load(Ordering::SeqCst) {
|
|
stop_recording();
|
|
}
|
|
|
|
// remember new configuration
|
|
SELECTED_MICROPHONE_IDX.store(device_index, Ordering::SeqCst);
|
|
FRAME_LENGTH.store(frame_length, Ordering::SeqCst);
|
|
|
|
if let Some(device) = get_device(device_index as usize) {
|
|
// store
|
|
recorder.get().unwrap().store(Arc::new(create_stream(device, frame_length)));
|
|
|
|
// success
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// success
|
|
true
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
fn create_stream(device: Device, frame_length: u32) -> Stream {
|
|
// get default input stream config
|
|
// let default_config = device.default_input_config().unwrap();
|
|
|
|
// create config for the stream
|
|
// let config: StreamConfig = StreamConfig {
|
|
// channels: default_config.channels(),
|
|
// sample_rate: SampleRate(16000),
|
|
// buffer_size: BufferSize::Fixed(frame_length)
|
|
// };
|
|
|
|
let config = device
|
|
.default_input_config()
|
|
.expect("Failed to load default input config");
|
|
|
|
let channels = config.channels();
|
|
|
|
let err_fn = move |err| {
|
|
eprintln!("an error occurred on stream: {}", err);
|
|
};
|
|
|
|
match config.sample_format() {
|
|
SampleFormat::F32 => device.build_input_stream(
|
|
&config.into(),
|
|
move |data: &[f32], info| {
|
|
cpal_data_callback(data, channels);
|
|
},
|
|
err_fn,
|
|
None
|
|
),
|
|
SampleFormat::U16 => device.build_input_stream(
|
|
&config.into(),
|
|
move |data: &[u16], info| {
|
|
cpal_data_callback(data, channels);
|
|
},
|
|
err_fn,
|
|
None
|
|
),
|
|
SampleFormat::I16 => device.build_input_stream(
|
|
&config.into(),
|
|
move |data: &[i16], info| {
|
|
cpal_data_callback(data, channels);
|
|
},
|
|
err_fn,
|
|
None
|
|
),
|
|
_ => todo!()
|
|
}.unwrap()
|
|
}
|
|
|
|
pub fn stereo_to_mono(input_data: &[i16]) -> Vec<i16> {
|
|
let mut result = Vec::with_capacity(input_data.len() / 2);
|
|
result.extend(
|
|
input_data
|
|
.chunks_exact(2)
|
|
.map(|chunk| chunk[0] / 2 + chunk[1] / 2),
|
|
);
|
|
|
|
result
|
|
}
|
|
|
|
fn get_device(device_index: usize) -> Option<Device> {
|
|
if let Some(device) = HOST.get().unwrap().input_devices().expect("Get devices error ...").nth(device_index) {
|
|
Some(device)
|
|
} else {
|
|
if let Some(default) = HOST.get().unwrap().default_input_device() {
|
|
Some(default)
|
|
} else {
|
|
error!("No default input device ...");
|
|
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn start_recording(device_index: i32, frame_length: u32) {
|
|
// ensure microphone is initialized
|
|
init_microphone(device_index, frame_length);
|
|
|
|
// start recording
|
|
RECORDER.with(|recorder| {
|
|
match recorder.get().unwrap().load().play() {
|
|
Err(msg) => {
|
|
error!("[CPAL] Audio stream PLAY error ... {:?}", msg);
|
|
},
|
|
_ => ()
|
|
};
|
|
|
|
IS_RECORDING.store(true, Ordering::SeqCst);
|
|
info!("START recording from microphone ...");
|
|
});
|
|
}
|
|
|
|
pub fn stop_recording() {
|
|
// ensure microphone is initialized
|
|
RECORDER.with(|recorder| {
|
|
if recorder.get().is_some() && IS_RECORDING.load(Ordering::SeqCst) {
|
|
// pause instead of stop
|
|
match recorder.get().unwrap().load().pause() {
|
|
Err(msg) => {
|
|
error!("[CPAL] Audio stream PAUSE error ... {:?}", msg);
|
|
},
|
|
_ => ()
|
|
};
|
|
|
|
IS_RECORDING.store(false, Ordering::SeqCst);
|
|
info!("STOP recording from microphone ...");
|
|
}
|
|
});
|
|
} |