Audio: robust rate estimation + color-coded log levels
Harden the guessed-stream sample-rate measurement that produced wrong rates (e.g. 44100 read as ~46205). New rate_estimator.hpp measures over longer ~0.5 s windows, rejects any window that doesn't snap to a standard rate (standard rates are >8% apart, so a quantization/burst error big enough to miss one lands in no-man's-land, never on a wrong neighbour), and requires consensus across windows before committing. If consensus isn't reached it commits a low-confidence estimate (new AudioFormat_LowConfidence, shown red) rather than spinning or publishing garbage. Pure logic, unit-tested with adversarial cadences (rate_estimator_test) incl. the real 46205 bug value. Add log severity levels: hook logw/loge set LogRecord.level; the host Log window colors warnings amber and errors red. The low-confidence rate logs a warning. Protocol -> v15 (new format states); also reserves AudioFormat_Override. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,10 @@ const char* audio_format_state_name(std::uint32_t state)
|
||||
return "measuring rate...";
|
||||
case AudioFormat_Measured:
|
||||
return "measured rate (ch/bits assumed)";
|
||||
case AudioFormat_LowConfidence:
|
||||
return "LOW-CONFIDENCE rate (verify / override)";
|
||||
case AudioFormat_Override:
|
||||
return "manual override";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
@@ -55,11 +59,12 @@ ImVec4 audio_format_state_color(std::uint32_t state)
|
||||
{
|
||||
case AudioFormat_Exact:
|
||||
case AudioFormat_Measured:
|
||||
case AudioFormat_Override:
|
||||
return kGreen; // format trustworthy -> correct pitch
|
||||
case AudioFormat_Measuring:
|
||||
return kAmber; // still verifying the rate
|
||||
default:
|
||||
return kRed; // unknown
|
||||
return kRed; // unknown or low-confidence -> needs attention
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ void LogPanel::add_line(const LogRecord& rec)
|
||||
|
||||
char buf[256];
|
||||
std::snprintf(buf, sizeof(buf), "[%8.3f] %s", secs, rec.text);
|
||||
lines_.emplace_back(buf);
|
||||
lines_.push_back({buf, rec.level});
|
||||
while (lines_.size() > kMaxLines)
|
||||
{
|
||||
lines_.pop_front();
|
||||
@@ -53,13 +53,24 @@ void LogPanel::draw()
|
||||
if (ImGui::BeginChild("loglines", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar))
|
||||
{
|
||||
const bool has_filter = filter_[0] != '\0';
|
||||
for (const std::string& line : lines_)
|
||||
for (const Line& line : lines_)
|
||||
{
|
||||
if (has_filter && line.find(filter_) == std::string::npos)
|
||||
if (has_filter && line.text.find(filter_) == std::string::npos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ImGui::TextUnformatted(line.c_str());
|
||||
switch (line.level)
|
||||
{
|
||||
case LogLevel_Warn:
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.3f, 1.0f), "%s", line.text.c_str()); // amber
|
||||
break;
|
||||
case LogLevel_Error:
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.45f, 0.4f, 1.0f), "%s", line.text.c_str()); // red
|
||||
break;
|
||||
default:
|
||||
ImGui::TextUnformatted(line.text.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Stick to the bottom while new lines arrive (unless the user scrolled up).
|
||||
if (autoscroll_ && ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 1.0f)
|
||||
|
||||
@@ -25,7 +25,12 @@ public:
|
||||
private:
|
||||
void add_line(const LogRecord& rec);
|
||||
|
||||
std::deque<std::string> lines_;
|
||||
struct Line
|
||||
{
|
||||
std::string text;
|
||||
std::uint32_t level; // LogLevel, for colouring
|
||||
};
|
||||
std::deque<Line> lines_;
|
||||
char filter_[96] = {};
|
||||
bool autoscroll_ = true;
|
||||
std::uint64_t first_millis_ = 0; // hook clock at the first line, for relative timestamps
|
||||
|
||||
Reference in New Issue
Block a user