Skip to content

Commit 6a5fd40

Browse files
committed
[add] documentation to the lambda-audio tool
1 parent ad962e9 commit 6a5fd40

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

tools/lambda_audio/src/main.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
#![allow(clippy::needless_return)]
2+
//! A small CLI for inspecting and playing sound files using the `lambda` audio
3+
//! APIs.
4+
//!
5+
//! This tool is intended for quick manual validation of decoding, device
6+
//! enumeration, and basic output playback behavior.
7+
//!
8+
//! # Commands
9+
//! - `info <path>`: Decode a sound file and print basic metadata.
10+
//! - `view <path>`: Decode a sound file, print metadata, and render an ASCII
11+
//! waveform preview.
12+
//! - `play <path>`: Decode a sound file and play it through the default output
13+
//! device.
14+
//! - `list-devices`: List output devices (platform-dependent).
215
316
use std::{
417
path::Path,
@@ -19,6 +32,7 @@ use lambda::audio::{
1932
SoundBuffer,
2033
};
2134

35+
/// Runs the `lambda-audio` CLI.
2236
fn main() {
2337
let mut args = std::env::args();
2438
let raw_program_name =
@@ -62,18 +76,45 @@ fn main() {
6276
}
6377

6478
#[derive(Debug)]
79+
/// A CLI error type that separates usage errors from runtime failures.
6580
enum ExitError {
81+
/// The user provided invalid arguments.
6682
Usage,
83+
/// The command failed due to an audio/runtime error.
6784
Runtime(AudioError),
6885
}
6986

87+
/// Prints metadata about a decoded sound file.
88+
///
89+
/// # Arguments
90+
/// - `program_name`: The CLI program name used for usage output.
91+
/// - `path`: A file path argument, if provided.
92+
///
93+
/// # Returns
94+
/// Returns `Ok(())` when the command completes successfully.
95+
///
96+
/// # Errors
97+
/// Returns `ExitError::Usage` when the path argument is missing.
98+
/// Returns `ExitError::Runtime` when decoding fails.
7099
fn cmd_info(program_name: &str, path: Option<String>) -> Result<(), ExitError> {
71100
let path = require_path(program_name, "info", path)?;
72101
let buffer = load_sound_buffer(&path).map_err(ExitError::Runtime)?;
73102
print_info(&path, &buffer);
74103
return Ok(());
75104
}
76105

106+
/// Prints metadata and renders an ASCII waveform preview.
107+
///
108+
/// # Arguments
109+
/// - `program_name`: The CLI program name used for usage output.
110+
/// - `path`: A file path argument, if provided.
111+
///
112+
/// # Returns
113+
/// Returns `Ok(())` when the command completes successfully.
114+
///
115+
/// # Errors
116+
/// Returns `ExitError::Usage` when the path argument is missing.
117+
/// Returns `ExitError::Runtime` when decoding fails.
77118
fn cmd_view(program_name: &str, path: Option<String>) -> Result<(), ExitError> {
78119
let path = require_path(program_name, "view", path)?;
79120
let buffer = load_sound_buffer(&path).map_err(ExitError::Runtime)?;
@@ -82,6 +123,18 @@ fn cmd_view(program_name: &str, path: Option<String>) -> Result<(), ExitError> {
82123
return Ok(());
83124
}
84125

126+
/// Plays a decoded sound file through the default output device.
127+
///
128+
/// # Arguments
129+
/// - `program_name`: The CLI program name used for usage output.
130+
/// - `path`: A file path argument, if provided.
131+
///
132+
/// # Returns
133+
/// Returns `Ok(())` when the command completes successfully.
134+
///
135+
/// # Errors
136+
/// Returns `ExitError::Usage` when the path argument is missing.
137+
/// Returns `ExitError::Runtime` when decoding or playback fails.
85138
fn cmd_play(program_name: &str, path: Option<String>) -> Result<(), ExitError> {
86139
let path = require_path(program_name, "play", path)?;
87140
let buffer = load_sound_buffer(&path).map_err(ExitError::Runtime)?;
@@ -90,6 +143,13 @@ fn cmd_play(program_name: &str, path: Option<String>) -> Result<(), ExitError> {
90143
return Ok(());
91144
}
92145

146+
/// Lists available output devices.
147+
///
148+
/// # Returns
149+
/// Returns `Ok(())` when the command completes successfully.
150+
///
151+
/// # Errors
152+
/// Returns `ExitError::Runtime` when device enumeration fails.
93153
fn cmd_list_devices() -> Result<(), ExitError> {
94154
let devices = enumerate_output_devices().map_err(ExitError::Runtime)?;
95155

@@ -106,6 +166,18 @@ fn cmd_list_devices() -> Result<(), ExitError> {
106166
return Ok(());
107167
}
108168

169+
/// Requires a file path argument for a command.
170+
///
171+
/// # Arguments
172+
/// - `program_name`: The CLI program name used for usage output.
173+
/// - `command`: The command name used for usage output.
174+
/// - `path`: A file path argument, if provided.
175+
///
176+
/// # Returns
177+
/// Returns the provided path string.
178+
///
179+
/// # Errors
180+
/// Returns `ExitError::Usage` when `path` is `None`.
109181
fn require_path(
110182
program_name: &str,
111183
command: &str,
@@ -118,6 +190,17 @@ fn require_path(
118190
return Ok(path);
119191
}
120192

193+
/// Loads a sound file into a decoded `SoundBuffer` based on its file extension.
194+
///
195+
/// # Arguments
196+
/// - `path`: A file path to a supported sound file.
197+
///
198+
/// # Returns
199+
/// Returns a decoded `SoundBuffer`.
200+
///
201+
/// # Errors
202+
/// Returns an `AudioError` if decoding fails or the file extension is not
203+
/// supported.
121204
fn load_sound_buffer(path: &str) -> Result<SoundBuffer, AudioError> {
122205
let path_value = Path::new(path);
123206
let extension = path_value
@@ -141,6 +224,14 @@ fn load_sound_buffer(path: &str) -> Result<SoundBuffer, AudioError> {
141224
}
142225
}
143226

227+
/// Prints basic decoded metadata for a `SoundBuffer`.
228+
///
229+
/// # Arguments
230+
/// - `path`: The decoded source path for display purposes.
231+
/// - `buffer`: The decoded sound buffer.
232+
///
233+
/// # Returns
234+
/// Returns `()` after printing metadata to stdout.
144235
fn print_info(path: &str, buffer: &SoundBuffer) {
145236
println!("path: {path}");
146237
println!("sample_rate: {}", buffer.sample_rate());
@@ -151,6 +242,17 @@ fn print_info(path: &str, buffer: &SoundBuffer) {
151242
return;
152243
}
153244

245+
/// Renders an ASCII waveform preview for a `SoundBuffer`.
246+
///
247+
/// The rendering uses a single channel (the first channel) and shows a
248+
/// peak-per-column visualization, which is intended for quick human inspection
249+
/// rather than precise analysis.
250+
///
251+
/// # Arguments
252+
/// - `buffer`: The decoded sound buffer to visualize.
253+
///
254+
/// # Returns
255+
/// Returns `()` after printing the visualization to stdout.
154256
fn print_waveform(buffer: &SoundBuffer) {
155257
let width: usize = 64;
156258
let height: usize = 10;
@@ -200,6 +302,22 @@ fn print_waveform(buffer: &SoundBuffer) {
200302
return;
201303
}
202304

305+
/// Plays a decoded `SoundBuffer` through the default output device.
306+
///
307+
/// This performs a best-effort playback by writing sequential frames into the
308+
/// output callback. No resampling or channel remapping is performed; instead,
309+
/// the output device is configured to match the buffer's sample rate and
310+
/// channel count.
311+
///
312+
/// # Arguments
313+
/// - `buffer`: The decoded sound buffer to play.
314+
///
315+
/// # Returns
316+
/// Returns `Ok(())` after playback has completed.
317+
///
318+
/// # Errors
319+
/// Returns an `AudioError` if the buffer is empty or output device creation
320+
/// fails.
203321
fn play_buffer(buffer: &SoundBuffer) -> Result<(), AudioError> {
204322
let samples = buffer.samples();
205323
let total_samples = samples.len();
@@ -259,6 +377,13 @@ fn play_buffer(buffer: &SoundBuffer) -> Result<(), AudioError> {
259377
return Ok(());
260378
}
261379

380+
/// Prints usage text to stdout.
381+
///
382+
/// # Arguments
383+
/// - `program_name`: The CLI program name shown in examples.
384+
///
385+
/// # Returns
386+
/// Returns `()` after printing the usage text.
262387
fn print_usage(program_name: &str) {
263388
println!("usage:");
264389
println!(" {program_name} info <path>");

0 commit comments

Comments
 (0)