mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
commit
b1e062945e
@ -63,6 +63,13 @@ class VSyncPredictor {
|
|||||||
frame_duration_ = Nanos(1'000'000'000.0f / rate);
|
frame_duration_ = Nanos(1'000'000'000.0f / rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns The time this class currently believes a whole frame occupies.
|
||||||
|
*/
|
||||||
|
Time::Nanos frame_duration() {
|
||||||
|
return frame_duration_;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Adds a record of how much jitter was experienced in scheduling; these values will be
|
Adds a record of how much jitter was experienced in scheduling; these values will be
|
||||||
factored into the @c suggested_draw_time if supplied.
|
factored into the @c suggested_draw_time if supplied.
|
||||||
@ -87,15 +94,13 @@ class VSyncPredictor {
|
|||||||
(if those figures are being supplied).
|
(if those figures are being supplied).
|
||||||
*/
|
*/
|
||||||
Nanos suggested_draw_time() {
|
Nanos suggested_draw_time() {
|
||||||
const auto mean = redraw_period_.mean() - timer_jitter_.mean() - vsync_jitter_.mean();
|
const auto mean = redraw_period_.mean() + timer_jitter_.mean() + vsync_jitter_.mean();
|
||||||
const auto variance = redraw_period_.variance() + timer_jitter_.variance() + vsync_jitter_.variance();
|
const auto variance = redraw_period_.variance() + timer_jitter_.variance() + vsync_jitter_.variance();
|
||||||
|
|
||||||
// Permit three standard deviations from the mean, to cover 99.9% of cases.
|
// Permit three standard deviations from the mean, to cover 99.9% of cases.
|
||||||
const auto period = mean - Nanos(3.0f * sqrt(float(variance)));
|
const auto period = mean + Nanos(3.0f * sqrt(float(variance)));
|
||||||
|
|
||||||
assert(abs(period) < 10'000'000'000);
|
return last_vsync_ + frame_duration_ - period;
|
||||||
|
|
||||||
return last_vsync_ + period;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -109,7 +114,6 @@ class VSyncPredictor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void post(Time::Nanos value) {
|
void post(Time::Nanos value) {
|
||||||
assert(abs(value) < 10'000'000'000); // 10 seconds is a very liberal maximum.
|
|
||||||
sum_ -= history_[write_pointer_];
|
sum_ -= history_[write_pointer_];
|
||||||
sum_ += value;
|
sum_ += value;
|
||||||
history_[write_pointer_] = value;
|
history_[write_pointer_] = value;
|
||||||
|
@ -45,7 +45,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
|||||||
|
|
||||||
MainWindow::MainWindow(const QString &fileName) {
|
MainWindow::MainWindow(const QString &fileName) {
|
||||||
init();
|
init();
|
||||||
launchFile(fileName);
|
if(!launchFile(fileName)) {
|
||||||
|
setUIPhase(UIPhase::SelectingMachine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::deleteMachine() {
|
void MainWindow::deleteMachine() {
|
||||||
@ -210,11 +212,17 @@ void MainWindow::insertFile(const QString &fileName) {
|
|||||||
mediaTarget->insert_media(media);
|
mediaTarget->insert_media(media);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::launchFile(const QString &fileName) {
|
bool MainWindow::launchFile(const QString &fileName) {
|
||||||
targets = Analyser::Static::GetTargets(fileName.toStdString());
|
targets = Analyser::Static::GetTargets(fileName.toStdString());
|
||||||
if(!targets.empty()) {
|
if(!targets.empty()) {
|
||||||
openFileName = QFileInfo(fileName).fileName();
|
openFileName = QFileInfo(fileName).fileName();
|
||||||
launchMachine();
|
launchMachine();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setText("Unable to open file: " + fileName);
|
||||||
|
msgBox.exec();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,6 +715,7 @@ void MainWindow::dropEvent(QDropEvent* event) {
|
|||||||
bool foundROM = false;
|
bool foundROM = false;
|
||||||
const auto appDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString();
|
const auto appDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString();
|
||||||
|
|
||||||
|
QString unusedRoms;
|
||||||
for(const auto &url: event->mimeData()->urls()) {
|
for(const auto &url: event->mimeData()->urls()) {
|
||||||
const char *const name = url.toLocalFile().toUtf8();
|
const char *const name = url.toLocalFile().toUtf8();
|
||||||
FILE *const file = fopen(name, "rb");
|
FILE *const file = fopen(name, "rb");
|
||||||
@ -716,6 +725,7 @@ void MainWindow::dropEvent(QDropEvent* event) {
|
|||||||
CRC::CRC32 generator;
|
CRC::CRC32 generator;
|
||||||
const uint32_t crc = generator.compute_crc(*contents);
|
const uint32_t crc = generator.compute_crc(*contents);
|
||||||
|
|
||||||
|
bool wasUsed = false;
|
||||||
for(const auto &rom: missingRoms) {
|
for(const auto &rom: missingRoms) {
|
||||||
if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) {
|
if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) {
|
||||||
foundROM = true;
|
foundROM = true;
|
||||||
@ -731,10 +741,22 @@ void MainWindow::dropEvent(QDropEvent* event) {
|
|||||||
FILE *const target = fopen(destination.c_str(), "wb");
|
FILE *const target = fopen(destination.c_str(), "wb");
|
||||||
fwrite(contents->data(), 1, contents->size(), target);
|
fwrite(contents->data(), 1, contents->size(), target);
|
||||||
fclose(target);
|
fclose(target);
|
||||||
}
|
|
||||||
|
wasUsed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!wasUsed) {
|
||||||
|
if(!unusedRoms.isEmpty()) unusedRoms += ", ";
|
||||||
|
unusedRoms += url.fileName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!unusedRoms.isEmpty()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setText("Couldn't identify ROMs: " + unusedRoms);
|
||||||
|
msgBox.exec();
|
||||||
|
}
|
||||||
if(foundROM) launchMachine();
|
if(foundROM) launchMachine();
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
@ -1338,24 +1360,25 @@ void MainWindow::addActivityObserver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::register_led(const std::string &name) {
|
void MainWindow::register_led(const std::string &name) {
|
||||||
|
std::lock_guard guard(ledStatusesLock);
|
||||||
ledStatuses[name] = false;
|
ledStatuses[name] = false;
|
||||||
updateStatusBarText();
|
QMetaObject::invokeMethod(this, "updateStatusBarText");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::set_led_status(const std::string &name, bool isLit) {
|
void MainWindow::set_led_status(const std::string &name, bool isLit) {
|
||||||
|
std::lock_guard guard(ledStatusesLock);
|
||||||
ledStatuses[name] = isLit;
|
ledStatuses[name] = isLit;
|
||||||
updateStatusBarText(); // Assumption here: Qt's attempt at automatic thread confinement will work here.
|
QMetaObject::invokeMethod(this, "updateStatusBarText");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::updateStatusBarText() {
|
void MainWindow::updateStatusBarText() {
|
||||||
QString fullText;
|
QString fullText;
|
||||||
bool isFirst = true;
|
std::lock_guard guard(ledStatusesLock);
|
||||||
for(const auto &pair: ledStatuses) {
|
for(const auto &pair: ledStatuses) {
|
||||||
if(!isFirst) fullText += " | ";
|
if(!fullText.isEmpty()) fullText += " | ";
|
||||||
fullText += QString::fromStdString(pair.first);
|
fullText += QString::fromStdString(pair.first);
|
||||||
fullText += " ";
|
fullText += " ";
|
||||||
fullText += pair.second ? "■" : "□";
|
fullText += pair.second ? "■" : "□";
|
||||||
isFirst = false;
|
|
||||||
}
|
}
|
||||||
statusBar()->showMessage(fullText);
|
statusBar()->showMessage(fullText);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "audiobuffer.h"
|
#include "audiobuffer.h"
|
||||||
@ -80,6 +81,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void startMachine();
|
void startMachine();
|
||||||
|
void updateStatusBarText();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void start_appleII();
|
void start_appleII();
|
||||||
@ -100,7 +102,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
|||||||
QAction *insertAction = nullptr;
|
QAction *insertAction = nullptr;
|
||||||
void insertFile(const QString &fileName);
|
void insertFile(const QString &fileName);
|
||||||
|
|
||||||
void launchFile(const QString &fileName);
|
bool launchFile(const QString &fileName);
|
||||||
void launchTarget(std::unique_ptr<Analyser::Static::Target> &&);
|
void launchTarget(std::unique_ptr<Analyser::Static::Target> &&);
|
||||||
|
|
||||||
void restoreSelections();
|
void restoreSelections();
|
||||||
@ -144,9 +146,11 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
|||||||
|
|
||||||
void register_led(const std::string &) override;
|
void register_led(const std::string &) override;
|
||||||
void set_led_status(const std::string &, bool) override;
|
void set_led_status(const std::string &, bool) override;
|
||||||
|
|
||||||
|
std::recursive_mutex ledStatusesLock;
|
||||||
std::map<std::string, bool> ledStatuses;
|
std::map<std::string, bool> ledStatuses;
|
||||||
|
|
||||||
void addActivityObserver();
|
void addActivityObserver();
|
||||||
void updateStatusBarText();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
@ -86,7 +86,7 @@ void ScanTargetWidget::vsync() {
|
|||||||
const auto time_now = Time::nanos_now();
|
const auto time_now = Time::nanos_now();
|
||||||
requestedRedrawTime = vsyncPredictor.suggested_draw_time();
|
requestedRedrawTime = vsyncPredictor.suggested_draw_time();
|
||||||
const auto delay_time = (requestedRedrawTime - time_now) / 1'000'000;
|
const auto delay_time = (requestedRedrawTime - time_now) / 1'000'000;
|
||||||
if(delay_time > 0) {
|
if(delay_time > 0 && delay_time < vsyncPredictor.frame_duration()) {
|
||||||
QTimer::singleShot(delay_time, this, SLOT(repaint()));
|
QTimer::singleShot(delay_time, this, SLOT(repaint()));
|
||||||
} else {
|
} else {
|
||||||
requestedRedrawTime = 0;
|
requestedRedrawTime = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user