1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Thin public `transcribe_file` / `transcribe_bytes` wrappers.
use super::*;
impl Engine {
/// Transcribe an audio file to text (supports WAV, MP3, M4A/AAC, OGG, FLAC).
///
/// Decodes the file to mono 16kHz, runs the full encoder+decoder pipeline,
/// and returns the recognized text with word-level details and duration.
///
/// Thin wrapper over [`Engine::transcribe_request`].
///
/// # Errors
///
/// Returns [`GigasttError::InvalidAudio`] if the file cannot be decoded, or
/// [`GigasttError::Inference`] if the ONNX runtime fails.
#[cfg(feature = "file-decode")]
pub fn transcribe_file(
&self,
path: &str,
triplet: &mut SessionTriplet,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_request(
TranscribeRequest::new(TranscribeSource::Path(path)),
triplet,
)
}
/// Like [`Engine::transcribe_file`] but applies per-request recognition-knob
/// [`overrides`](TranscribeOverrides). With `TranscribeOverrides::default()`
/// this is byte-for-byte [`Engine::transcribe_file`]; the plain method
/// delegates here so binding call sites (FFI / UniFFI / Node) keep the
/// no-override signature unchanged.
#[cfg(feature = "file-decode")]
pub fn transcribe_file_with_overrides(
&self,
path: &str,
triplet: &mut SessionTriplet,
overrides: &TranscribeOverrides,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_file_with_overrides_hotwords(path, triplet, overrides, None)
}
/// Like [`Engine::transcribe_file_with_overrides`] with optional per-request
/// [`HotwordOverride`] (semver-additive sibling so the no-hotwords signature
/// stays byte-stable).
#[cfg(feature = "file-decode")]
pub fn transcribe_file_with_overrides_hotwords(
&self,
path: &str,
triplet: &mut SessionTriplet,
overrides: &TranscribeOverrides,
hotwords: Option<&HotwordOverride>,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_request(
TranscribeRequest::new(TranscribeSource::Path(path))
.with_overrides(*overrides)
.with_hotwords(hotwords),
triplet,
)
}
/// Transcribe audio from raw bytes in memory (no temp file needed).
///
/// Backwards-compatible shim: clones `data` into a [`bytes::Bytes`] and
/// delegates to [`Engine::transcribe_bytes_shared`]. Prefer the shared
/// variant on hot paths (REST/SSE) to avoid the extra copy.
#[cfg(feature = "file-decode")]
pub fn transcribe_bytes(
&self,
data: &[u8],
triplet: &mut SessionTriplet,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_bytes_shared(bytes::Bytes::copy_from_slice(data), triplet)
}
/// Transcribe audio from a reference-counted [`bytes::Bytes`] buffer
/// without cloning.
///
/// Reuses the same decode/inference pipeline as [`Engine::transcribe_bytes`]
/// but hands the buffer straight to symphonia via [`audio::decode_audio_bytes_shared`].
/// This is the zero-copy entry point used by the REST upload handler so a
/// 50 MiB `axum::body::Bytes` body stays as a single in-memory buffer
/// instead of being cloned into a `Vec<u8>` before decode.
#[cfg(feature = "file-decode")]
pub fn transcribe_bytes_shared(
&self,
data: bytes::Bytes,
triplet: &mut SessionTriplet,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_bytes_shared_with_overrides(data, triplet, &TranscribeOverrides::default())
}
/// Like [`Engine::transcribe_bytes_shared`] but applies per-request
/// recognition-knob [`overrides`](TranscribeOverrides). With
/// `TranscribeOverrides::default()` this is byte-for-byte
/// [`Engine::transcribe_bytes_shared`]; the plain method delegates here so
/// the zero-copy REST call site can opt into overrides without changing the
/// no-override signature that other callers rely on.
#[cfg(feature = "file-decode")]
pub fn transcribe_bytes_shared_with_overrides(
&self,
data: bytes::Bytes,
triplet: &mut SessionTriplet,
overrides: &TranscribeOverrides,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_bytes_shared_with_overrides_hotwords(data, triplet, overrides, None)
}
/// Like [`Engine::transcribe_bytes_shared_with_overrides`] with optional
/// per-request [`HotwordOverride`].
#[cfg(feature = "file-decode")]
pub fn transcribe_bytes_shared_with_overrides_hotwords(
&self,
data: bytes::Bytes,
triplet: &mut SessionTriplet,
overrides: &TranscribeOverrides,
hotwords: Option<&HotwordOverride>,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_request(
TranscribeRequest::new(TranscribeSource::Bytes(data))
.with_overrides(*overrides)
.with_hotwords(hotwords),
triplet,
)
}
/// Like [`Engine::transcribe_bytes_shared_with_overrides`], but also runs
/// offline speaker diarization (labels each word's `speaker`) when a speaker
/// encoder is loaded. Diarization is **opt-in per request** (`?diarization=true`
/// on the REST surface): the non-diarized transcribe methods never label
/// speakers, so a plain transcript — and the `channels=split` dual-mono
/// fallback — carries no speaker labels. Without the `diarization` feature or a
/// loaded speaker encoder this is byte-for-byte the non-diarized method.
#[cfg(feature = "file-decode")]
pub fn transcribe_bytes_shared_with_overrides_diarized(
&self,
data: bytes::Bytes,
triplet: &mut SessionTriplet,
overrides: &TranscribeOverrides,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_bytes_shared_with_overrides_diarized_hotwords(
data, triplet, overrides, None,
)
}
/// Like [`Engine::transcribe_bytes_shared_with_overrides_diarized`] with
/// optional per-request [`HotwordOverride`].
#[cfg(feature = "file-decode")]
pub fn transcribe_bytes_shared_with_overrides_diarized_hotwords(
&self,
data: bytes::Bytes,
triplet: &mut SessionTriplet,
overrides: &TranscribeOverrides,
hotwords: Option<&HotwordOverride>,
) -> Result<TranscribeResult, GigasttError> {
self.transcribe_request(
TranscribeRequest::new(TranscribeSource::Bytes(data))
.with_overrides(*overrides)
.with_hotwords(hotwords)
.with_diarization(true),
triplet,
)
}
}