@@ -50,20 +50,20 @@ struct Engine { | |||
/** Returns the estimated timestamp corresponding to the current frame, based on the timestamp of when step() was last called. | |||
Calculated by `stepTime + framesSinceStep / sampleRate`. | |||
*/ | |||
int64_t getFrameTime(); | |||
double getFrameTime(); | |||
/** Returns the frame when step() was last called. | |||
*/ | |||
int64_t getStepFrame(); | |||
/** Returns the timestamp in nanoseconds when step() was last called. | |||
/** Returns the timestamp in seconds when step() was last called. | |||
*/ | |||
int64_t getStepTime(); | |||
double getStepTime(); | |||
/** Returns the total number of frames in the current step() call. | |||
*/ | |||
int getStepFrames(); | |||
/** Returns the total time that step() is advancing, in nanoseconds. | |||
/** Returns the total time that step() is advancing, in seconds. | |||
Calculated by `stepFrames / sampleRate`. | |||
*/ | |||
int64_t getStepDuration(); | |||
double getStepDuration(); | |||
// Modules | |||
size_t getNumModules(); | |||
@@ -20,8 +20,8 @@ namespace midi { | |||
struct Message { | |||
/** Initialized to 3 empty bytes. */ | |||
std::vector<uint8_t> bytes; | |||
/** Timestamp of MIDI message in nanoseconds. Negative if not set. */ | |||
int64_t timestamp = -1; | |||
/** Timestamp of MIDI message in nanoseconds. NAN if not set. */ | |||
double timestamp = NAN; | |||
Message() : bytes(3) {} | |||
@@ -131,11 +131,10 @@ void setThreadName(const std::string& name); | |||
/** Returns the caller's human-readable stack trace with "\n"-separated lines. */ | |||
std::string getStackTrace(); | |||
/** Returns the current number of nanoseconds since the epoch. | |||
The goal of this function is to give the most precise (fine-grained) time available on the OS for benchmarking purposes, while being fast to compute. | |||
The epoch is undefined. Do not use this function to get absolute time, as it is different on each OS. | |||
/** Returns the number of seconds since application launch. | |||
The goal of this function is to give the most precise (fine-grained) time differences available on the OS for benchmarking purposes, while being fast to compute. | |||
*/ | |||
int64_t getNanoseconds(); | |||
double getTime(); | |||
std::string getOperatingSystemInfo(); | |||
// Applications | |||
@@ -152,6 +151,8 @@ The launched process will continue running if the current process is closed. | |||
*/ | |||
void runProcessDetached(const std::string& path); | |||
void init(); | |||
} // namespace system | |||
} // namespace rack |
@@ -7,7 +7,7 @@ namespace core { | |||
struct CCMidiOutput : midi::Output { | |||
int lastValues[128]; | |||
int64_t timestamp = -1; | |||
double timestamp = 0.0; | |||
CCMidiOutput() { | |||
reset(); | |||
@@ -33,7 +33,7 @@ struct CCMidiOutput : midi::Output { | |||
sendMessage(m); | |||
} | |||
void setTimestamp(int64_t timestamp) { | |||
void setTimestamp(double timestamp) { | |||
this->timestamp = timestamp; | |||
} | |||
}; | |||
@@ -8,7 +8,7 @@ namespace core { | |||
struct GateMidiOutput : midi::Output { | |||
int vels[128]; | |||
bool lastGates[128]; | |||
int64_t timestamp = -1; | |||
double timestamp = 0.0; | |||
GateMidiOutput() { | |||
reset(); | |||
@@ -62,7 +62,7 @@ struct GateMidiOutput : midi::Output { | |||
lastGates[note] = gate; | |||
} | |||
void setTimestamp(int64_t timestamp) { | |||
void setTimestamp(double timestamp) { | |||
this->timestamp = timestamp; | |||
} | |||
}; | |||
@@ -198,7 +198,7 @@ struct Engine::Internal { | |||
float sampleTime = 0.f; | |||
int64_t frame = 0; | |||
int64_t stepFrame = 0; | |||
int64_t stepTime = 0; | |||
double stepTime = 0.0; | |||
int stepFrames = 0; | |||
Module* primaryModule = NULL; | |||
@@ -338,9 +338,9 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||
Module* module = internal->modules[i]; | |||
// Start CPU timer | |||
int64_t startTime; | |||
double startTime; | |||
if (cpuMeter) { | |||
startTime = system::getNanoseconds(); | |||
startTime = system::getTime(); | |||
} | |||
// Step module | |||
@@ -351,8 +351,8 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) { | |||
// Stop CPU timer | |||
if (cpuMeter) { | |||
int64_t endTime = system::getNanoseconds(); | |||
float duration = (endTime - startTime) * 1e-9f; | |||
double endTime = system::getTime(); | |||
float duration = (endTime - startTime) / 1e9; | |||
// Smooth CPU time | |||
const float cpuTau = 2.f /* seconds */; | |||
@@ -564,7 +564,7 @@ void Engine::step(int frames) { | |||
random::init(); | |||
internal->stepFrame = internal->frame; | |||
internal->stepTime = system::getNanoseconds(); | |||
internal->stepTime = system::getTime(); | |||
internal->stepFrames = frames; | |||
// Set sample rate | |||
@@ -635,9 +635,9 @@ int64_t Engine::getFrame() { | |||
} | |||
int64_t Engine::getFrameTime() { | |||
double Engine::getFrameTime() { | |||
double timeSinceStep = (internal->frame - internal->stepFrame) * internal->sampleTime; | |||
return internal->stepTime + int64_t(timeSinceStep * 1e9); | |||
return internal->stepTime + timeSinceStep; | |||
} | |||
@@ -646,7 +646,7 @@ int64_t Engine::getStepFrame() { | |||
} | |||
int64_t Engine::getStepTime() { | |||
double Engine::getStepTime() { | |||
return internal->stepTime; | |||
} | |||
@@ -656,9 +656,8 @@ int Engine::getStepFrames() { | |||
} | |||
int64_t Engine::getStepDuration() { | |||
double duration = internal->stepFrames * internal->sampleTime; | |||
return int64_t(duration * 1e9); | |||
double Engine::getStepDuration() { | |||
return internal->stepFrames * internal->sampleTime; | |||
} | |||
@@ -12,12 +12,12 @@ namespace logger { | |||
static FILE* outputFile = NULL; | |||
static int64_t startTime = 0; | |||
static double startTime = 0.0; | |||
static std::mutex logMutex; | |||
void init() { | |||
startTime = system::getNanoseconds(); | |||
startTime = system::getTime(); | |||
// Don't open a file in development mode. | |||
if (settings::devMode) { | |||
outputFile = stderr; | |||
@@ -64,8 +64,8 @@ static void logVa(Level level, const char* filename, int line, const char* func, | |||
if (!outputFile) | |||
return; | |||
int64_t nowTime = system::getNanoseconds(); | |||
double duration = (nowTime - startTime) / 1e9; | |||
double nowTime = system::getTime(); | |||
double duration = nowTime - startTime; | |||
if (outputFile == stderr) | |||
std::fprintf(outputFile, "\x1B[%dm", levelColors[level]); | |||
std::fprintf(outputFile, "[%.03f %s %s:%d %s] ", duration, levelLabels[level], filename, line, func); | |||
@@ -30,8 +30,8 @@ void InputDevice::unsubscribe(Input* input) { | |||
void InputDevice::onMessage(const Message &message) { | |||
// Set timestamp to now if unset | |||
Message msg = message; | |||
if (msg.timestamp < 0) | |||
msg.timestamp = system::getNanoseconds(); | |||
if (msg.timestamp == 0.0) | |||
msg.timestamp = system::getTime(); | |||
for (Input* input : subscribed) { | |||
// We're probably in the MIDI driver's thread, so set the Rack context. | |||
@@ -69,11 +69,11 @@ void PatchManager::save(std::string path) { | |||
// Take screenshot (disabled because there is currently no way to quickly view them on any OS or website.) | |||
// APP->window->screenshot(system::join(asset::autosavePath, "screenshot.png")); | |||
uint64_t startTime = system::getNanoseconds(); | |||
double startTime = system::getTime(); | |||
// Set compression level to 1 so that a 500MB/s SSD is almost bottlenecked | |||
system::archiveFolder(path, asset::autosavePath, 1); | |||
uint64_t endTime = system::getNanoseconds(); | |||
INFO("Archived patch in %lf seconds", (endTime - startTime) / 1e9); | |||
double endTime = system::getTime(); | |||
INFO("Archived patch in %lf seconds", (endTime - startTime)); | |||
} | |||
@@ -222,10 +222,10 @@ void PatchManager::load(std::string path) { | |||
} | |||
else { | |||
// Extract the .vcv file as a .tar.zst archive. | |||
uint64_t startTime = system::getNanoseconds(); | |||
double startTime = system::getTime(); | |||
system::unarchiveToFolder(path, asset::autosavePath); | |||
uint64_t endTime = system::getNanoseconds(); | |||
INFO("Unarchived patch in %lf seconds", (endTime - startTime) / 1e9); | |||
double endTime = system::getTime(); | |||
INFO("Unarchived patch in %lf seconds", (endTime - startTime)); | |||
} | |||
loadAutosave(); | |||
@@ -148,12 +148,14 @@ struct RtMidiOutputDevice : midi::OutputDevice { | |||
else { | |||
// Get earliest message | |||
const midi::Message& message = messageQueue.top(); | |||
int64_t duration = message.timestamp - system::getNanoseconds(); | |||
double duration = message.timestamp - system::getTime(); | |||
// If we need to wait, release the lock and wait for the timeout, or if the CV is notified. | |||
// This correctly handles MIDI messages with no timestamp, because duration will be negative. | |||
if ((duration > 0) && cv.wait_for(lock, std::chrono::nanoseconds(duration)) != std::cv_status::timeout) | |||
continue; | |||
// This correctly handles MIDI messages with no timestamp, because duration will be NAN. | |||
if (duration > 0) { | |||
if (cv.wait_for(lock, std::chrono::duration<double>(duration)) != std::cv_status::timeout) | |||
continue; | |||
} | |||
// Send and remove from queue | |||
sendMessageNow(message); | |||
@@ -18,6 +18,8 @@ | |||
#if defined ARCH_MAC | |||
#include <mach/mach_init.h> | |||
#include <mach/thread_act.h> | |||
#include <mach/clock.h> | |||
#include <mach/mach.h> | |||
#endif | |||
#if defined ARCH_WIN | |||
@@ -526,30 +528,62 @@ std::string getStackTrace() { | |||
} | |||
int64_t getNanoseconds() { | |||
#if defined ARCH_WIN | |||
static int64_t startCounter = 0; | |||
static double counterTime = 0.0; | |||
#endif | |||
#if defined ARCH_LIN || defined ARCH_MAC | |||
static int64_t startTime = 0; | |||
#endif | |||
static void initTime() { | |||
#if defined ARCH_WIN | |||
assert(startCounter == 0); | |||
LARGE_INTEGER counter; | |||
QueryPerformanceCounter(&counter); | |||
startCounter = counter.QuadPart; | |||
LARGE_INTEGER frequency; | |||
QueryPerformanceFrequency(&frequency); | |||
// TODO Check if this is always an integer factor on all CPUs | |||
int64_t nsPerTick = 1000000000LL / frequency.QuadPart; | |||
int64_t time = counter.QuadPart * nsPerTick; | |||
return time; | |||
counterTime = 1.0 / frequency.QuadPart; | |||
#endif | |||
#if defined ARCH_LIN | |||
assert(startTime == 0); | |||
struct timespec ts; | |||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts); | |||
startTime = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec; | |||
#endif | |||
#if defined ARCH_MAC | |||
assert(startTime == 0); | |||
clock_serv_t cclock; | |||
mach_timespec_t mts; | |||
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); | |||
clock_get_time(cclock, &mts); | |||
mach_port_deallocate(mach_task_self(), cclock); | |||
startTime = int64_t(mts.tv_sec) * 1000000000LL + mts.tv_nsec; | |||
#endif | |||
} | |||
double getTime() { | |||
#if defined ARCH_WIN | |||
LARGE_INTEGER counter; | |||
QueryPerformanceCounter(&counter); | |||
return (counter.QuadPart - startCounter) * counterTime; | |||
#endif | |||
#if defined ARCH_LIN | |||
struct timespec ts; | |||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts); | |||
int64_t time = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec; | |||
return time; | |||
return (time - startTime) / 1e9; | |||
#endif | |||
#if defined ARCH_MAC | |||
using clock = std::chrono::high_resolution_clock; | |||
using time_point = std::chrono::time_point<clock>; | |||
time_point now = clock::now(); | |||
using duration = std::chrono::duration<int64_t, std::nano>; | |||
duration d = now.time_since_epoch(); | |||
return d.count(); | |||
clock_serv_t cclock; | |||
mach_timespec_t mts; | |||
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); | |||
clock_get_time(cclock, &mts); | |||
mach_port_deallocate(mach_task_self(), cclock); | |||
int64_t time = int64_t(mts.tv_sec) * 1000000000LL + mts.tv_nsec; | |||
return (time - startTime) / 1e9; | |||
#endif | |||
} | |||
@@ -621,5 +655,10 @@ void runProcessDetached(const std::string& path) { | |||
} | |||
void init() { | |||
initTime(); | |||
} | |||
} // namespace system | |||
} // namespace rack |
@@ -108,10 +108,15 @@ int main(int argc, char* argv[]) { | |||
} | |||
// Initialize environment | |||
system::init(); | |||
asset::init(); | |||
bool loggerWasTruncated = logger::isTruncated(); | |||
logger::init(); | |||
for (int i = 0; i < 100; i++) | |||
DEBUG("%lf", system::getTime() * 1e9); | |||
exit(0); | |||
// We can now install a signal handler and log the output | |||
if (!settings::devMode) { | |||
signal(SIGABRT, fatalSignalHandler); | |||