From e81028d75137387ae23efcd45daa13a356e95d6a Mon Sep 17 00:00:00 2001 From: Julius Koskela Date: Sun, 25 Sep 2022 17:44:48 +0300 Subject: [PATCH] Implement merge and play, refactor --- audio-logger/Cargo.lock | 222 +++++++++++++++++++++++++++-- audio-logger/Cargo.toml | 3 +- audio-logger/Makefile | 9 +- audio-logger/src/cli.rs | 64 ++++++++- audio-logger/src/constants.rs | 7 + audio-logger/src/getters.rs | 39 +++-- audio-logger/src/input_handling.rs | 7 +- audio-logger/src/main.rs | 68 ++++----- audio-logger/src/merge.rs | 43 ++++++ audio-logger/src/play.rs | 14 ++ audio-logger/src/print_configs.rs | 14 ++ audio-logger/src/recorder.rs | 52 +++++-- 12 files changed, 450 insertions(+), 92 deletions(-) create mode 100644 audio-logger/src/constants.rs create mode 100644 audio-logger/src/merge.rs create mode 100644 audio-logger/src/play.rs diff --git a/audio-logger/Cargo.lock b/audio-logger/Cargo.lock index a7a1b2f..86808a2 100644 --- a/audio-logger/Cargo.lock +++ b/audio-logger/Cargo.lock @@ -51,20 +51,21 @@ dependencies = [ ] [[package]] -name = "audio-logger" +name = "audio" version = "0.1.0" dependencies = [ "alsa", "anyhow", "chrono", "clap", - "cpal", + "cpal 0.13.5", "ctrlc", "hound", "jack 0.9.2", "libc", "nix 0.23.1", "parking_lot 0.12.1", + "rodio", ] [[package]] @@ -104,6 +105,12 @@ version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.2.1" @@ -205,6 +212,12 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "claxon" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" + [[package]] name = "combine" version = "4.6.6" @@ -255,7 +268,7 @@ dependencies = [ "lazy_static", "libc", "mach", - "ndk", + "ndk 0.6.0", "ndk-glue", "nix 0.23.1", "oboe", @@ -266,6 +279,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "cpal" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d466b47cf0ea4100186a7c12d7d0166813dda7cf648553554c9c39c6324841b" +dependencies = [ + "alsa", + "core-foundation-sys", + "coreaudio-rs", + "jni", + "js-sys", + "libc", + "mach", + "ndk 0.7.0", + "ndk-context", + "nix 0.23.1", + "oboe", + "once_cell", + "parking_lot 0.12.1", + "stdweb", + "thiserror", + "web-sys", + "windows", +] + [[package]] name = "ctrlc" version = "3.2.3" @@ -276,6 +314,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + [[package]] name = "darling" version = "0.13.4" @@ -523,6 +567,17 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lewton" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" +dependencies = [ + "byteorder", + "ogg", + "tinyvec", +] + [[package]] name = "libc" version = "0.2.133" @@ -598,6 +653,26 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "minimp3" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985438f75febf74c392071a975a29641b420dd84431135a6e6db721de4b74372" +dependencies = [ + "minimp3-sys", + "slice-deque", + "thiserror", +] + +[[package]] +name = "minimp3-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e21c73734c69dc95696c9ed8926a2b393171d98b3f5f5935686a26a487ab9b90" +dependencies = [ + "cc", +] + [[package]] name = "ndk" version = "0.6.0" @@ -606,11 +681,25 @@ checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" dependencies = [ "bitflags", "jni-sys", - "ndk-sys", + "ndk-sys 0.3.0", "num_enum", "thiserror", ] +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys 0.4.0", + "num_enum", + "raw-window-handle", + "thiserror", +] + [[package]] name = "ndk-context" version = "0.1.1" @@ -626,10 +715,10 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk", + "ndk 0.6.0", "ndk-context", "ndk-macro", - "ndk-sys", + "ndk-sys 0.3.0", ] [[package]] @@ -654,6 +743,15 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" +dependencies = [ + "jni-sys", +] + [[package]] name = "nix" version = "0.23.1" @@ -747,7 +845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1" dependencies = [ "jni", - "ndk", + "ndk 0.6.0", "ndk-context", "num-derive", "num-traits", @@ -763,6 +861,15 @@ dependencies = [ "cc", ] +[[package]] +name = "ogg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" +dependencies = [ + "byteorder", +] + [[package]] name = "once_cell" version = "1.15.0" @@ -906,6 +1013,15 @@ dependencies = [ "proc-macro2 1.0.43", ] +[[package]] +name = "raw-window-handle" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" +dependencies = [ + "cty", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -930,6 +1046,19 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "rodio" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb10b653d5ec0e9411a2e7d46e2c7f4046fd87d35b9955bd73ba4108d69072b5" +dependencies = [ + "claxon", + "cpal 0.14.0", + "hound", + "lewton", + "minimp3", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -963,6 +1092,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "slice-deque" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ef6ee280cdefba6d2d0b4b78a84a1c1a3f3a4cec98c2d4231c8bc225de0f25" +dependencies = [ + "libc", + "mach", + "winapi", +] + [[package]] name = "smallvec" version = "1.9.0" @@ -1049,6 +1189,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "toml" version = "0.5.9" @@ -1188,17 +1343,30 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +dependencies = [ + "windows_aarch64_msvc 0.37.0", + "windows_i686_gnu 0.37.0", + "windows_i686_msvc 0.37.0", + "windows_x86_64_gnu 0.37.0", + "windows_x86_64_msvc 0.37.0", +] + [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", ] [[package]] @@ -1207,26 +1375,56 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" diff --git a/audio-logger/Cargo.toml b/audio-logger/Cargo.toml index b6d594e..147f3d9 100644 --- a/audio-logger/Cargo.toml +++ b/audio-logger/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "audio-logger" +name = "audio" version = "0.1.0" edition = "2021" @@ -10,6 +10,7 @@ anyhow = "1.0.61" hound = "3.4.0" chrono = "0.4.22" ctrlc = "3.2.3" +rodio = "0.16.0" [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))'.dependencies] alsa = "0.6" diff --git a/audio-logger/Makefile b/audio-logger/Makefile index 8cd27c9..38011ab 100644 --- a/audio-logger/Makefile +++ b/audio-logger/Makefile @@ -1,9 +1,10 @@ -test: all - bash run_test.sh - all: cargo build --release + +test: + cargo build --release mkdir -p recordings + bash run_test.sh clean: cargo clean @@ -14,4 +15,4 @@ fclean: clean re: fclean cargo build --release -.PHONY: all clean re test \ No newline at end of file +.PHONY: all clean fclean re test diff --git a/audio-logger/src/cli.rs b/audio-logger/src/cli.rs index d4c5e9e..1843a28 100644 --- a/audio-logger/src/cli.rs +++ b/audio-logger/src/cli.rs @@ -1,4 +1,4 @@ -use clap::{Parser, ValueEnum}; +use clap::{Parser, ValueEnum, Subcommand}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] pub enum Hosts { @@ -6,9 +6,39 @@ pub enum Hosts { Jack, } +#[derive(Subcommand)] +#[clap(about = "A tool to record audio on Linux using the command line.")] +pub enum Commands { + /// Record audio either continously or in batches (in case a possible + /// interruption is an issue) + Rec(Rec), + /// Play audio from a .wav file + Play(Play), + /// Merge audio resulting from a batch recording into a single file + Merge(Merge), + /// Get reports of system's audio resources + Info(Info), +} + #[derive(Parser, Debug)] -#[clap(about = "A tool to record audio.")] -pub struct Args { +#[clap(about = "Merge audio resulting from a batch recording into a single + file. +")] +pub struct Merge { + /// The directory to look for files to merge. + #[clap(short, long, default_value = "./", required = true)] + pub input: std::path::PathBuf, + + /// The directory to save the merged file to. + #[clap(short, long, default_value = "./")] + pub output: std::path::PathBuf, +} + +#[derive(Parser, Debug)] +#[clap(about = "Record audio either continously or in batches (in case a + possible interruption is an issue). +")] +pub struct Rec { /// Filename will be `[NAME]-yyyy-mm-dd-H:M:S.wav` #[clap(required = true, short, long)] @@ -22,10 +52,6 @@ pub struct Args { #[clap(short, long, value_name = "SECONDS")] pub batch_recording: Option, - /// Output the available devices and their configurations - #[clap(long)] - pub print_configs: bool, - /// Host API to use #[clap(value_enum)] pub host: Hosts, @@ -42,3 +68,27 @@ pub struct Args { #[clap(long, value_name = "FRAMES")] pub buffer_size: Option, } +#[derive(Parser, Debug)] +#[clap(about = "Play audio from a .wav file. +")] +pub struct Play { + /// Path to the file to play + #[clap(value_parser, value_name = "PATH", value_hint = clap::ValueHint::FilePath)] + pub input: std::path::PathBuf, +} + +#[derive(Parser, Debug)] +#[clap(about = "Get reports of system's audio resources.")] +pub struct Info { + /// Output the available devices and their configurations + #[clap(long)] + pub print_configs: bool, +} + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +#[clap(propagate_version = true)] +pub struct Cli { + #[clap(subcommand)] + pub command: Commands, +} \ No newline at end of file diff --git a/audio-logger/src/constants.rs b/audio-logger/src/constants.rs new file mode 100644 index 0000000..7fa1495 --- /dev/null +++ b/audio-logger/src/constants.rs @@ -0,0 +1,7 @@ +pub const DEFAULT_SAMPLE_RATE: u32 = 44100; +pub const DEFAULT_CHANNEL_COUNT: u16 = 1; +pub const DEFAULT_BUFFER_SIZE: u32 = 1024; +pub const ALLOWED_SAMPLE_RATES: [u32; 6] = [44100, 48000, 88200, 96000, 176400, 192000]; +pub const MAX_CHANNEL_COUNT: u16 = 2; +pub const MIN_BUFFER_SIZE: usize = 64; +pub const MAX_BUFFER_SIZE: usize = 8192; \ No newline at end of file diff --git a/audio-logger/src/getters.rs b/audio-logger/src/getters.rs index 191ea68..aa6ef1e 100644 --- a/audio-logger/src/getters.rs +++ b/audio-logger/src/getters.rs @@ -1,10 +1,18 @@ -use cpal::traits::{DeviceTrait, HostTrait}; -use cpal::*; -use std::path::PathBuf; -use hound::WavSpec; -use chrono::prelude::*; -use anyhow::{Error, anyhow}; use super::*; +use cpal::{ + StreamConfig, + SupportedStreamConfig, + Device, + HostId, + Host, + SampleRate, + BufferSize, + traits::{DeviceTrait, HostTrait}, +}; +use hound::WavSpec; +use std::path::PathBuf; +use chrono::*; +use anyhow::{Error, Result, anyhow}; /// # Get Host /// @@ -86,17 +94,20 @@ pub fn get_wav_spec(default_config: &SupportedStreamConfig, user_config: &Stream }) } +pub fn get_date_time_string() -> String { + let now: DateTime = Local::now(); + format!( + "{}-{}-{}_{}:{}:{}", + now.year(), now.month(), now.day(), + now.hour(), now.minute(), now.second(), + ) +} /// # Get Filename /// /// Get the filename for the current recording according to the given format, /// the current date and time, and the name prefix. pub fn get_filename(name: &str, path: &PathBuf) -> String { - let now: DateTime = Local::now(); - let filename = format!( - "{}-{}-{}-{}-{}:{}:{}.wav", - name, - now.year(), now.month(), now.day(), - now.hour(), now.minute(), now.second(), - ); - path.join(filename).to_str().unwrap().to_string() + let mut filename = path.clone(); + filename.push(format!("{}_{}.wav", get_date_time_string(), name)); + filename.to_str().unwrap().to_string() } diff --git a/audio-logger/src/input_handling.rs b/audio-logger/src/input_handling.rs index ab0b675..9f5b3d1 100644 --- a/audio-logger/src/input_handling.rs +++ b/audio-logger/src/input_handling.rs @@ -1,5 +1,10 @@ -use std::sync::{Arc, Mutex}; use super::*; +use std::sync::{ + Arc, + Mutex, + Condvar, + atomic::{AtomicBool, Ordering} +}; type StreamInterrupt = Arc<(Mutex, Condvar)>; type BatchInterrupt= Arc; diff --git a/audio-logger/src/main.rs b/audio-logger/src/main.rs index fef0a94..d952d5e 100644 --- a/audio-logger/src/main.rs +++ b/audio-logger/src/main.rs @@ -1,59 +1,43 @@ -//! Records a WAV file (roughly 3 seconds long) using the default input device and config. -//! -//! The input data is recorded to "$CARGO_MANIFEST_DIR/recorded.wav". +//! # Main mod cli; mod recorder; mod print_configs; mod getters; mod input_handling; +mod merge; +mod constants; +mod play; -use clap::Parser; -use recorder::{batch_recording, contiguous_recording}; -use cli::*; -use std::path::Path; use print_configs::*; -use std::sync::{Condvar, atomic::{AtomicBool, Ordering}}; +use cli::*; +use merge::*; +use recorder::*; +use constants::*; +use getters::*; +use input_handling::*; +use play::*; -const DEFAULT_SAMPLE_RATE: u32 = 44100; -const DEFAULT_CHANNEL_COUNT: u16 = 1; -const DEFAULT_BUFFER_SIZE: u32 = 1024; -const ALLOWED_SAMPLE_RATES: [u32; 6] = [44100, 48000, 88200, 96000, 176400, 192000]; -const MAX_CHANNEL_COUNT: u16 = 2; -const MIN_BUFFER_SIZE: usize = 64; -const MAX_BUFFER_SIZE: usize = 8192; +use anyhow::{Result, Error}; +use clap::Parser; -fn main() -> Result<(), anyhow::Error> { - let args = Args::parse(); +fn main() -> Result<(), Error> { + let cli = Cli::parse(); - if args.print_configs { - print_configs()?; - return Ok(()); - } - - let mut recorder = recorder::Recorder::init( - args.name.clone(), - match args.output.clone() { - Some(path) => path, - None => Path::new("./").to_path_buf(), + match &cli.command { + Commands::Rec(args) => { + record(&args)?; }, - match args.host { - Hosts::Alsa => cpal::HostId::Alsa, - Hosts::Jack => cpal::HostId::Jack, + Commands::Play(args) => { + play(&args.input)?; }, - args.sample_rate.unwrap_or(DEFAULT_SAMPLE_RATE), - args.channels.unwrap_or(DEFAULT_CHANNEL_COUNT), - args.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE), - )?; - - match args.batch_recording { - Some(secs) => { - batch_recording(&mut recorder, secs)?; + Commands::Info(args) => { + if args.print_configs { + print_configs()?; + } }, - None => { - contiguous_recording(&mut recorder)?; + Commands::Merge(args) => { + merge_wavs(&args.input, &args.output)?; } } - Ok(()) } - diff --git a/audio-logger/src/merge.rs b/audio-logger/src/merge.rs new file mode 100644 index 0000000..bebc093 --- /dev/null +++ b/audio-logger/src/merge.rs @@ -0,0 +1,43 @@ +use hound::{WavReader, WavWriter}; +use anyhow::{Result, Error}; + +pub fn merge_wavs(input: &std::path::PathBuf, output: &std::path::PathBuf) -> Result<(), Error> { + // Read files from input directory + let mut files = std::fs::read_dir(input)? + .filter_map(|entry| entry.ok()) + .filter(|entry| entry.file_type().ok().map(|t| t.is_file()).unwrap_or(false)) + .filter(|entry| entry.path().extension().unwrap_or_default() == "wav") + .collect::>(); + + // Sort files by name + files.sort_by(|a, b| a.file_name().cmp(&b.file_name())); + + // Use the name of the first file as the name of the output file + let output_name = files.first().unwrap().file_name(); + let output_name = output_name.to_str().unwrap(); + + // Get wav spec from file + let spec = WavReader::open(files.first().unwrap().path())?.spec(); + let mut writer = WavWriter::create(output.join(output_name), spec)?; + + match spec.sample_format { + hound::SampleFormat::Float => { + for file in files { + let mut reader = WavReader::open(file.path())?; + for sample in reader.samples::() { + writer.write_sample(sample?)?; + } + } + }, + hound::SampleFormat::Int => { + for file in files { + let mut reader = WavReader::open(file.path())?; + for sample in reader.samples::() { + writer.write_sample(sample?)?; + } + } + }, + } + writer.finalize()?; + Ok(()) +} diff --git a/audio-logger/src/play.rs b/audio-logger/src/play.rs new file mode 100644 index 0000000..0115156 --- /dev/null +++ b/audio-logger/src/play.rs @@ -0,0 +1,14 @@ +//! Play audio from a .wav file. + +use anyhow::{Error, Result}; +use rodio::{Decoder, OutputStream, Sink}; + +pub fn play(path: &std::path::PathBuf) -> Result<(), Error> { + let file = std::fs::File::open(path)?; + let source = Decoder::new(file).unwrap(); + let (_stream, stream_handle) = OutputStream::try_default()?; + let sink = Sink::try_new(&stream_handle)?; + sink.append(source); + sink.sleep_until_end(); + Ok(()) +} \ No newline at end of file diff --git a/audio-logger/src/print_configs.rs b/audio-logger/src/print_configs.rs index 6784749..45b0fb5 100644 --- a/audio-logger/src/print_configs.rs +++ b/audio-logger/src/print_configs.rs @@ -1,5 +1,19 @@ use cpal::traits::{DeviceTrait, HostTrait}; +// pub fn print_available_hosts() -> Result<(), anyhow::Error> { +// let hosts = cpal::available_hosts(); +// println!("Available hosts:"); +// for host in hosts { +// println!(" {}", host.name()); +// } +// Ok(()) +// } + +// pub fn print_supported_hosts() { +// println!("Supported hosts:"); +// println!("{:?}", cpal::ALL_HOSTS); +// } + pub fn print_configs() -> Result<(), anyhow::Error> { println!("Supported hosts:\n {:?}", cpal::ALL_HOSTS); let available_hosts = cpal::available_hosts(); diff --git a/audio-logger/src/recorder.rs b/audio-logger/src/recorder.rs index 3a87fe7..e2d063f 100644 --- a/audio-logger/src/recorder.rs +++ b/audio-logger/src/recorder.rs @@ -1,12 +1,20 @@ -use cpal::traits::{DeviceTrait, StreamTrait}; -use cpal::*; -use std::fs::File; -use std::io::BufWriter; -use std::path::PathBuf; -use std::sync::{Arc, Mutex}; -use crate::getters::*; -use crate::input_handling::*; +use super::*; +use cpal::{ + StreamConfig, + SupportedStreamConfig, + Device, + HostId, + Stream, + traits::{DeviceTrait, StreamTrait}, +}; +use std::{ + fs::File, + io::BufWriter, + path::{PathBuf, Path}, + sync::{Arc, Mutex}, +}; use anyhow::Error; + type WriteHandle = Arc>>>>; pub struct Recorder { @@ -85,7 +93,7 @@ impl Recorder { fn create_stream(&self) -> Result { let writer = self.writer.clone(); let config = self.user_config.clone(); - let err_fn = |err| { eprintln!("An error occurred on stream: {}", err); }; + let err_fn = |err| { eprintln!("{}: An error occurred on stream: {}", get_date_time_string(), err); }; let stream = match self.default_config.sample_format() { cpal::SampleFormat::F32 => self.device.build_input_stream( @@ -161,14 +169,36 @@ where } } -pub fn batch_recording(rec: &mut Recorder, secs: u64) -> Result<(), Error> { +fn batch_recording(rec: &mut Recorder, secs: u64) -> Result<(), Error> { while rec.interrupt_handles.batch_is_running() { rec.record_secs(secs)?; } Ok(()) } -pub fn contiguous_recording(rec: &mut Recorder) -> Result<(), Error> { +fn continuous_recording(rec: &mut Recorder) -> Result<(), Error> { rec.record()?; Ok(()) } + +pub fn record(args: &Rec) -> Result<(), Error> { + let mut recorder = Recorder::init( + args.name.clone(), + match args.output.clone() { + Some(path) => path, + None => Path::new("./").to_path_buf(), + }, + match args.host { + Hosts::Alsa => cpal::HostId::Alsa, + Hosts::Jack => cpal::HostId::Jack, + }, + args.sample_rate.unwrap_or(DEFAULT_SAMPLE_RATE), + args.channels.unwrap_or(DEFAULT_CHANNEL_COUNT), + args.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE), + )?; + + match args.batch_recording { + Some(seconds) => batch_recording(&mut recorder, seconds), + None => continuous_recording(&mut recorder), + } +}