You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

368 lines
11KB

  1. #include <jansson.h>
  2. #include <settings.hpp>
  3. #include <window.hpp>
  4. #include <plugin.hpp>
  5. #include <app/Scene.hpp>
  6. #include <app/ModuleBrowser.hpp>
  7. #include <engine/Engine.hpp>
  8. #include <context.hpp>
  9. #include <patch.hpp>
  10. namespace rack {
  11. namespace settings {
  12. bool devMode = false;
  13. bool headless = false;
  14. std::string token;
  15. math::Vec windowSize;
  16. math::Vec windowPos;
  17. float zoom = 0.25;
  18. bool invertZoom = false;
  19. float cableOpacity = 0.5;
  20. float cableTension = 0.5;
  21. bool allowCursorLock = true;
  22. KnobMode knobMode = KNOB_MODE_LINEAR;
  23. float knobLinearSensitivity = 0.001f;
  24. float sampleRate = 44100.0;
  25. int threadCount = 1;
  26. bool tooltips = true;
  27. bool cpuMeter = false;
  28. bool lockModules = false;
  29. #if defined ARCH_MAC
  30. // Most Mac GPUs can't handle rendering the screen every frame, so use ~30 Hz by default.
  31. int frameSwapInterval = 2;
  32. #else
  33. int frameSwapInterval = 1;
  34. #endif
  35. float autosaveInterval = 15.0;
  36. bool skipLoadOnLaunch = false;
  37. std::string patchPath;
  38. std::list<std::string> recentPatchPaths;
  39. std::vector<NVGcolor> cableColors = {
  40. color::fromHexString("#fc2d5aff"), // red
  41. color::fromHexString("#f9b130ff"), // orange
  42. // color::fromHexString("#f7da31ff"), // yellow
  43. color::fromHexString("#67c12dff"), // green
  44. color::fromHexString("#0f8df4ff"), // blue
  45. color::fromHexString("#8c1889ff"), // purple
  46. };
  47. bool autoCheckUpdates = true;
  48. bool showTipsOnLaunch = true;
  49. int tipIndex = -1;
  50. ModuleBrowserSort moduleBrowserSort = MODULE_BROWSER_SORT_UPDATED;
  51. float moduleBrowserZoom = -1.f;
  52. std::map<std::string, std::set<std::string>> moduleWhitelist = {};
  53. std::map<std::string, std::map<std::string, ModuleUsage>> moduleUsages = {};
  54. ModuleUsage* getModuleUsage(const std::string& pluginSlug, const std::string& moduleSlug) {
  55. auto it1 = moduleUsages.find(pluginSlug);
  56. if (it1 == moduleUsages.end())
  57. return NULL;
  58. auto it2 = it1->second.find(moduleSlug);
  59. if (it2 == it1->second.end())
  60. return NULL;
  61. return &it2->second;
  62. }
  63. json_t* toJson() {
  64. json_t* rootJ = json_object();
  65. json_object_set_new(rootJ, "token", json_string(token.c_str()));
  66. json_t* windowSizeJ = json_pack("[f, f]", windowSize.x, windowSize.y);
  67. json_object_set_new(rootJ, "windowSize", windowSizeJ);
  68. json_t* windowPosJ = json_pack("[f, f]", windowPos.x, windowPos.y);
  69. json_object_set_new(rootJ, "windowPos", windowPosJ);
  70. json_object_set_new(rootJ, "zoom", json_real(zoom));
  71. json_object_set_new(rootJ, "invertZoom", json_boolean(invertZoom));
  72. json_object_set_new(rootJ, "cableOpacity", json_real(cableOpacity));
  73. json_object_set_new(rootJ, "cableTension", json_real(cableTension));
  74. json_object_set_new(rootJ, "allowCursorLock", json_boolean(allowCursorLock));
  75. json_object_set_new(rootJ, "knobMode", json_integer((int) knobMode));
  76. json_object_set_new(rootJ, "knobLinearSensitivity", json_real(knobLinearSensitivity));
  77. json_object_set_new(rootJ, "sampleRate", json_real(sampleRate));
  78. json_object_set_new(rootJ, "threadCount", json_integer(threadCount));
  79. json_object_set_new(rootJ, "tooltips", json_boolean(tooltips));
  80. json_object_set_new(rootJ, "cpuMeter", json_boolean(cpuMeter));
  81. json_object_set_new(rootJ, "lockModules", json_boolean(lockModules));
  82. json_object_set_new(rootJ, "frameSwapInterval", json_integer(frameSwapInterval));
  83. json_object_set_new(rootJ, "autosaveInterval", json_real(autosaveInterval));
  84. if (skipLoadOnLaunch)
  85. json_object_set_new(rootJ, "skipLoadOnLaunch", json_boolean(true));
  86. json_object_set_new(rootJ, "patchPath", json_string(patchPath.c_str()));
  87. json_t* recentPatchPathsJ = json_array();
  88. for (const std::string& path : recentPatchPaths) {
  89. json_array_append_new(recentPatchPathsJ, json_string(path.c_str()));
  90. }
  91. json_object_set_new(rootJ, "recentPatchPaths", recentPatchPathsJ);
  92. json_t* cableColorsJ = json_array();
  93. for (NVGcolor cableColor : cableColors) {
  94. std::string colorStr = color::toHexString(cableColor);
  95. json_array_append_new(cableColorsJ, json_string(colorStr.c_str()));
  96. }
  97. json_object_set_new(rootJ, "cableColors", cableColorsJ);
  98. json_object_set_new(rootJ, "autoCheckUpdates", json_boolean(autoCheckUpdates));
  99. json_object_set_new(rootJ, "showTipsOnLaunch", json_boolean(showTipsOnLaunch));
  100. json_object_set_new(rootJ, "tipIndex", json_integer(tipIndex));
  101. json_object_set_new(rootJ, "moduleBrowserSort", json_integer((int) moduleBrowserSort));
  102. json_object_set_new(rootJ, "moduleBrowserZoom", json_real(moduleBrowserZoom));
  103. json_t* moduleWhitelistJ = json_object();
  104. for (const auto& pair : moduleWhitelist) {
  105. json_t* moduleSlugsJ = json_array();
  106. for (const std::string& moduleSlug : pair.second) {
  107. json_array_append_new(moduleSlugsJ, json_string(moduleSlug.c_str()));
  108. }
  109. json_object_set_new(moduleWhitelistJ, pair.first.c_str(), moduleSlugsJ);
  110. }
  111. json_object_set_new(rootJ, "moduleWhitelist", moduleWhitelistJ);
  112. json_t* moduleUsagesJ = json_object();
  113. for (const auto& pair : moduleUsages) {
  114. json_t* modulesJ = json_object();
  115. for (const auto& modulePair : pair.second) {
  116. const ModuleUsage& mu = modulePair.second;
  117. if (mu.count <= 0 || !std::isfinite(mu.lastTime))
  118. continue;
  119. json_t* moduleUsagesJ = json_object();
  120. {
  121. json_object_set_new(moduleUsagesJ, "count", json_integer(mu.count));
  122. json_object_set_new(moduleUsagesJ, "lastTime", json_real(mu.lastTime));
  123. }
  124. json_object_set_new(modulesJ, modulePair.first.c_str(), moduleUsagesJ);
  125. }
  126. json_object_set_new(moduleUsagesJ, pair.first.c_str(), modulesJ);
  127. }
  128. json_object_set_new(rootJ, "moduleUsages", moduleUsagesJ);
  129. return rootJ;
  130. }
  131. void fromJson(json_t* rootJ) {
  132. json_t* tokenJ = json_object_get(rootJ, "token");
  133. if (tokenJ)
  134. token = json_string_value(tokenJ);
  135. json_t* windowSizeJ = json_object_get(rootJ, "windowSize");
  136. if (windowSizeJ) {
  137. double x, y;
  138. json_unpack(windowSizeJ, "[F, F]", &x, &y);
  139. windowSize = math::Vec(x, y);
  140. }
  141. json_t* windowPosJ = json_object_get(rootJ, "windowPos");
  142. if (windowPosJ) {
  143. double x, y;
  144. json_unpack(windowPosJ, "[F, F]", &x, &y);
  145. windowPos = math::Vec(x, y);
  146. }
  147. json_t* zoomJ = json_object_get(rootJ, "zoom");
  148. if (zoomJ)
  149. zoom = json_number_value(zoomJ);
  150. json_t* invertZoomJ = json_object_get(rootJ, "invertZoom");
  151. if (invertZoomJ)
  152. invertZoom = json_boolean_value(invertZoomJ);
  153. json_t* cableOpacityJ = json_object_get(rootJ, "cableOpacity");
  154. if (cableOpacityJ)
  155. cableOpacity = json_number_value(cableOpacityJ);
  156. json_t* cableTensionJ = json_object_get(rootJ, "cableTension");
  157. if (cableTensionJ)
  158. cableTension = json_number_value(cableTensionJ);
  159. json_t* allowCursorLockJ = json_object_get(rootJ, "allowCursorLock");
  160. if (allowCursorLockJ)
  161. allowCursorLock = json_boolean_value(allowCursorLockJ);
  162. json_t* knobModeJ = json_object_get(rootJ, "knobMode");
  163. if (knobModeJ)
  164. knobMode = (KnobMode) json_integer_value(knobModeJ);
  165. json_t* knobLinearSensitivityJ = json_object_get(rootJ, "knobLinearSensitivity");
  166. if (knobLinearSensitivityJ)
  167. knobLinearSensitivity = json_number_value(knobLinearSensitivityJ);
  168. json_t* sampleRateJ = json_object_get(rootJ, "sampleRate");
  169. if (sampleRateJ)
  170. sampleRate = json_number_value(sampleRateJ);
  171. json_t* threadCountJ = json_object_get(rootJ, "threadCount");
  172. if (threadCountJ)
  173. threadCount = json_integer_value(threadCountJ);
  174. json_t* tooltipsJ = json_object_get(rootJ, "tooltips");
  175. if (tooltipsJ)
  176. tooltips = json_boolean_value(tooltipsJ);
  177. json_t* cpuMeterJ = json_object_get(rootJ, "cpuMeter");
  178. if (cpuMeterJ)
  179. cpuMeter = json_boolean_value(cpuMeterJ);
  180. json_t* lockModulesJ = json_object_get(rootJ, "lockModules");
  181. if (lockModulesJ)
  182. lockModules = json_boolean_value(lockModulesJ);
  183. json_t* frameSwapIntervalJ = json_object_get(rootJ, "frameSwapInterval");
  184. if (frameSwapIntervalJ)
  185. frameSwapInterval = json_integer_value(frameSwapIntervalJ);
  186. json_t* autosaveIntervalJ = json_object_get(rootJ, "autosaveInterval");
  187. if (autosaveIntervalJ)
  188. autosaveInterval = json_number_value(autosaveIntervalJ);
  189. json_t* skipLoadOnLaunchJ = json_object_get(rootJ, "skipLoadOnLaunch");
  190. if (skipLoadOnLaunchJ)
  191. skipLoadOnLaunch = json_boolean_value(skipLoadOnLaunchJ);
  192. json_t* patchPathJ = json_object_get(rootJ, "patchPath");
  193. if (patchPathJ)
  194. patchPath = json_string_value(patchPathJ);
  195. recentPatchPaths.clear();
  196. json_t* recentPatchPathsJ = json_object_get(rootJ, "recentPatchPaths");
  197. if (recentPatchPathsJ) {
  198. size_t i;
  199. json_t* pathJ;
  200. json_array_foreach(recentPatchPathsJ, i, pathJ) {
  201. std::string path = json_string_value(pathJ);
  202. recentPatchPaths.push_back(path);
  203. }
  204. }
  205. cableColors.clear();
  206. json_t* cableColorsJ = json_object_get(rootJ, "cableColors");
  207. if (cableColorsJ) {
  208. size_t i;
  209. json_t* cableColorJ;
  210. json_array_foreach(cableColorsJ, i, cableColorJ) {
  211. std::string colorStr = json_string_value(cableColorJ);
  212. cableColors.push_back(color::fromHexString(colorStr));
  213. }
  214. }
  215. json_t* autoCheckUpdatesJ = json_object_get(rootJ, "autoCheckUpdates");
  216. if (autoCheckUpdatesJ)
  217. autoCheckUpdates = json_boolean_value(autoCheckUpdatesJ);
  218. json_t* showTipsOnLaunchJ = json_object_get(rootJ, "showTipsOnLaunch");
  219. if (showTipsOnLaunchJ)
  220. showTipsOnLaunch = json_boolean_value(showTipsOnLaunchJ);
  221. json_t* tipIndexJ = json_object_get(rootJ, "tipIndex");
  222. if (tipIndexJ)
  223. tipIndex = json_integer_value(tipIndexJ);
  224. json_t* moduleBrowserSortJ = json_object_get(rootJ, "moduleBrowserSort");
  225. if (moduleBrowserSortJ)
  226. moduleBrowserSort = (ModuleBrowserSort) json_integer_value(moduleBrowserSortJ);
  227. json_t* moduleBrowserZoomJ = json_object_get(rootJ, "moduleBrowserZoom");
  228. if (moduleBrowserZoomJ)
  229. moduleBrowserZoom = json_number_value(moduleBrowserZoomJ);
  230. moduleWhitelist.clear();
  231. json_t* moduleWhitelistJ = json_object_get(rootJ, "moduleWhitelist");
  232. if (moduleWhitelistJ) {
  233. const char* pluginSlug;
  234. json_t* moduleSlugsJ;
  235. json_object_foreach(moduleWhitelistJ, pluginSlug, moduleSlugsJ) {
  236. auto& moduleSlugs = moduleWhitelist[pluginSlug];
  237. size_t i;
  238. json_t* moduleSlugJ;
  239. json_array_foreach(moduleSlugsJ, i, moduleSlugJ) {
  240. std::string moduleSlug = json_string_value(moduleSlugJ);
  241. moduleSlugs.insert(moduleSlug);
  242. }
  243. }
  244. }
  245. moduleUsages.clear();
  246. json_t* moduleUsagesJ = json_object_get(rootJ, "moduleUsages");
  247. if (moduleUsagesJ) {
  248. const char* pluginSlug;
  249. json_t* modulesJ;
  250. json_object_foreach(moduleUsagesJ, pluginSlug, modulesJ) {
  251. const char* moduleSlug;
  252. json_t* moduleJ;
  253. json_object_foreach(modulesJ, moduleSlug, moduleJ) {
  254. ModuleUsage mu;
  255. json_t* countJ = json_object_get(moduleJ, "count");
  256. if (countJ)
  257. mu.count = json_integer_value(countJ);
  258. json_t* lastTimeJ = json_object_get(moduleJ, "lastTime");
  259. if (lastTimeJ)
  260. mu.lastTime = json_number_value(lastTimeJ);
  261. moduleUsages[pluginSlug][moduleSlug] = mu;
  262. }
  263. }
  264. }
  265. }
  266. void save(const std::string& path) {
  267. INFO("Saving settings %s", path.c_str());
  268. json_t* rootJ = toJson();
  269. if (!rootJ)
  270. return;
  271. FILE* file = std::fopen(path.c_str(), "w");
  272. if (!file)
  273. return;
  274. DEFER({std::fclose(file);});
  275. // Because settings include doubles, it should use 17 decimal digits of precision instead of just 9 for float32.
  276. json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(17));
  277. json_decref(rootJ);
  278. }
  279. void load(const std::string& path) {
  280. INFO("Loading settings %s", path.c_str());
  281. FILE* file = std::fopen(path.c_str(), "r");
  282. if (!file)
  283. return;
  284. DEFER({std::fclose(file);});
  285. json_error_t error;
  286. json_t* rootJ = json_loadf(file, 0, &error);
  287. if (!rootJ)
  288. throw Exception(string::f("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text));
  289. fromJson(rootJ);
  290. json_decref(rootJ);
  291. }
  292. } // namespace settings
  293. } // namespace rack