remove demo - we will use. Also add --exec handling

This commit is contained in:
Emil Lerch 2025-09-10 18:40:36 -07:00
parent 6fe5bba155
commit 3a8138c901
Signed by: lobo
GPG key ID: A7B62D657EF764F8
2 changed files with 42 additions and 34 deletions

View file

@ -46,9 +46,9 @@ pub fn build(b: *std.Build) void {
b.installArtifact(stt_lib); b.installArtifact(stt_lib);
// Create the demo executable // Create the executable
const exe = b.addExecutable(.{ const exe = b.addExecutable(.{
.name = "stt-demo", .name = "stt",
.root_module = b.createModule(.{ .root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"), .root_source_file = b.path("src/main.zig"),
.target = target, .target = target,
@ -114,7 +114,7 @@ pub fn build(b: *std.Build) void {
const run_dedicated_unit_tests = b.addRunArtifact(dedicated_unit_tests); const run_dedicated_unit_tests = b.addRunArtifact(dedicated_unit_tests);
// Creates a step for unit testing the demo application // Creates a step for unit testing the application
const exe_unit_tests = b.addTest(.{ const exe_unit_tests = b.addTest(.{
.root_module = b.createModule(.{ .root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"), .root_source_file = b.path("src/main.zig"),

View file

@ -1,7 +1,4 @@
//! STT Library Demo Application //! STT with callback-based event handling
//!
//! This demonstrates how to use the STT library for speech recognition
//! with callback-based event handling and proper resource management.
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
@ -11,29 +8,38 @@ const stt = @import("stt.zig");
var should_exit = std.atomic.Value(bool).init(false); var should_exit = std.atomic.Value(bool).init(false);
/// Demo implementation of speech event handler with comprehensive error handling /// Demo implementation of speech event handler with comprehensive error handling
const DemoHandler = struct { const SpeechHandler = struct {
speech_count: u32 = 0, speech_count: u32 = 0,
error_count: u32 = 0, error_count: u32 = 0,
warning_count: u32 = 0, warning_count: u32 = 0,
recoverable_error_count: u32 = 0, recoverable_error_count: u32 = 0,
exec_program: ?[]const u8 = null,
/// Handle detected speech /// Handle detected speech
fn onSpeech(ctx: *anyopaque, text: []const u8) void { fn onSpeech(ctx: *anyopaque, text: []const u8) void {
if (builtin.is_test) return; // Suppress output during tests if (builtin.is_test) return; // Suppress output during tests
const self: *DemoHandler = @ptrCast(@alignCast(ctx)); const self: *SpeechHandler = @ptrCast(@alignCast(ctx));
self.speech_count += 1; self.speech_count += 1;
// Print with timestamp for better demo experience // Print with timestamp for better experience
const timestamp = std.time.timestamp(); const timestamp = std.time.timestamp();
std.debug.print("[{}] Speech #{}: {s}\n", .{ timestamp, self.speech_count, text }); std.debug.print("[{}] Speech #{}: {s}\n", .{ timestamp, self.speech_count, text });
// Execute program if specified
if (self.exec_program) |program| {
var child = std.process.Child.init(&[_][]const u8{ program, text }, std.heap.page_allocator);
_ = child.spawn() catch |err| {
std.log.err("Failed to execute program '{s}': {}", .{ program, err });
};
}
} }
/// Handle basic errors (fallback for compatibility) /// Handle basic errors (fallback for compatibility)
fn onError(ctx: *anyopaque, error_code: stt.SttError, message: []const u8) void { fn onError(ctx: *anyopaque, error_code: stt.SttError, message: []const u8) void {
if (builtin.is_test) return; // Suppress output during tests if (builtin.is_test) return; // Suppress output during tests
const self: *DemoHandler = @ptrCast(@alignCast(ctx)); const self: *SpeechHandler = @ptrCast(@alignCast(ctx));
self.error_count += 1; self.error_count += 1;
// Print error with timestamp // Print error with timestamp
@ -43,13 +49,13 @@ const DemoHandler = struct {
/// Handle detailed errors with comprehensive information /// Handle detailed errors with comprehensive information
fn onDetailedError(ctx: *anyopaque, error_info: stt.SttErrorInfo) void { fn onDetailedError(ctx: *anyopaque, error_info: stt.SttErrorInfo) void {
const self: *DemoHandler = @ptrCast(@alignCast(ctx)); const self: *SpeechHandler = @ptrCast(@alignCast(ctx));
logDetail(self, error_info) catch |e| logDetail(self, error_info) catch |e|
std.log.err("Error writing error {}. Original message: {s}", .{ e, error_info.message }); std.log.err("Error writing error {}. Original message: {s}", .{ e, error_info.message });
} }
fn logDetail(self: *DemoHandler, error_info: stt.SttErrorInfo) !void { fn logDetail(self: *SpeechHandler, error_info: stt.SttErrorInfo) !void {
const log = std.log.scoped(.stt); const log = std.log.scoped(.stt);
// Categorize the error for statistics // Categorize the error for statistics
if (error_info.recoverable) if (error_info.recoverable)
@ -106,7 +112,7 @@ const DemoHandler = struct {
} }
/// Get comprehensive statistics for summary /// Get comprehensive statistics for summary
fn getStats(self: *const DemoHandler) struct { fn getStats(self: *const SpeechHandler) struct {
speech_count: u32, speech_count: u32,
error_count: u32, error_count: u32,
warning_count: u32, warning_count: u32,
@ -156,29 +162,31 @@ pub fn main() !void {
}); });
_ = c.signal(c.SIGINT, signalHandler); _ = c.signal(c.SIGINT, signalHandler);
// Create demo handler with statistics tracking
var demo_handler = DemoHandler{};
const speech_handler = stt.SpeechEventHandler{
.onSpeechFn = DemoHandler.onSpeech,
.onErrorFn = DemoHandler.onError,
.onDetailedErrorFn = DemoHandler.onDetailedError,
.ctx = &demo_handler,
};
// Parse command line arguments // Parse command line arguments
const args = try std.process.argsAlloc(allocator); const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args); defer std.process.argsFree(allocator, args);
var model_path: ?[]const u8 = null; var model_path: ?[]const u8 = null;
var exec_program: ?[]const u8 = null;
// Parse --model argument // Parse --model and --exec arguments
for (args[1..]) |arg| { for (args[1..]) |arg| {
if (std.mem.startsWith(u8, arg, "--model=")) { if (std.mem.startsWith(u8, arg, "--model=")) {
model_path = arg[8..]; // Skip "--model=" model_path = arg[8..]; // Skip "--model="
break; } else if (std.mem.startsWith(u8, arg, "--exec=")) {
exec_program = arg[7..]; // Skip "--exec="
} }
} }
// Create handler with statistics tracking
var handler = SpeechHandler{ .exec_program = exec_program };
const speech_handler = stt.SpeechEventHandler{
.onSpeechFn = SpeechHandler.onSpeech,
.onErrorFn = SpeechHandler.onError,
.onDetailedErrorFn = SpeechHandler.onDetailedError,
.ctx = &handler,
};
// If no model specified, try default locations // If no model specified, try default locations
const default_paths = [_][]const u8{ const default_paths = [_][]const u8{
"vosk-model-small-en-us-0.15", "vosk-model-small-en-us-0.15",
@ -197,7 +205,7 @@ pub fn main() !void {
// Check if model path exists // Check if model path exists
if (model_path == null) { if (model_path == null) {
_ = try stderr.writeAll("Error: Vosk model not found.\n\n"); _ = try stderr.writeAll("Error: Vosk model not found.\n\n");
_ = try stderr.writeAll("Usage: stt-demo [--model=<path>]\n\n"); _ = try stderr.writeAll("Usage: stt [--model=<path>] [--exec=<program>]\n\n");
_ = try stderr.writeAll("Locations searched:\n"); _ = try stderr.writeAll("Locations searched:\n");
inline for (default_paths) |path| inline for (default_paths) |path|
_ = try stderr.writeAll("\t" ++ path ++ "\n"); _ = try stderr.writeAll("\t" ++ path ++ "\n");
@ -289,8 +297,8 @@ pub fn main() !void {
_ = stdout.writeAll("\n----------------------------------------\n") catch {}; _ = stdout.writeAll("\n----------------------------------------\n") catch {};
_ = stdout.writeAll("Shutdown signal received, stopping...\n") catch {}; _ = stdout.writeAll("Shutdown signal received, stopping...\n") catch {};
// Get final statistics from demo handler // Get final statistics from handler
const stats = demo_handler.getStats(); const stats = handler.getStats();
std.log.info("Demo Session Summary:", .{}); std.log.info("Demo Session Summary:", .{});
std.log.info(" Speech detections: {}", .{stats.speech_count}); std.log.info(" Speech detections: {}", .{stats.speech_count});
std.log.info(" Fatal errors: {}", .{stats.error_count}); std.log.info(" Fatal errors: {}", .{stats.error_count});
@ -304,15 +312,15 @@ pub fn main() !void {
_ = stdout.writeAll("Session completed successfully.\n") catch {}; _ = stdout.writeAll("Session completed successfully.\n") catch {};
} }
// Test the demo functionality // Test the functionality
test "demo handler functionality" { test "handler functionality" {
const testing = std.testing; const testing = std.testing;
var demo_handler = DemoHandler{}; var handler = SpeechHandler{};
const speech_handler = stt.SpeechEventHandler{ const speech_handler = stt.SpeechEventHandler{
.onSpeechFn = DemoHandler.onSpeech, .onSpeechFn = SpeechHandler.onSpeech,
.onErrorFn = DemoHandler.onError, .onErrorFn = SpeechHandler.onError,
.ctx = &demo_handler, .ctx = &handler,
}; };
// Test that callbacks can be invoked without crashing // Test that callbacks can be invoked without crashing