mirror of
https://github.com/baketnk/frame-yap.git
synced 2026-10-06 09:00:05 +02:00
61 lines
2.7 KiB
C++
61 lines
2.7 KiB
C++
#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; }
|
|
}
|