Add matching host platform, max seconds recording, template Makefile

This commit is contained in:
Satu Koskinen 2022-09-25 22:41:09 +03:00
parent aba5279be9
commit 36dea6c9e2
8 changed files with 93 additions and 16 deletions

17
Makefile Normal file
View File

@ -0,0 +1,17 @@
configure:
build:
run-audio-recorder:
run-gps-recorder:
run-depth-recorder:

View File

@ -1,8 +1,7 @@
all: all:
cargo build --release cargo build --release
test: test: all
cargo build --release
mkdir -p recordings mkdir -p recordings
bash run_test.sh bash run_test.sh

View File

@ -4,6 +4,8 @@ use clap::{Parser, ValueEnum, Subcommand};
pub enum Hosts { pub enum Hosts {
Alsa, Alsa,
Jack, Jack,
CoreAudio,
Asio,
} }
#[derive(Subcommand)] #[derive(Subcommand)]
@ -52,6 +54,10 @@ pub struct Rec {
#[clap(short, long, value_name = "SECONDS")] #[clap(short, long, value_name = "SECONDS")]
pub batch_recording: Option<u64>, pub batch_recording: Option<u64>,
/// (optional) Will record for a total of [SECONDS]
#[clap(short, long, value_name = "MAX_SECONDS")]
pub max_seconds: Option<u64>,
/// Host API to use /// Host API to use
#[clap(value_enum)] #[clap(value_enum)]
pub host: Hosts, pub host: Hosts,

View File

@ -97,7 +97,7 @@ pub fn get_wav_spec(default_config: &SupportedStreamConfig, user_config: &Stream
pub fn get_date_time_string() -> String { pub fn get_date_time_string() -> String {
let now: DateTime<Local> = Local::now(); let now: DateTime<Local> = Local::now();
format!( format!(
"{}-{}-{}_{}:{}:{}", "{}-{}-{}_{}:{}:{:02}",
now.year(), now.month(), now.day(), now.year(), now.month(), now.day(),
now.hour(), now.minute(), now.second(), now.hour(), now.minute(), now.second(),
) )

View File

@ -19,10 +19,11 @@ type BatchInterrupt= Arc<AtomicBool>;
pub struct InterruptHandles { pub struct InterruptHandles {
batch_interrupt: BatchInterrupt, batch_interrupt: BatchInterrupt,
stream_interrupt: StreamInterrupt, stream_interrupt: StreamInterrupt,
max_seconds: Option<u64>,
} }
impl InterruptHandles { impl InterruptHandles {
pub fn new() -> Result<Self, anyhow::Error> { pub fn new(max_seconds: Option<u64>) -> Result<Self, anyhow::Error> {
let stream_interrupt = Arc::new((Mutex::new(false), Condvar::new())); let stream_interrupt = Arc::new((Mutex::new(false), Condvar::new()));
let stream_interrupt_cloned = stream_interrupt.clone(); let stream_interrupt_cloned = stream_interrupt.clone();
@ -42,13 +43,23 @@ impl InterruptHandles {
Ok(Self { Ok(Self {
batch_interrupt, batch_interrupt,
stream_interrupt, stream_interrupt,
max_seconds,
}) })
} }
pub fn stream_wait(&self) { pub fn stream_wait(&self) {
let &(ref lock, ref cvar) = &*self.stream_interrupt; let &(ref lock, ref cvar) = &*self.stream_interrupt;
let mut started = lock.lock().unwrap(); let mut started = lock.lock().unwrap();
let now = std::time::Instant::now();
while !*started { while !*started {
match self.max_seconds {
Some(secs) => {
if now.elapsed().as_secs() >= secs {
break;
}
}
None => (),
}
started = cvar.wait(started).unwrap(); started = cvar.wait(started).unwrap();
} }
} }

View File

@ -16,8 +16,8 @@ use constants::*;
use getters::*; use getters::*;
use input_handling::*; use input_handling::*;
use play::*; use play::*;
use anyhow::{Error, Result, anyhow};
use anyhow::{Result, Error};
use clap::Parser; use clap::Parser;
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {

View File

@ -27,6 +27,7 @@ pub struct Recorder {
name: String, name: String,
path: PathBuf, path: PathBuf,
current_file: String, current_file: String,
max_seconds: Option<u64>,
} }
/// # Recorder /// # Recorder
@ -50,10 +51,11 @@ impl Recorder {
sample_rate: u32, sample_rate: u32,
channels: u16, channels: u16,
buffer_size: u32, buffer_size: u32,
max_seconds: Option<u64>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
// Create interrupt handles to be used by the stream or batch loop. // Create interrupt handles to be used by the stream or batch loop.
let interrupt_handles = InterruptHandles::new()?; let interrupt_handles = InterruptHandles::new(max_seconds)?;
// Select requested host. // Select requested host.
let host = get_host(host)?; let host = get_host(host)?;
@ -80,13 +82,14 @@ impl Recorder {
name, name,
path, path,
current_file: "".to_string(), current_file: "".to_string(),
max_seconds,
}) })
} }
fn init_writer(&mut self) -> Result<(), Error> { fn init_writer(&mut self) -> Result<(), Error> {
let filename = get_filename(&self.name, &self.path); let filename = get_filename(&self.name, &self.path);
self.current_file = filename.clone(); self.current_file = filename.clone();
*self.writer.lock().unwrap() = Some(hound::WavWriter::create(filename, self.spec)?); self.writer = Arc::new(Mutex::new(Some(hound::WavWriter::create(filename, self.spec)?)));
Ok(()) Ok(())
} }
@ -141,15 +144,19 @@ impl Recorder {
stream.play()?; stream.play()?;
println!("REC: {}", self.current_file); println!("REC: {}", self.current_file);
let now = std::time::Instant::now(); let now = std::time::Instant::now();
loop { while self.interrupt_handles.batch_is_running() {
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(1));
if now.elapsed().as_secs() >= secs { if now.elapsed().as_secs() >= secs {
break; break;
} }
} }
drop(stream); drop(stream);
self.writer.lock().unwrap().take().unwrap().finalize()?; let writer = self.writer.clone();
println!("STOP: {}", self.current_file); let current_file = self.current_file.clone();
std::thread::spawn(move || {
writer.lock().unwrap().take().unwrap().finalize().unwrap();
println!("STOP: {}", current_file);
});
Ok(()) Ok(())
} }
} }
@ -170,7 +177,16 @@ where
} }
fn batch_recording(rec: &mut Recorder, secs: u64) -> Result<(), Error> { fn batch_recording(rec: &mut Recorder, secs: u64) -> Result<(), Error> {
let now = std::time::Instant::now();
while rec.interrupt_handles.batch_is_running() { while rec.interrupt_handles.batch_is_running() {
match rec.max_seconds {
Some(max_secs) => {
if now.elapsed().as_secs() >= max_secs {
break;
}
}
None => {}
}
rec.record_secs(secs)?; rec.record_secs(secs)?;
} }
Ok(()) Ok(())
@ -181,6 +197,31 @@ fn continuous_recording(rec: &mut Recorder) -> Result<(), Error> {
Ok(()) Ok(())
} }
#[cfg(target_os = "linux")]
fn match_host_platform(host: Hosts) -> Result<cpal::HostId, Error> {
match host {
Hosts::Alsa => Ok(cpal::HostId::Alsa),
Hosts::Jack => Ok(cpal::HostId::Jack),
_ => Err(anyhow!("Host not supported on Linux.")),
}
}
#[cfg(target_os = "macos")]
fn match_host_platform(host: Hosts) -> Result<cpal::HostId, Error> {
match host {
Hosts::CoreAudio => Ok(cpal::HostId::CoreAudio),
_ => Err(anyhow!("Host not supported on macOS.")),
}
}
#[cfg(target_os = "windows")]
fn match_host_platform(host: Hosts) -> Result<cpal::HostId, Error> {
match host {
Hosts::Asio => Ok(cpal::HostId::Asio),
_ => Err(anyhow!("Host not supported on Windows.")),
}
}
pub fn record(args: &Rec) -> Result<(), Error> { pub fn record(args: &Rec) -> Result<(), Error> {
let mut recorder = Recorder::init( let mut recorder = Recorder::init(
args.name.clone(), args.name.clone(),
@ -188,13 +229,11 @@ pub fn record(args: &Rec) -> Result<(), Error> {
Some(path) => path, Some(path) => path,
None => Path::new("./").to_path_buf(), None => Path::new("./").to_path_buf(),
}, },
match args.host { match_host_platform(args.host)?,
Hosts::Alsa => cpal::HostId::Alsa,
Hosts::Jack => cpal::HostId::Jack,
},
args.sample_rate.unwrap_or(DEFAULT_SAMPLE_RATE), args.sample_rate.unwrap_or(DEFAULT_SAMPLE_RATE),
args.channels.unwrap_or(DEFAULT_CHANNEL_COUNT), args.channels.unwrap_or(DEFAULT_CHANNEL_COUNT),
args.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE), args.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE),
args.max_seconds,
)?; )?;
match args.batch_recording { match args.batch_recording {

View File

@ -6,4 +6,9 @@ https://www.circuito.io/app?components=9443,200000,267055
# Setting up Jack on Raspberry Pi # Setting up Jack on Raspberry Pi
https://wiki.linuxaudio.org/wiki/raspberrypi https://wiki.linuxaudio.org/wiki/raspberrypi
https://github.com/supercollider/supercollider/blob/develop/README_RASPBERRY_PI.md
https://madskjeldgaard.dk/posts/raspi4-notes/
# Setting up GPS