mirror of
https://github.com/baketnk/frame-yap.git
synced 2026-10-06 13:00:04 +02:00
Add bounded dictation state and persistent offline Redux worker
This commit is contained in:
1 parent
f79db54ca9
commit
0cb2991f02
13 files changed
+1132
No files matched your search
@@ -0,0 +1,60 @@
|
||||
#include "core.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace frameyap {
|
||||
std::string literal_text(std::string_view input) {
|
||||
if (input.size() > 4096) throw std::runtime_error("Transcript exceeds 4096 bytes");
|
||||
std::string out;
|
||||
for (std::size_t i = 0; i < input.size();) {
|
||||
const auto start = i;
|
||||
const auto first = static_cast<unsigned char>(input[i++]);
|
||||
uint32_t cp = first;
|
||||
int continuation = 0;
|
||||
uint32_t minimum = 0;
|
||||
if (first >= 0xc2 && first <= 0xdf) { cp = first & 31; continuation = 1; minimum = 0x80; }
|
||||
else if (first >= 0xe0 && first <= 0xef) { cp = first & 15; continuation = 2; minimum = 0x800; }
|
||||
else if (first >= 0xf0 && first <= 0xf4) { cp = first & 7; continuation = 3; minimum = 0x10000; }
|
||||
else if (first >= 0x80) throw std::runtime_error("Invalid UTF-8 transcript");
|
||||
while (continuation--) {
|
||||
if (i == input.size()) throw std::runtime_error("Truncated UTF-8 transcript");
|
||||
auto byte = static_cast<unsigned char>(input[i++]);
|
||||
if ((byte & 0xc0) != 0x80) throw std::runtime_error("Invalid UTF-8 transcript");
|
||||
cp = (cp << 6) | (byte & 63);
|
||||
}
|
||||
if (cp < minimum || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff))
|
||||
throw std::runtime_error("Invalid UTF-8 codepoint");
|
||||
if (cp == '\n' || cp == '\r' || cp == '\t' || cp == 0x2028 || cp == 0x2029) {
|
||||
out += ' ';
|
||||
} else {
|
||||
if (cp < 32 || (cp >= 0x7f && cp <= 0x9f))
|
||||
throw std::runtime_error("Control characters rejected");
|
||||
out.append(input.substr(start, i - start));
|
||||
}
|
||||
}
|
||||
if (out.find_first_not_of(' ') == std::string::npos) out.clear();
|
||||
return out;
|
||||
}
|
||||
void Session::ready() { if (state_ == State::Warming) state_ = State::Ready; }
|
||||
bool Session::record() {
|
||||
if (state_ != State::Ready && state_ != State::Queued) return false;
|
||||
++id_; text_.clear(); state_ = State::Recording; return true;
|
||||
}
|
||||
bool Session::finish(std::size_t samples) {
|
||||
if (state_ != State::Recording) return false;
|
||||
if (samples < 3200 || samples > 320000) { cancel(); return false; }
|
||||
state_ = State::Transcribing; return true;
|
||||
}
|
||||
bool Session::reply(uint64_t id, std::string_view text) {
|
||||
if (id != id_ || state_ != State::Transcribing) return false;
|
||||
text_ = literal_text(text);
|
||||
state_ = text_.empty() ? State::Ready : State::Review;
|
||||
return true;
|
||||
}
|
||||
std::optional<std::string> Session::take_insert() {
|
||||
if (state_ != State::Review) return std::nullopt;
|
||||
auto text = std::move(text_); text_.clear(); state_ = State::Queued;
|
||||
return text;
|
||||
}
|
||||
void Session::cancel() { ++id_; text_.clear(); state_ = State::Ready; }
|
||||
void Session::fail() { ++id_; text_.clear(); state_ = State::Error; }
|
||||
}
|
||||
Reference in new issue
Block a user