| @@ -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); | |||