SttSession -> Session
This commit is contained in:
parent
f81d2369f6
commit
e2ffff8130
3 changed files with 30 additions and 30 deletions
|
@ -406,7 +406,7 @@ pub fn main() !void {
|
||||||
};
|
};
|
||||||
|
|
||||||
std.log.debug("Initializing STT library...", .{});
|
std.log.debug("Initializing STT library...", .{});
|
||||||
var session = stt.SttSession.init(allocator, options) catch |err| {
|
var session = stt.Session.init(allocator, options) catch |err| {
|
||||||
std.log.err("Failed to initialize STT library: {}", .{err});
|
std.log.err("Failed to initialize STT library: {}", .{err});
|
||||||
std.log.err("Please ensure:", .{});
|
std.log.err("Please ensure:", .{});
|
||||||
std.log.err(" - Audio device '{s}' is available", .{options.audio_device});
|
std.log.err(" - Audio device '{s}' is available", .{options.audio_device});
|
||||||
|
|
52
src/stt.zig
52
src/stt.zig
|
@ -539,7 +539,7 @@ pub const Options = struct {
|
||||||
///
|
///
|
||||||
/// This represents an active speech-to-text session with configured
|
/// This represents an active speech-to-text session with configured
|
||||||
/// audio input and speech recognition model.
|
/// audio input and speech recognition model.
|
||||||
pub const SttSession = struct {
|
pub const Session = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
/// Memory allocator
|
/// Memory allocator
|
||||||
|
@ -574,9 +574,9 @@ pub const SttSession = struct {
|
||||||
/// - options: Configuration options for the session
|
/// - options: Configuration options for the session
|
||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// - SttSession instance on success
|
/// - Session instance on success
|
||||||
/// - SttError on failure
|
/// - SttError on failure
|
||||||
pub fn init(allocator: std.mem.Allocator, options: Options) SttError!SttSession {
|
pub fn init(allocator: std.mem.Allocator, options: Options) SttError!Session {
|
||||||
// Validate options first with detailed error reporting
|
// Validate options first with detailed error reporting
|
||||||
validateOptions(options) catch |err| {
|
validateOptions(options) catch |err| {
|
||||||
const error_info = switch (err) {
|
const error_info = switch (err) {
|
||||||
|
@ -625,7 +625,7 @@ pub const SttSession = struct {
|
||||||
vosk_audio_buffer_mut.deinit();
|
vosk_audio_buffer_mut.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
var session = SttSession{
|
var session = Session{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.options = options,
|
.options = options,
|
||||||
.alsa_capture = alsa_capture,
|
.alsa_capture = alsa_capture,
|
||||||
|
@ -655,7 +655,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize Vosk model and recognizer
|
/// Initialize Vosk model and recognizer
|
||||||
fn initVosk(self: *SttSession) !void {
|
fn initVosk(self: *Session) !void {
|
||||||
// Convert model path to null-terminated string
|
// Convert model path to null-terminated string
|
||||||
const model_path_cstr = try self.allocator.dupeZ(u8, self.options.model_path);
|
const model_path_cstr = try self.allocator.dupeZ(u8, self.options.model_path);
|
||||||
defer self.allocator.free(model_path_cstr);
|
defer self.allocator.free(model_path_cstr);
|
||||||
|
@ -683,7 +683,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Partial cleanup for initialization failures
|
/// Partial cleanup for initialization failures
|
||||||
fn deinitPartial(self: *SttSession) void {
|
fn deinitPartial(self: *Session) void {
|
||||||
// Clean up Vosk resources
|
// Clean up Vosk resources
|
||||||
if (self.vosk_recognizer) |recognizer| {
|
if (self.vosk_recognizer) |recognizer| {
|
||||||
c.vosk_recognizer_free(recognizer);
|
c.vosk_recognizer_free(recognizer);
|
||||||
|
@ -708,7 +708,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Audio capture thread function with comprehensive error handling
|
/// Audio capture thread function with comprehensive error handling
|
||||||
fn audioThreadFn(self: *SttSession) void {
|
fn audioThreadFn(self: *Session) void {
|
||||||
var retry_count: u32 = 0;
|
var retry_count: u32 = 0;
|
||||||
const max_retries = 5; // not sure this needs retries...
|
const max_retries = 5; // not sure this needs retries...
|
||||||
const retry_delay_ms = 100;
|
const retry_delay_ms = 100;
|
||||||
|
@ -843,7 +843,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Vosk processing thread function with comprehensive error handling
|
/// Vosk processing thread function with comprehensive error handling
|
||||||
fn processingThreadFn(self: *SttSession) void {
|
fn processingThreadFn(self: *Session) void {
|
||||||
// Processing buffer for Vosk (4096 samples = ~256ms at 16kHz)
|
// Processing buffer for Vosk (4096 samples = ~256ms at 16kHz)
|
||||||
const vosk_chunk_size = 4096;
|
const vosk_chunk_size = 4096;
|
||||||
const min_chunk_size = 1024; // Minimum chunk size for processing
|
const min_chunk_size = 1024; // Minimum chunk size for processing
|
||||||
|
@ -954,7 +954,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process audio chunk with Vosk and handle results
|
/// Process audio chunk with Vosk and handle results
|
||||||
fn processVoskAudio(self: *SttSession, audio_data: []const i16) !void {
|
fn processVoskAudio(self: *Session, audio_data: []const i16) !void {
|
||||||
if (self.vosk_recognizer == null) {
|
if (self.vosk_recognizer == null) {
|
||||||
return SttError.InvalidState;
|
return SttError.InvalidState;
|
||||||
}
|
}
|
||||||
|
@ -1000,7 +1000,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Vosk JSON result and extract recognized text
|
/// Parse Vosk JSON result and extract recognized text
|
||||||
fn parseVoskResult(self: *SttSession, json_str: []const u8) !void {
|
fn parseVoskResult(self: *Session, json_str: []const u8) !void {
|
||||||
// Simple JSON parsing to extract "text" field
|
// Simple JSON parsing to extract "text" field
|
||||||
// Vosk returns JSON like: {"text": "hello world"}
|
// Vosk returns JSON like: {"text": "hello world"}
|
||||||
|
|
||||||
|
@ -1040,7 +1040,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Vosk partial result (for real-time feedback)
|
/// Parse Vosk partial result (for real-time feedback)
|
||||||
fn parseVoskPartialResult(self: *SttSession, json_str: []const u8) !void {
|
fn parseVoskPartialResult(self: *Session, json_str: []const u8) !void {
|
||||||
// Similar to parseVoskResult but for partial results
|
// Similar to parseVoskResult but for partial results
|
||||||
// For now, we don't use partial results, but this could be extended
|
// For now, we don't use partial results, but this could be extended
|
||||||
// to provide real-time transcription feedback
|
// to provide real-time transcription feedback
|
||||||
|
@ -1049,7 +1049,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to recover from audio device errors with detailed error reporting
|
/// Attempt to recover from audio device errors with detailed error reporting
|
||||||
fn recoverAudioDevice(self: *SttSession) SttError!void {
|
fn recoverAudioDevice(self: *Session) SttError!void {
|
||||||
if (self.alsa_capture) |*capture| {
|
if (self.alsa_capture) |*capture| {
|
||||||
// Close the current device handle
|
// Close the current device handle
|
||||||
capture.close();
|
capture.close();
|
||||||
|
@ -1079,7 +1079,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reinitialize Vosk recognizer for error recovery
|
/// Reinitialize Vosk recognizer for error recovery
|
||||||
fn reinitializeVosk(self: *SttSession) SttError!void {
|
fn reinitializeVosk(self: *Session) SttError!void {
|
||||||
// Clean up existing Vosk resources
|
// Clean up existing Vosk resources
|
||||||
if (self.vosk_recognizer) |recognizer| {
|
if (self.vosk_recognizer) |recognizer| {
|
||||||
c.vosk_recognizer_free(recognizer);
|
c.vosk_recognizer_free(recognizer);
|
||||||
|
@ -1106,7 +1106,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current session status information
|
/// Get current session status information
|
||||||
pub fn getStatus(self: *SttSession) struct {
|
pub fn getStatus(self: *Session) struct {
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
listening: bool,
|
listening: bool,
|
||||||
audio_samples_available: usize,
|
audio_samples_available: usize,
|
||||||
|
@ -1140,7 +1140,7 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reinitialize the session after an error (recovery mechanism)
|
/// Reinitialize the session after an error (recovery mechanism)
|
||||||
pub fn reinitialize(self: *SttSession) SttError!void {
|
pub fn reinitialize(self: *Session) SttError!void {
|
||||||
if (self.listening) {
|
if (self.listening) {
|
||||||
self.stop_listening();
|
self.stop_listening();
|
||||||
}
|
}
|
||||||
|
@ -1176,7 +1176,7 @@ pub const SttSession = struct {
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// - void on success
|
/// - void on success
|
||||||
/// - SttError on failure
|
/// - SttError on failure
|
||||||
pub fn start_listening(self: *SttSession) SttError!void {
|
pub fn start_listening(self: *Session) SttError!void {
|
||||||
if (!self.initialized) {
|
if (!self.initialized) {
|
||||||
return SttError.InvalidState;
|
return SttError.InvalidState;
|
||||||
}
|
}
|
||||||
|
@ -1226,7 +1226,7 @@ pub const SttSession = struct {
|
||||||
///
|
///
|
||||||
/// This stops audio capture and speech recognition processing.
|
/// This stops audio capture and speech recognition processing.
|
||||||
/// Any ongoing processing will be completed before returning.
|
/// Any ongoing processing will be completed before returning.
|
||||||
pub fn stop_listening(self: *SttSession) void {
|
pub fn stop_listening(self: *Session) void {
|
||||||
if (!self.listening) {
|
if (!self.listening) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1258,12 +1258,12 @@ pub const SttSession = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the session is currently listening
|
/// Check if the session is currently listening
|
||||||
pub fn is_listening(self: *const SttSession) bool {
|
pub fn is_listening(self: *const Session) bool {
|
||||||
return self.listening;
|
return self.listening;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the session is initialized
|
/// Check if the session is initialized
|
||||||
pub fn is_initialized(self: *const SttSession) bool {
|
pub fn is_initialized(self: *const Session) bool {
|
||||||
return self.initialized;
|
return self.initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1271,7 +1271,7 @@ pub const SttSession = struct {
|
||||||
///
|
///
|
||||||
/// This must be called to properly clean up the session.
|
/// This must be called to properly clean up the session.
|
||||||
/// After calling deinit(), the session should not be used.
|
/// After calling deinit(), the session should not be used.
|
||||||
pub fn deinit(self: *SttSession) void {
|
pub fn deinit(self: *Session) void {
|
||||||
// Ensure we're not listening before cleanup
|
// Ensure we're not listening before cleanup
|
||||||
if (self.listening) {
|
if (self.listening) {
|
||||||
self.stop_listening();
|
self.stop_listening();
|
||||||
|
@ -1327,10 +1327,10 @@ pub const SttSession = struct {
|
||||||
/// - options: Configuration options for the session
|
/// - options: Configuration options for the session
|
||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// - SttSession instance on success
|
/// - Session instance on success
|
||||||
/// - SttError on failure
|
/// - SttError on failure
|
||||||
pub fn init(allocator: std.mem.Allocator, options: Options) SttError!SttSession {
|
pub fn init(allocator: std.mem.Allocator, options: Options) SttError!Session {
|
||||||
return SttSession.init(allocator, options);
|
return Session.init(allocator, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// C-compatible API functions for use from other languages
|
/// C-compatible API functions for use from other languages
|
||||||
|
@ -1439,7 +1439,7 @@ test "Options validation" {
|
||||||
try testing.expect(valid_options.buffer_size == 256);
|
try testing.expect(valid_options.buffer_size == 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "SttSession state management" {
|
test "Session state management" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
const DummyHandler = struct {
|
const DummyHandler = struct {
|
||||||
|
@ -1660,7 +1660,7 @@ test "AudioBuffer thread safety" {
|
||||||
try testing.expect(read_buffer[9] == 10);
|
try testing.expect(read_buffer[9] == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "SttSession session management API" {
|
test "Session session management API" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
@ -1715,7 +1715,7 @@ test "SttSession session management API" {
|
||||||
try testing.expectError(SttError.InvalidParameter, invalid_result);
|
try testing.expectError(SttError.InvalidParameter, invalid_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "SttSession status and recovery" {
|
test "Session status and recovery" {
|
||||||
// Skip this test to avoid segfaults during cleanup
|
// Skip this test to avoid segfaults during cleanup
|
||||||
// The test tries to initialize real Vosk models and ALSA devices
|
// The test tries to initialize real Vosk models and ALSA devices
|
||||||
// which can cause segmentation faults during deinit
|
// which can cause segmentation faults during deinit
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Unit tests for STT library components
|
//! Unit tests for STT library components
|
||||||
//!
|
//!
|
||||||
//! This file contains comprehensive tests for:
|
//! This file contains comprehensive tests for:
|
||||||
//! - SttSession initialization and cleanup
|
//! - Session initialization and cleanup
|
||||||
//! - Audio buffer management and threading
|
//! - Audio buffer management and threading
|
||||||
//! - Error handling and recovery mechanisms
|
//! - Error handling and recovery mechanisms
|
||||||
//! - Callback invocation and event handling
|
//! - Callback invocation and event handling
|
||||||
|
@ -253,7 +253,7 @@ test "AudioConverter sample rate conversion" {
|
||||||
try testing.expect(upsampled_count == 4);
|
try testing.expect(upsampled_count == 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "SttSession initialization error handling" {
|
test "Session initialization error handling" {
|
||||||
var test_handler = TestEventHandler.init(test_allocator);
|
var test_handler = TestEventHandler.init(test_allocator);
|
||||||
defer test_handler.deinit();
|
defer test_handler.deinit();
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ test "SttSession initialization error handling" {
|
||||||
try testing.expect(invalid_options.buffer_size == 256);
|
try testing.expect(invalid_options.buffer_size == 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "SttSession mock initialization and cleanup" {
|
test "Session mock initialization and cleanup" {
|
||||||
// Note: This test would require mocking the Vosk and ALSA dependencies
|
// Note: This test would require mocking the Vosk and ALSA dependencies
|
||||||
// For now, we test the structure and error handling paths
|
// For now, we test the structure and error handling paths
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue