Implement merge and play, refactor
This commit is contained in:
parent
16a2c5c47c
commit
e81028d751
222
audio-logger/Cargo.lock
generated
222
audio-logger/Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
.PHONY: all clean fclean re test
|
||||
|
@ -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<u64>,
|
||||
|
||||
/// 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<u32>,
|
||||
}
|
||||
#[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,
|
||||
}
|
7
audio-logger/src/constants.rs
Normal file
7
audio-logger/src/constants.rs
Normal file
@ -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;
|
@ -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> = 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> = 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()
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use super::*;
|
||||
use std::sync::{
|
||||
Arc,
|
||||
Mutex,
|
||||
Condvar,
|
||||
atomic::{AtomicBool, Ordering}
|
||||
};
|
||||
|
||||
type StreamInterrupt = Arc<(Mutex<bool>, Condvar)>;
|
||||
type BatchInterrupt= Arc<AtomicBool>;
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
43
audio-logger/src/merge.rs
Normal file
43
audio-logger/src/merge.rs
Normal file
@ -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::<Vec<_>>();
|
||||
|
||||
// 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::<f32>() {
|
||||
writer.write_sample(sample?)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
hound::SampleFormat::Int => {
|
||||
for file in files {
|
||||
let mut reader = WavReader::open(file.path())?;
|
||||
for sample in reader.samples::<i32>() {
|
||||
writer.write_sample(sample?)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
writer.finalize()?;
|
||||
Ok(())
|
||||
}
|
14
audio-logger/src/play.rs
Normal file
14
audio-logger/src/play.rs
Normal file
@ -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(())
|
||||
}
|
@ -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();
|
||||
|
@ -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<Mutex<Option<hound::WavWriter<BufWriter<File>>>>>;
|
||||
|
||||
pub struct Recorder {
|
||||
@ -85,7 +93,7 @@ impl Recorder {
|
||||
fn create_stream(&self) -> Result<Stream, Error> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user