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
316use std:: {
417 path:: Path ,
@@ -19,6 +32,7 @@ use lambda::audio::{
1932 SoundBuffer ,
2033} ;
2134
35+ /// Runs the `lambda-audio` CLI.
2236fn 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.
6580enum 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.
7099fn 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.
77118fn 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.
85138fn 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.
93153fn 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`.
109181fn 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.
121204fn 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.
144235fn 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.
154256fn 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.
203321fn 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.
262387fn print_usage ( program_name : & str ) {
263388 println ! ( "usage:" ) ;
264389 println ! ( " {program_name} info <path>" ) ;
0 commit comments