| @@ -27,7 +27,7 @@ struct CableWidget : widget::OpaqueWidget { | |||||
| math::Vec getOutputPos(); | math::Vec getOutputPos(); | ||||
| math::Vec getInputPos(); | math::Vec getInputPos(); | ||||
| json_t *toJson(); | json_t *toJson(); | ||||
| void fromJson(json_t *rootJ, const std::map<int, ModuleWidget*> &moduleWidgets); | |||||
| void fromJson(json_t *rootJ); | |||||
| void draw(const widget::DrawContext &ctx) override; | void draw(const widget::DrawContext &ctx) override; | ||||
| void drawPlugs(const widget::DrawContext &ctx); | void drawPlugs(const widget::DrawContext &ctx); | ||||
| }; | }; | ||||
| @@ -23,75 +23,6 @@ inline float sinc(float x) { | |||||
| return std::sin(x) / x; | return std::sin(x) / x; | ||||
| } | } | ||||
| // Window functions | |||||
| /** Hann window function | |||||
| p: proportion from [0, 1], usually `i / (len - 1)` | |||||
| https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows | |||||
| */ | |||||
| inline float hann(float p) { | |||||
| return 0.5f * (1.f - std::cos(2*M_PI * p)); | |||||
| } | |||||
| /** Applies the Hann window to a signal `x` */ | |||||
| inline void hannWindow(float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | |||||
| x[i] *= hann((float) i / (len - 1)); | |||||
| } | |||||
| } | |||||
| /** Blackman window function | |||||
| https://en.wikipedia.org/wiki/Window_function#Blackman_window | |||||
| A typical alpha value is 0.16. | |||||
| */ | |||||
| inline float blackman(float alpha, float p) { | |||||
| return | |||||
| + (1 - alpha) / 2.f | |||||
| - 1 / 2.f * std::cos(2*M_PI * p) | |||||
| + alpha / 2.f * std::cos(4*M_PI * p); | |||||
| } | |||||
| inline void blackmanWindow(float alpha, float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | |||||
| x[i] *= blackman(alpha, (float) i / (len - 1)); | |||||
| } | |||||
| } | |||||
| /** Blackman-Nuttall window function | |||||
| https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Nuttall_window | |||||
| */ | |||||
| inline float blackmanNuttall(float p) { | |||||
| return | |||||
| + 0.3635819f | |||||
| - 0.4891775f * std::cos(2*M_PI * p) | |||||
| + 0.1365995f * std::cos(4*M_PI * p) | |||||
| - 0.0106411f * std::cos(6*M_PI * p); | |||||
| } | |||||
| inline void blackmanNuttallWindow(float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | |||||
| x[i] *= blackmanNuttall((float) i / (len - 1)); | |||||
| } | |||||
| } | |||||
| /** Blackman-Harris window function | |||||
| https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window | |||||
| */ | |||||
| inline float blackmanHarris(float p) { | |||||
| return | |||||
| + 0.35875f | |||||
| - 0.48829f * std::cos(2*M_PI * p) | |||||
| + 0.14128f * std::cos(4*M_PI * p) | |||||
| - 0.01168f * std::cos(6*M_PI * p); | |||||
| } | |||||
| inline void blackmanHarrisWindow(float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | |||||
| x[i] *= blackmanHarris((float) i / (len - 1)); | |||||
| } | |||||
| } | |||||
| // Conversion functions | // Conversion functions | ||||
| inline float amplitudeToDb(float amp) { | inline float amplitudeToDb(float amp) { | ||||
| @@ -3,6 +3,7 @@ | |||||
| #include "dsp/frame.hpp" | #include "dsp/frame.hpp" | ||||
| #include "dsp/ringbuffer.hpp" | #include "dsp/ringbuffer.hpp" | ||||
| #include "dsp/fir.hpp" | #include "dsp/fir.hpp" | ||||
| #include "dsp/window.hpp" | |||||
| #include <assert.h> | #include <assert.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <speex/speex_resampler.h> | #include <speex/speex_resampler.h> | ||||
| @@ -0,0 +1,78 @@ | |||||
| #pragma once | |||||
| #include "math.hpp" | |||||
| namespace rack { | |||||
| namespace dsp { | |||||
| /** Hann window function | |||||
| p: proportion from [0, 1], usually `i / (len - 1)` | |||||
| https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows | |||||
| */ | |||||
| inline float hann(float p) { | |||||
| return 0.5f * (1.f - std::cos(2*M_PI * p)); | |||||
| } | |||||
| /** Applies the Hann window to a signal `x` */ | |||||
| inline void hannWindow(float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | |||||
| x[i] *= hann((float) i / (len - 1)); | |||||
| } | |||||
| } | |||||
| /** Blackman window function | |||||
| https://en.wikipedia.org/wiki/Window_function#Blackman_window | |||||
| A typical alpha value is 0.16. | |||||
| */ | |||||
| inline float blackman(float alpha, float p) { | |||||
| return | |||||
| + (1 - alpha) / 2.f | |||||
| - 1 / 2.f * std::cos(2*M_PI * p) | |||||
| + alpha / 2.f * std::cos(4*M_PI * p); | |||||
| } | |||||
| inline void blackmanWindow(float alpha, float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | |||||
| x[i] *= blackman(alpha, (float) i / (len - 1)); | |||||
| } | |||||
| } | |||||
| /** Blackman-Nuttall window function | |||||
| https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Nuttall_window | |||||
| */ | |||||
| inline float blackmanNuttall(float p) { | |||||
| return | |||||
| + 0.3635819f | |||||
| - 0.4891775f * std::cos(2*M_PI * p) | |||||
| + 0.1365995f * std::cos(4*M_PI * p) | |||||
| - 0.0106411f * std::cos(6*M_PI * p); | |||||
| } | |||||
| inline void blackmanNuttallWindow(float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | |||||
| x[i] *= blackmanNuttall((float) i / (len - 1)); | |||||
| } | |||||
| } | |||||
| /** Blackman-Harris window function | |||||
| https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window | |||||
| */ | |||||
| inline float blackmanHarris(float p) { | |||||
| return | |||||
| + 0.35875f | |||||
| - 0.48829f * std::cos(2*M_PI * p) | |||||
| + 0.14128f * std::cos(4*M_PI * p) | |||||
| - 0.01168f * std::cos(6*M_PI * p); | |||||
| } | |||||
| inline void blackmanHarrisWindow(float *x, int len) { | |||||
| for (int i = 0; i < len; i++) { | |||||
| x[i] *= blackmanHarris((float) i / (len - 1)); | |||||
| } | |||||
| } | |||||
| } // namespace dsp | |||||
| } // namespace rack | |||||
| @@ -8,7 +8,7 @@ namespace engine { | |||||
| struct Cable { | struct Cable { | ||||
| int id = 0; | |||||
| int id = -1; | |||||
| Module *outputModule = NULL; | Module *outputModule = NULL; | ||||
| int outputId; | int outputId; | ||||
| Module *inputModule = NULL; | Module *inputModule = NULL; | ||||
| @@ -14,7 +14,7 @@ namespace engine { | |||||
| struct Module { | struct Module { | ||||
| /** Automatically generated by the engine. */ | /** Automatically generated by the engine. */ | ||||
| int id = 0; | |||||
| int id = -1; | |||||
| std::vector<Param> params; | std::vector<Param> params; | ||||
| std::vector<Output> outputs; | std::vector<Output> outputs; | ||||
| std::vector<Input> inputs; | std::vector<Input> inputs; | ||||
| @@ -0,0 +1,18 @@ | |||||
| #pragma once | |||||
| #include "common.hpp" | |||||
| #include "math.hpp" | |||||
| #include <jansson.h> | |||||
| namespace rack { | |||||
| namespace engine { | |||||
| struct ParamMap { | |||||
| int moduleId; | |||||
| int paramId; | |||||
| }; | |||||
| } // namespace engine | |||||
| } // namespace rack | |||||
| @@ -88,6 +88,7 @@ | |||||
| #include "dsp/resampler.hpp" | #include "dsp/resampler.hpp" | ||||
| #include "dsp/ringbuffer.hpp" | #include "dsp/ringbuffer.hpp" | ||||
| #include "dsp/vumeter.hpp" | #include "dsp/vumeter.hpp" | ||||
| #include "dsp/window.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -146,9 +146,6 @@ json_t *CableWidget::toJson() { | |||||
| assert(isComplete()); | assert(isComplete()); | ||||
| json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
| // This is just here for fun. It is not used in fromJson(), since cableIds are not preserved across multiple launches of Rack. | |||||
| json_object_set_new(rootJ, "id", json_integer(cable->id)); | |||||
| json_object_set_new(rootJ, "outputModuleId", json_integer(cable->outputModule->id)); | json_object_set_new(rootJ, "outputModuleId", json_integer(cable->outputModule->id)); | ||||
| json_object_set_new(rootJ, "outputId", json_integer(cable->outputId)); | json_object_set_new(rootJ, "outputId", json_integer(cable->outputId)); | ||||
| json_object_set_new(rootJ, "inputModuleId", json_integer(cable->inputModule->id)); | json_object_set_new(rootJ, "inputModuleId", json_integer(cable->inputModule->id)); | ||||
| @@ -160,20 +157,30 @@ json_t *CableWidget::toJson() { | |||||
| return rootJ; | return rootJ; | ||||
| } | } | ||||
| void CableWidget::fromJson(json_t *rootJ, const std::map<int, ModuleWidget*> &moduleWidgets) { | |||||
| int outputModuleId = json_integer_value(json_object_get(rootJ, "outputModuleId")); | |||||
| int outputId = json_integer_value(json_object_get(rootJ, "outputId")); | |||||
| int inputModuleId = json_integer_value(json_object_get(rootJ, "inputModuleId")); | |||||
| int inputId = json_integer_value(json_object_get(rootJ, "inputId")); | |||||
| // Get module widgets | |||||
| auto outputModuleIt = moduleWidgets.find(outputModuleId); | |||||
| auto inputModuleIt = moduleWidgets.find(inputModuleId); | |||||
| if (outputModuleIt == moduleWidgets.end() || inputModuleIt == moduleWidgets.end()) | |||||
| return; | |||||
| ModuleWidget *outputModule = outputModuleIt->second; | |||||
| ModuleWidget *inputModule = inputModuleIt->second; | |||||
| void CableWidget::fromJson(json_t *rootJ) { | |||||
| // outputModuleId | |||||
| json_t *outputModuleIdJ = json_object_get(rootJ, "outputModuleId"); | |||||
| if (!outputModuleIdJ) return; | |||||
| int outputModuleId = json_integer_value(outputModuleIdJ); | |||||
| ModuleWidget *outputModule = APP->scene->rackWidget->getModule(outputModuleId); | |||||
| if (!outputModule) return; | |||||
| // inputModuleId | |||||
| json_t *inputModuleIdJ = json_object_get(rootJ, "inputModuleId"); | |||||
| if (!inputModuleIdJ) return; | |||||
| int inputModuleId = json_integer_value(inputModuleIdJ); | |||||
| ModuleWidget *inputModule = APP->scene->rackWidget->getModule(inputModuleId); | |||||
| if (!inputModule) return; | |||||
| // outputId | |||||
| json_t *outputIdJ = json_object_get(rootJ, "outputId"); | |||||
| if (!outputIdJ) return; | |||||
| int outputId = json_integer_value(outputIdJ); | |||||
| // inputId | |||||
| json_t *inputIdJ = json_object_get(rootJ, "inputId"); | |||||
| if (!inputIdJ) return; | |||||
| int inputId = json_integer_value(inputIdJ); | |||||
| // Set ports | // Set ports | ||||
| if (APP->patch->isLegacy(1)) { | if (APP->patch->isLegacy(1)) { | ||||
| @@ -26,6 +26,8 @@ float ParamQuantity::getSmoothValue() { | |||||
| void ParamQuantity::setValue(float value) { | void ParamQuantity::setValue(float value) { | ||||
| if (!module) | if (!module) | ||||
| return; | return; | ||||
| if (!std::isfinite(value)) | |||||
| return; | |||||
| // This setter clamps the value | // This setter clamps the value | ||||
| getParam()->setValue(value); | getParam()->setValue(value); | ||||
| } | } | ||||
| @@ -229,7 +229,13 @@ json_t *RackWidget::toJson() { | |||||
| if (!cw->isComplete()) | if (!cw->isComplete()) | ||||
| continue; | continue; | ||||
| json_array_append_new(cablesJ, cw->toJson()); | |||||
| json_t *cableJ = cw->toJson(); | |||||
| { | |||||
| // id | |||||
| json_object_set_new(rootJ, "id", json_integer(cw->cable->id)); | |||||
| } | |||||
| json_array_append_new(cablesJ, cableJ); | |||||
| } | } | ||||
| json_object_set_new(rootJ, "cables", cablesJ); | json_object_set_new(rootJ, "cables", cablesJ); | ||||
| @@ -241,18 +247,21 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
| json_t *modulesJ = json_object_get(rootJ, "modules"); | json_t *modulesJ = json_object_get(rootJ, "modules"); | ||||
| if (!modulesJ) | if (!modulesJ) | ||||
| return; | return; | ||||
| std::map<int, ModuleWidget*> moduleWidgets; | |||||
| size_t moduleIndex; | size_t moduleIndex; | ||||
| json_t *moduleJ; | json_t *moduleJ; | ||||
| json_array_foreach(modulesJ, moduleIndex, moduleJ) { | json_array_foreach(modulesJ, moduleIndex, moduleJ) { | ||||
| ModuleWidget *moduleWidget = moduleFromJson(moduleJ); | ModuleWidget *moduleWidget = moduleFromJson(moduleJ); | ||||
| if (moduleWidget) { | if (moduleWidget) { | ||||
| // Before 1.0, the module ID was the index in the "modules" array | |||||
| if (APP->patch->isLegacy(2)) { | |||||
| moduleWidget->module->id = moduleIndex; | |||||
| } | |||||
| // id | // id | ||||
| json_t *idJ = json_object_get(moduleJ, "id"); | json_t *idJ = json_object_get(moduleJ, "id"); | ||||
| int id = 0; | |||||
| if (idJ) | if (idJ) | ||||
| id = json_integer_value(idJ); | |||||
| moduleWidget->module->id = json_integer_value(idJ); | |||||
| // pos | // pos | ||||
| json_t *posJ = json_object_get(moduleJ, "pos"); | json_t *posJ = json_object_get(moduleJ, "pos"); | ||||
| double x, y; | double x, y; | ||||
| @@ -266,13 +275,6 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
| moduleWidget->box.pos = pos.mult(RACK_GRID_SIZE); | moduleWidget->box.pos = pos.mult(RACK_GRID_SIZE); | ||||
| } | } | ||||
| if (APP->patch->isLegacy(2)) { | |||||
| // Before 1.0, the module ID was the index in the "modules" array | |||||
| moduleWidgets[moduleIndex] = moduleWidget; | |||||
| } | |||||
| else { | |||||
| moduleWidgets[id] = moduleWidget; | |||||
| } | |||||
| addModule(moduleWidget); | addModule(moduleWidget); | ||||
| } | } | ||||
| else { | else { | ||||
| @@ -295,11 +297,20 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
| json_array_foreach(cablesJ, cableIndex, cableJ) { | json_array_foreach(cablesJ, cableIndex, cableJ) { | ||||
| // Create a unserialize cable | // Create a unserialize cable | ||||
| CableWidget *cw = new CableWidget; | CableWidget *cw = new CableWidget; | ||||
| cw->fromJson(cableJ, moduleWidgets); | |||||
| cw->fromJson(cableJ); | |||||
| if (!cw->isComplete()) { | if (!cw->isComplete()) { | ||||
| delete cw; | delete cw; | ||||
| continue; | continue; | ||||
| } | } | ||||
| // Before 1.0, cables IDs were not used, so just use the index of the "cables" array. | |||||
| if (APP->patch->isLegacy(2)) { | |||||
| cw->cable->id = cableIndex; | |||||
| } | |||||
| // id | |||||
| json_t *idJ = json_object_get(cableJ, "id"); | |||||
| if (idJ) | |||||
| cw->cable->id = json_integer_value(idJ); | |||||
| addCable(cw); | addCable(cw); | ||||
| } | } | ||||
| } | } | ||||
| @@ -211,6 +211,7 @@ static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrame | |||||
| // Exploit the stream time to run code on startup of the audio thread | // Exploit the stream time to run code on startup of the audio thread | ||||
| if (streamTime == 0.0) { | if (streamTime == 0.0) { | ||||
| system::setThreadName("Audio"); | system::setThreadName("Audio"); | ||||
| system::setThreadRealTime(); | |||||
| } | } | ||||
| audioIO->processStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); | audioIO->processStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); | ||||
| return 0; | return 0; | ||||
| @@ -1,5 +1,6 @@ | |||||
| #include "dsp/minblep.hpp" | #include "dsp/minblep.hpp" | ||||
| #include "dsp/fft.hpp" | #include "dsp/fft.hpp" | ||||
| #include "dsp/window.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -100,8 +100,6 @@ struct EngineWorker { | |||||
| void start() { | void start() { | ||||
| thread = std::thread([&] { | thread = std::thread([&] { | ||||
| system::setThreadName("Engine worker"); | |||||
| system::setThreadRealTime(); | |||||
| run(); | run(); | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -115,6 +113,8 @@ struct EngineWorker { | |||||
| } | } | ||||
| void run() { | void run() { | ||||
| system::setThreadName("Engine worker"); | |||||
| system::setThreadRealTime(); | |||||
| while (running) { | while (running) { | ||||
| step(); | step(); | ||||
| } | } | ||||
| @@ -134,8 +134,8 @@ struct Engine::Internal { | |||||
| float sampleTime; | float sampleTime; | ||||
| float sampleRateRequested; | float sampleRateRequested; | ||||
| int nextModuleId = 1; | |||||
| int nextCableId = 1; | |||||
| int nextModuleId = 0; | |||||
| int nextCableId = 0; | |||||
| // Parameter smoothing | // Parameter smoothing | ||||
| Module *smoothModule = NULL; | Module *smoothModule = NULL; | ||||
| @@ -209,13 +209,13 @@ static void Engine_setWorkerCount(Engine *engine, int workerCount) { | |||||
| } | } | ||||
| } | } | ||||
| static void Engine_stepModules(Engine *engine, int id) { | |||||
| static void Engine_stepModules(Engine *engine, int threadId) { | |||||
| Engine::Internal *internal = engine->internal; | Engine::Internal *internal = engine->internal; | ||||
| int threadCount = internal->threadCount; | int threadCount = internal->threadCount; | ||||
| int modulesLen = internal->modules.size(); | int modulesLen = internal->modules.size(); | ||||
| for (int i = id; i < modulesLen; i += threadCount) { | |||||
| for (int i = threadId; i < modulesLen; i += threadCount) { | |||||
| Module *module = internal->modules[i]; | Module *module = internal->modules[i]; | ||||
| if (!module->bypass) { | if (!module->bypass) { | ||||
| // Step module | // Step module | ||||
| @@ -389,17 +389,19 @@ void Engine::addModule(Module *module) { | |||||
| auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | ||||
| assert(it == internal->modules.end()); | assert(it == internal->modules.end()); | ||||
| // Set ID | // Set ID | ||||
| if (module->id == 0) { | |||||
| if (module->id < 0) { | |||||
| // Automatically assign ID | // Automatically assign ID | ||||
| module->id = internal->nextModuleId++; | module->id = internal->nextModuleId++; | ||||
| } | } | ||||
| else { | else { | ||||
| // Manual ID | // Manual ID | ||||
| assert(module->id < internal->nextModuleId); | |||||
| // Check that the ID is not already taken | // Check that the ID is not already taken | ||||
| for (Module *m : internal->modules) { | for (Module *m : internal->modules) { | ||||
| assert(module->id != m->id); | assert(module->id != m->id); | ||||
| } | } | ||||
| if (module->id >= internal->nextModuleId) { | |||||
| internal->nextModuleId = module->id + 1; | |||||
| } | |||||
| } | } | ||||
| // Add module | // Add module | ||||
| internal->modules.push_back(module); | internal->modules.push_back(module); | ||||
| @@ -424,7 +426,7 @@ void Engine::removeModule(Module *module) { | |||||
| // Remove the module | // Remove the module | ||||
| internal->modules.erase(it); | internal->modules.erase(it); | ||||
| // Remove id | // Remove id | ||||
| module->id = 0; | |||||
| module->id = -1; | |||||
| } | } | ||||
| void Engine::resetModule(Module *module) { | void Engine::resetModule(Module *module) { | ||||
| @@ -493,17 +495,19 @@ void Engine::addCable(Cable *cable) { | |||||
| assert(!(cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId)); | assert(!(cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId)); | ||||
| } | } | ||||
| // Set ID | // Set ID | ||||
| if (cable->id == 0) { | |||||
| if (cable->id < 0) { | |||||
| // Automatically assign ID | // Automatically assign ID | ||||
| cable->id = internal->nextCableId++; | cable->id = internal->nextCableId++; | ||||
| } | } | ||||
| else { | else { | ||||
| // Manual ID | // Manual ID | ||||
| assert(cable->id < internal->nextCableId); | |||||
| // Check that the ID is not already taken | // Check that the ID is not already taken | ||||
| for (Cable *w : internal->cables) { | for (Cable *w : internal->cables) { | ||||
| assert(cable->id != w->id); | assert(cable->id != w->id); | ||||
| } | } | ||||
| if (cable->id >= internal->nextCableId) { | |||||
| internal->nextCableId = cable->id + 1; | |||||
| } | |||||
| } | } | ||||
| // Add the cable | // Add the cable | ||||
| internal->cables.push_back(cable); | internal->cables.push_back(cable); | ||||
| @@ -524,7 +528,7 @@ void Engine::removeCable(Cable *cable) { | |||||
| internal->cables.erase(it); | internal->cables.erase(it); | ||||
| Engine_updateConnected(this); | Engine_updateConnected(this); | ||||
| // Remove ID | // Remove ID | ||||
| cable->id = 0; | |||||
| cable->id = -1; | |||||
| } | } | ||||
| void Engine::setParam(Module *module, int paramId, float value) { | void Engine::setParam(Module *module, int paramId, float value) { | ||||
| @@ -124,7 +124,7 @@ void ParamChange::redo() { | |||||
| void CableAdd::setCable(app::CableWidget *cw) { | void CableAdd::setCable(app::CableWidget *cw) { | ||||
| assert(cw->cable); | assert(cw->cable); | ||||
| assert(cw->cable->id > 0); | |||||
| assert(cw->cable->id >= 0); | |||||
| cableId = cw->cable->id; | cableId = cw->cable->id; | ||||
| assert(cw->cable->outputModule); | assert(cw->cable->outputModule); | ||||
| outputModuleId = cw->cable->outputModule->id; | outputModuleId = cw->cable->outputModule->id; | ||||
| @@ -209,9 +209,9 @@ Window::Window() { | |||||
| exit(1); | exit(1); | ||||
| } | } | ||||
| float pixelRatio; | |||||
| glfwGetWindowContentScale(win, &pixelRatio, NULL); | |||||
| INFO("Pixel ratio: %f", pixelRatio); | |||||
| float contentScale; | |||||
| glfwGetWindowContentScale(win, &contentScale, NULL); | |||||
| INFO("Window content scale: %f", contentScale); | |||||
| glfwSetWindowSizeLimits(win, 800, 600, GLFW_DONT_CARE, GLFW_DONT_CARE); | glfwSetWindowSizeLimits(win, 800, 600, GLFW_DONT_CARE, GLFW_DONT_CARE); | ||||
| if (settings.windowSize.isZero()) { | if (settings.windowSize.isZero()) { | ||||