9 hydrophonitor cli (#14)

This commit is contained in:
Julius Koskela 2023-03-22 21:58:14 +02:00 committed by GitHub
parent 44332972f1
commit 56d6d5ecbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 645 additions and 2 deletions

View File

@ -142,7 +142,8 @@ impl Recorder {
self.init_writer()?; self.init_writer()?;
let stream = self.create_stream()?; let stream = self.create_stream()?;
stream.play()?; stream.play()?;
println!("REC: {}", self.current_file); let date_time = get_date_time_string();
println!("REC[{}]: {}", date_time, self.current_file);
let now = std::time::Instant::now(); let now = std::time::Instant::now();
while self.interrupt_handles.batch_is_running() { while self.interrupt_handles.batch_is_running() {
std::thread::sleep(std::time::Duration::from_millis(1)); std::thread::sleep(std::time::Duration::from_millis(1));
@ -155,7 +156,9 @@ impl Recorder {
let current_file = self.current_file.clone(); let current_file = self.current_file.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
writer.lock().unwrap().take().unwrap().finalize().unwrap(); writer.lock().unwrap().take().unwrap().finalize().unwrap();
println!("STOP: {}", current_file); // Print STOP and the time and date
let date_time = get_date_time_string();
println!("STOP[{}]: {}", date_time, current_file);
}); });
Ok(()) Ok(())
} }

441
hp-cli/Cargo.lock generated Normal file
View File

@ -0,0 +1,441 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "clap"
version = "4.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098"
dependencies = [
"bitflags 2.0.2",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "console"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys 0.42.0",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "hound"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1"
[[package]]
name = "hp-cli"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"hound",
"indicatif",
"walkdir",
]
[[package]]
name = "indicatif"
version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729"
dependencies = [
"console",
"number_prefix",
"portable-atomic",
"unicode-width",
]
[[package]]
name = "io-lifetimes"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd6da19f25979c7270e70fa95ab371ec3b701cd0eefc47667a09785b3c59155"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.45.0",
]
[[package]]
name = "is-terminal"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys 0.45.0",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "portable-atomic"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.36.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fe885c3a125aa45213b68cc1472a49880cb5923dc23f522ad2791b882228778"
dependencies = [
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.45.0",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

13
hp-cli/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "hp-cli"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.70"
clap = { version = "4.1.11", features = ["derive"] }
hound = "3.5.0"
indicatif = "0.17.3"
walkdir = "2.3.3"

186
hp-cli/src/main.rs Normal file
View File

@ -0,0 +1,186 @@
use clap::{Parser, Subcommand};
use std::error::Error;
use std::fs;
use std::path::PathBuf;
use walkdir::WalkDir;
use indicatif::ProgressBar;
use hound::{WavReader, WavWriter};
const DATA_FOLDER: &str = "home/pi/data";
#[derive(Subcommand)]
#[clap(about = "A tool to record audio on Linux using the command line.")]
pub enum Commands {
Install(Install),
Import(Import),
}
#[derive(Parser, Debug)]
#[clap(about = "Install hydrophonitor on an SD card.")]
pub struct Install {
/// Path to the SD card. You can find the path to the SD card by running
/// `lsblk` in the terminal.
#[clap(short, long, required = true)]
pub sd_card: PathBuf,
}
#[derive(Parser, Debug)]
#[clap(about = "Import audio from an SD card.")]
pub struct Import {
/// Path to the SD card. You can find the path to the SD card by running
/// `lsblk` in the terminal.
#[clap(short, long, required = true)]
pub sd_card: PathBuf,
/// Path to the output folder. If not specified, the output folder will be
/// the current directory.
#[clap(short, long)]
pub output_folder: Option<PathBuf>,
}
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
#[clap(propagate_version = true)]
pub struct Cli {
#[clap(subcommand)]
pub commands: Commands,
}
fn import_from_sd(sd_card: &mut PathBuf, output_folder: Option<PathBuf>) -> Result<(), Box<dyn Error>>{
let output_folder = match output_folder {
Some(output_folder) => output_folder,
None => std::env::current_dir().unwrap_or_else(|err| {
eprintln!("Error: {}", err);
std::process::exit(1);
})
};
sd_card.push(DATA_FOLDER);
let count = WalkDir::new(sd_card.clone()).into_iter().count();
let progress_bar = ProgressBar::new(count as u64);
for entry in WalkDir::new(sd_card.clone()) {
let entry = entry?;
let from = entry.path();
let to = output_folder.join(from.strip_prefix(sd_card.clone())?);
if entry.file_type().is_dir() {
fs::create_dir_all(to)?;
} else if entry.file_type().is_file() {
fs::copy(from, to)?;
}
progress_bar.inc(1);
}
progress_bar.finish();
Ok(())
}
pub fn merge_wavs(input: &std::path::PathBuf, output: &std::path::PathBuf) -> Result<(), Box<dyn 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::<Vec<_>>();
// If there are no wav files, return
if files.is_empty() {
println!("No wav files found in {:?}", input);
return Ok(());
}
// Sort files by name
files.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
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)?;
let progress_bar = ProgressBar::new(files.len() as u64);
match spec.sample_format {
hound::SampleFormat::Float => {
for file in files {
let mut reader = WavReader::open(file.path())?;
for sample in reader.samples::<f32>() {
writer.write_sample(sample?)?;
}
progress_bar.inc(1);
}
},
hound::SampleFormat::Int => {
for file in files {
let mut reader = WavReader::open(file.path())?;
for sample in reader.samples::<i32>() {
writer.write_sample(sample?)?;
}
progress_bar.inc(1);
}
},
}
progress_bar.finish();
writer.finalize()?;
Ok(())
}
fn main() {
let commands = Cli::parse();
match commands.commands {
Commands::Install(install) => {
println!("Installing hydrophonitor on SD card at {:?}", install.sd_card);
}
Commands::Import(mut import) => {
println!("Importing audio from SD card at {:?}", import.sd_card);
if let Some(output_folder) = &import.output_folder {
println!("Output folder: {:?}", output_folder);
import_from_sd(&mut import.sd_card, Some(output_folder.clone())).unwrap_or_else(|err| {
eprintln!("Error: {}", err);
std::process::exit(1);
});
} else {
println!("Output folder: current directory");
import_from_sd(&mut import.sd_card, None).unwrap_or_else(|err| {
eprintln!("Error: {}", err);
std::process::exit(1);
});
}
// Iterate folders inside output folder. Inside each iterated folder there is
// a folder called "audio" which contains the wav files. Merge them into a single
// wav file and delete the "audio" folder.
let output_folder = match import.output_folder {
Some(output_folder) => output_folder,
None => std::env::current_dir().unwrap_or_else(|err| {
eprintln!("Error: {}", err);
std::process::exit(1);
})
};
for entry in WalkDir::new(output_folder.clone()) {
let entry = entry.unwrap_or_else(|err| {
eprintln!("Error: {}", err);
std::process::exit(1);
});
let path = entry.path();
if path.is_dir() {
let audio_folder = path.join("audio");
if audio_folder.exists() {
merge_wavs(&audio_folder, &PathBuf::from(path)).unwrap_or_else(|err| {
eprintln!("Error: {}", err);
std::process::exit(1);
});
fs::remove_dir_all(audio_folder).unwrap_or_else(|err| {
eprintln!("Error: {}", err);
std::process::exit(1);
});
}
}
}
}
}
}