Add level measurement to gauge background noise
This commit is contained in:
parent
362be00d07
commit
07308a548a
2 changed files with 81 additions and 5 deletions
78
src/main.zig
78
src/main.zig
|
|
@ -286,6 +286,70 @@ fn signalHandler(sig: i32) callconv(.c) void {
|
|||
}
|
||||
}
|
||||
|
||||
fn runMeasureLevels(allocator: std.mem.Allocator) !void {
|
||||
const stdout = std.fs.File.stdout();
|
||||
const is_tty = stdout.isTty();
|
||||
|
||||
var capture = try stt.AlsaCapture.init(allocator, "default", 16000, 1024);
|
||||
defer capture.deinit();
|
||||
try capture.open();
|
||||
|
||||
_ = try stdout.writeAll("Measuring audio levels... Press Ctrl+C to exit\n");
|
||||
if (is_tty) {
|
||||
_ = try stdout.writeAll("Histogram (0-10000):\n");
|
||||
}
|
||||
|
||||
var buffer: [4096]i16 = undefined;
|
||||
var write_buffer: [256]u8 = undefined;
|
||||
var second_max: u16 = 0;
|
||||
var last_print = std.time.milliTimestamp();
|
||||
|
||||
while (!should_exit.load(.acquire)) {
|
||||
_ = try capture.readAudio();
|
||||
const samples_read = capture.getAudioSamples(&buffer);
|
||||
if (samples_read == 0) {
|
||||
std.Thread.sleep(10 * std.time.ns_per_ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
var max_amp: u16 = 0;
|
||||
for (buffer[0..samples_read]) |sample| {
|
||||
const abs_sample = @abs(sample);
|
||||
if (abs_sample > max_amp) max_amp = abs_sample;
|
||||
}
|
||||
|
||||
if (max_amp > second_max) second_max = max_amp;
|
||||
|
||||
const now = std.time.milliTimestamp();
|
||||
if (now - last_print >= 1000) {
|
||||
if (is_tty) {
|
||||
const bar_width = (@as(u32, second_max) * 60) / 10000;
|
||||
var writer = stdout.writer(&write_buffer);
|
||||
const w = &writer.interface;
|
||||
try w.print("{d:5} |", .{second_max});
|
||||
try w.flush();
|
||||
for (0..bar_width) |_| {
|
||||
_ = try stdout.writeAll("█");
|
||||
}
|
||||
_ = try stdout.writeAll("\n");
|
||||
} else {
|
||||
var writer = stdout.writer(&write_buffer);
|
||||
const w = &writer.interface;
|
||||
try w.print("{d}\n", .{second_max});
|
||||
try w.flush();
|
||||
}
|
||||
second_max = 0;
|
||||
last_print = now;
|
||||
}
|
||||
|
||||
std.Thread.sleep(50 * std.time.ns_per_ms);
|
||||
}
|
||||
|
||||
if (is_tty) {
|
||||
_ = try stdout.writeAll("\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn signalAction(sig: i32, info: *const std.posix.siginfo_t, _: ?*anyopaque) callconv(.c) void {
|
||||
// NOTE: info only works correctly if std.posix.SA.SIGINFO is in the flags
|
||||
// std.log.debug("signal action. sig {d}", .{sig});
|
||||
|
|
@ -353,6 +417,7 @@ pub fn main() !void {
|
|||
|
||||
var model_path: ?[]const u8 = null;
|
||||
var exec_program: ?[]const u8 = null;
|
||||
var measure_levels = false;
|
||||
|
||||
// Parse arguments
|
||||
for (args[1..]) |arg| {
|
||||
|
|
@ -363,11 +428,13 @@ pub fn main() !void {
|
|||
_ = try stdout.writeAll("OPTIONS:\n");
|
||||
_ = try stdout.writeAll(" --model=<path> Path to Vosk model directory\n");
|
||||
_ = try stdout.writeAll(" --exec=<program> Program to execute with recognized text\n");
|
||||
_ = try stdout.writeAll(" --measure-levels Display real-time audio level histogram\n");
|
||||
_ = try stdout.writeAll(" --help, -h Show this help message\n\n");
|
||||
_ = try stdout.writeAll("EXAMPLES:\n");
|
||||
_ = try stdout.writeAll(" stt\n");
|
||||
_ = try stdout.writeAll(" stt --model=../share/vosk/models/vosk-model-small-en-us-0.15\n");
|
||||
_ = try stdout.writeAll(" stt --exec=echo\n\n");
|
||||
_ = try stdout.writeAll(" stt --exec=echo\n");
|
||||
_ = try stdout.writeAll(" stt --measure-levels\n\n");
|
||||
_ = try stdout.writeAll("The application will search for models in these locations:\n");
|
||||
_ = try stdout.writeAll(" vosk-model-small-en-us-0.15\n");
|
||||
_ = try stdout.writeAll(" <binary_dir>/../share/vosk/models/vosk-model-small-en-us-0.15\n");
|
||||
|
|
@ -376,6 +443,8 @@ pub fn main() !void {
|
|||
model_path = arg[8..]; // Skip "--model="
|
||||
} else if (std.mem.startsWith(u8, arg, "--exec=")) {
|
||||
exec_program = arg[7..]; // Skip "--exec="
|
||||
} else if (std.mem.eql(u8, arg, "--measure-levels")) {
|
||||
measure_levels = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -385,6 +454,13 @@ pub fn main() !void {
|
|||
.exec_program = exec_program,
|
||||
};
|
||||
defer handler.deinit();
|
||||
|
||||
// If measure-levels mode, run that instead of normal STT
|
||||
if (measure_levels) {
|
||||
try runMeasureLevels(allocator);
|
||||
return;
|
||||
}
|
||||
|
||||
const speech_handler = stt.SpeechEventHandler{
|
||||
.onSpeechFn = SpeechHandler.onSpeech,
|
||||
.onErrorFn = SpeechHandler.onError,
|
||||
|
|
|
|||
|
|
@ -531,7 +531,7 @@ pub const AlsaCapture = struct {
|
|||
}
|
||||
|
||||
/// Read audio data from ALSA device and process it
|
||||
fn readAudio(self: *Self) !usize {
|
||||
pub fn readAudio(self: *Self) !usize {
|
||||
if (self.pcm_handle == null)
|
||||
return Error.AudioDeviceError;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue