| @@ -1,7 +1,7 @@ | |||
| #pragma once | |||
| #include "util/common.hpp" | |||
| #include "string.hpp" | |||
| #include "nanovg.h" | |||
| @@ -87,9 +87,9 @@ inline std::string toHexString(NVGcolor c) { | |||
| uint8_t b = std::round(c.b * 255); | |||
| uint8_t a = std::round(c.a * 255); | |||
| if (a == 255) | |||
| return stringf("#%02x%02x%02x", r, g, b); | |||
| return string::stringf("#%02x%02x%02x", r, g, b); | |||
| else | |||
| return stringf("#%02x%02x%02x%02x", r, g, b, a); | |||
| return string::stringf("#%02x%02x%02x%02x", r, g, b, a); | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| #pragma once | |||
| /** Example usage: | |||
| debug("error: %d", errno); | |||
| will print something like | |||
| [0.123 debug myfile.cpp:45] error: 67 | |||
| */ | |||
| #define DEBUG(format, ...) rack::logger::log(rack::logger::DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||
| #define INFO(format, ...) rack::logger::log(rack::logger::INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||
| #define WARN(format, ...) rack::logger::log(rack::logger::WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||
| #define FATAL(format, ...) rack::logger::log(rack::logger::FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||
| /** Deprecated lowercase log functions */ | |||
| #define debug(...) DEBUG(__VA_ARGS__) | |||
| #define info(...) INFO(__VA_ARGS__) | |||
| #define warn(...) WARN(__VA_ARGS__) | |||
| #define fatal(...) FATAL(__VA_ARGS__) | |||
| namespace rack { | |||
| namespace logger { | |||
| enum Level { | |||
| DEBUG_LEVEL, | |||
| INFO_LEVEL, | |||
| WARN_LEVEL, | |||
| FATAL_LEVEL | |||
| }; | |||
| void init(bool devMode); | |||
| void destroy(); | |||
| /** Do not use this function directly. Use the macros below. */ | |||
| void log(Level level, const char *filename, int line, const char *format, ...); | |||
| } // namespace logger | |||
| } // namespace rack | |||
| @@ -0,0 +1,67 @@ | |||
| #pragma once | |||
| /** Concatenates two literals or two macros | |||
| Example: | |||
| #define COUNT 42 | |||
| CONCAT(myVariable, COUNT) | |||
| expands to | |||
| myVariable42 | |||
| */ | |||
| #define CONCAT_LITERAL(x, y) x ## y | |||
| #define CONCAT(x, y) CONCAT_LITERAL(x, y) | |||
| /** Surrounds raw text with quotes | |||
| Example: | |||
| #define NAME "world" | |||
| printf("Hello " TOSTRING(NAME)) | |||
| expands to | |||
| printf("Hello " "world") | |||
| and of course the C++ lexer/parser then concatenates the string literals. | |||
| */ | |||
| #define TOSTRING_LITERAL(x) #x | |||
| #define TOSTRING(x) TOSTRING_LITERAL(x) | |||
| /** Produces the length of a static array in number of elements */ | |||
| #define LENGTHOF(arr) (sizeof(arr) / sizeof((arr)[0])) | |||
| /** Reserve space for `count` enums starting with `name`. | |||
| Example: | |||
| enum Foo { | |||
| ENUMS(BAR, 14), | |||
| BAZ | |||
| }; | |||
| BAR + 0 to BAR + 13 is reserved. BAZ has a value of 14. | |||
| */ | |||
| #define ENUMS(name, count) name, name ## _LAST = name + (count) - 1 | |||
| /** Deprecation notice for GCC */ | |||
| #define DEPRECATED __attribute__ ((deprecated)) | |||
| /** References binary files compiled into the program. | |||
| For example, to include a file "Test.dat" directly into your program binary, add | |||
| BINARIES += Test.dat | |||
| to your Makefile and declare | |||
| BINARY(Test_dat); | |||
| at the root of a .c or .cpp source file. Note that special characters are replaced with "_". Then use | |||
| BINARY_START(Test_dat) | |||
| BINARY_END(Test_dat) | |||
| to reference the data beginning and end as a void* array, and | |||
| BINARY_SIZE(Test_dat) | |||
| to get its size in bytes. | |||
| */ | |||
| #ifdef ARCH_MAC | |||
| // Use output from `xxd -i` | |||
| #define BINARY(sym) extern unsigned char sym[]; extern unsigned int sym##_len | |||
| #define BINARY_START(sym) ((const void*) sym) | |||
| #define BINARY_END(sym) ((const void*) sym + sym##_len) | |||
| #define BINARY_SIZE(sym) (sym##_len) | |||
| #else | |||
| #define BINARY(sym) extern char _binary_##sym##_start, _binary_##sym##_end, _binary_##sym##_size | |||
| #define BINARY_START(sym) ((const void*) &_binary_##sym##_start) | |||
| #define BINARY_END(sym) ((const void*) &_binary_##sym##_end) | |||
| // The symbol "_binary_##sym##_size" doesn't seem to be valid after a plugin is dynamically loaded, so simply take the difference between the two addresses. | |||
| #define BINARY_SIZE(sym) ((size_t) (&_binary_##sym##_end - &_binary_##sym##_start)) | |||
| #endif | |||
| @@ -1,8 +1,8 @@ | |||
| #pragma once | |||
| #include "util/common.hpp" | |||
| #include <cmath> | |||
| #include <cstdlib> | |||
| #include <algorithm> // for std::min, max | |||
| #include "macros.hpp" | |||
| namespace rack { | |||
| @@ -15,6 +15,8 @@ namespace rack { | |||
| using namespace math; | |||
| using namespace string; | |||
| using namespace logger; | |||
| } // namespace rack | |||
| @@ -1,13 +1,16 @@ | |||
| #pragma once | |||
| #include "util/common.hpp" | |||
| #include <stdarg.h> | |||
| #include <algorithm> | |||
| #include <cstdarg> | |||
| #include <libgen.h> // for dirname and basename | |||
| namespace rack { | |||
| namespace string { | |||
| std::string stringf(const char *format, ...) { | |||
| /** Converts a printf format string and optional arguments into a std::string */ | |||
| inline std::string stringf(const char *format, ...) { | |||
| va_list args; | |||
| va_start(args, format); | |||
| // Compute size of required buffer | |||
| @@ -24,51 +27,60 @@ std::string stringf(const char *format, ...) { | |||
| return s; | |||
| } | |||
| std::string stringLowercase(std::string s) { | |||
| inline std::string lowercase(std::string s) { | |||
| std::transform(s.begin(), s.end(), s.begin(), ::tolower); | |||
| return s; | |||
| } | |||
| std::string stringUppercase(std::string s) { | |||
| inline std::string uppercase(std::string s) { | |||
| std::transform(s.begin(), s.end(), s.begin(), ::toupper); | |||
| return s; | |||
| } | |||
| std::string stringEllipsize(std::string s, size_t len) { | |||
| /** Truncates and adds "..." to a string, not exceeding `len` characters */ | |||
| inline std::string ellipsize(std::string s, size_t len) { | |||
| if (s.size() <= len) | |||
| return s; | |||
| else | |||
| return s.substr(0, len - 3) + "..."; | |||
| } | |||
| bool stringStartsWith(std::string str, std::string prefix) { | |||
| inline bool startsWith(std::string str, std::string prefix) { | |||
| return str.substr(0, prefix.size()) == prefix; | |||
| } | |||
| bool stringEndsWith(std::string str, std::string suffix) { | |||
| inline bool endsWith(std::string str, std::string suffix) { | |||
| return str.substr(str.size() - suffix.size(), suffix.size()) == suffix; | |||
| } | |||
| std::string stringDirectory(std::string path) { | |||
| /** Extracts portions of a path */ | |||
| inline std::string directory(std::string path) { | |||
| char *pathDup = strdup(path.c_str()); | |||
| std::string directory = dirname(pathDup); | |||
| free(pathDup); | |||
| return directory; | |||
| } | |||
| std::string stringFilename(std::string path) { | |||
| inline std::string filename(std::string path) { | |||
| char *pathDup = strdup(path.c_str()); | |||
| std::string filename = basename(pathDup); | |||
| free(pathDup); | |||
| return filename; | |||
| } | |||
| std::string stringExtension(std::string path) { | |||
| const char *ext = strrchr(stringFilename(path).c_str(), '.'); | |||
| inline std::string extension(std::string path) { | |||
| const char *ext = strrchr(filename(path).c_str(), '.'); | |||
| if (!ext) | |||
| return ""; | |||
| return ext + 1; | |||
| } | |||
| struct CaseInsensitiveCompare { | |||
| bool operator()(const std::string &a, const std::string &b) const { | |||
| return lowercase(a) < lowercase(b); | |||
| } | |||
| }; | |||
| } // namespace string | |||
| } // namespace rack | |||
| @@ -13,77 +13,10 @@ | |||
| #include <condition_variable> | |||
| #include <mutex> | |||
| //////////////////// | |||
| // Handy macros | |||
| //////////////////// | |||
| /** Concatenates two literals or two macros | |||
| Example: | |||
| #define COUNT 42 | |||
| CONCAT(myVariable, COUNT) | |||
| expands to | |||
| myVariable42 | |||
| */ | |||
| #define CONCAT_LITERAL(x, y) x ## y | |||
| #define CONCAT(x, y) CONCAT_LITERAL(x, y) | |||
| /** Surrounds raw text with quotes | |||
| Example: | |||
| #define NAME "world" | |||
| printf("Hello " TOSTRING(NAME)) | |||
| expands to | |||
| printf("Hello " "world") | |||
| and of course the C++ lexer/parser then concatenates the string literals. | |||
| */ | |||
| #define TOSTRING_LITERAL(x) #x | |||
| #define TOSTRING(x) TOSTRING_LITERAL(x) | |||
| /** Produces the length of a static array in number of elements */ | |||
| #define LENGTHOF(arr) (sizeof(arr) / sizeof((arr)[0])) | |||
| /** Reserve space for `count` enums starting with `name`. | |||
| Example: | |||
| enum Foo { | |||
| ENUMS(BAR, 14), | |||
| BAZ | |||
| }; | |||
| BAR + 0 to BAR + 13 is reserved. BAZ has a value of 14. | |||
| */ | |||
| #define ENUMS(name, count) name, name ## _LAST = name + (count) - 1 | |||
| /** Deprecation notice for GCC */ | |||
| #define DEPRECATED __attribute__ ((deprecated)) | |||
| /** References binary files compiled into the program. | |||
| For example, to include a file "Test.dat" directly into your program binary, add | |||
| BINARIES += Test.dat | |||
| to your Makefile and declare | |||
| BINARY(Test_dat); | |||
| at the root of a .c or .cpp source file. Note that special characters are replaced with "_". Then use | |||
| BINARY_START(Test_dat) | |||
| BINARY_END(Test_dat) | |||
| to reference the data beginning and end as a void* array, and | |||
| BINARY_SIZE(Test_dat) | |||
| to get its size in bytes. | |||
| */ | |||
| #ifdef ARCH_MAC | |||
| // Use output from `xxd -i` | |||
| #define BINARY(sym) extern unsigned char sym[]; extern unsigned int sym##_len | |||
| #define BINARY_START(sym) ((const void*) sym) | |||
| #define BINARY_END(sym) ((const void*) sym + sym##_len) | |||
| #define BINARY_SIZE(sym) (sym##_len) | |||
| #else | |||
| #define BINARY(sym) extern char _binary_##sym##_start, _binary_##sym##_end, _binary_##sym##_size | |||
| #define BINARY_START(sym) ((const void*) &_binary_##sym##_start) | |||
| #define BINARY_END(sym) ((const void*) &_binary_##sym##_end) | |||
| // The symbol "_binary_##sym##_size" doesn't seem to be valid after a plugin is dynamically loaded, so simply take the difference between the two addresses. | |||
| #define BINARY_SIZE(sym) ((size_t) (&_binary_##sym##_end - &_binary_##sym##_start)) | |||
| #endif | |||
| #include "macros.hpp" | |||
| #include "math.hpp" | |||
| #include "string.hpp" | |||
| #include "logger.hpp" | |||
| namespace rack { | |||
| @@ -148,32 +81,6 @@ float randomNormal(); | |||
| DEPRECATED inline float randomf() {return randomUniform();} | |||
| //////////////////// | |||
| // String utilities | |||
| // string.cpp | |||
| //////////////////// | |||
| /** Converts a printf format string and optional arguments into a std::string */ | |||
| std::string stringf(const char *format, ...); | |||
| std::string stringLowercase(std::string s); | |||
| std::string stringUppercase(std::string s); | |||
| /** Truncates and adds "..." to a string, not exceeding `len` characters */ | |||
| std::string stringEllipsize(std::string s, size_t len); | |||
| bool stringStartsWith(std::string str, std::string prefix); | |||
| bool stringEndsWith(std::string str, std::string suffix); | |||
| /** Extracts portions of a path */ | |||
| std::string stringDirectory(std::string path); | |||
| std::string stringFilename(std::string path); | |||
| std::string stringExtension(std::string path); | |||
| struct StringCaseInsensitiveCompare { | |||
| bool operator()(const std::string &a, const std::string &b) const { | |||
| return stringLowercase(a) < stringLowercase(b); | |||
| } | |||
| }; | |||
| //////////////////// | |||
| // Operating-system specific utilities | |||
| // system.cpp | |||
| @@ -196,26 +103,6 @@ void systemOpenBrowser(std::string url); | |||
| // logger.cpp | |||
| //////////////////// | |||
| enum LoggerLevel { | |||
| DEBUG_LEVEL = 0, | |||
| INFO_LEVEL, | |||
| WARN_LEVEL, | |||
| FATAL_LEVEL | |||
| }; | |||
| void loggerInit(bool devMode); | |||
| void loggerDestroy(); | |||
| /** Do not use this function directly. Use the macros below. */ | |||
| void loggerLog(LoggerLevel level, const char *file, int line, const char *format, ...); | |||
| /** Example usage: | |||
| debug("error: %d", errno); | |||
| will print something like | |||
| [0.123 debug myfile.cpp:45] error: 67 | |||
| */ | |||
| #define debug(format, ...) loggerLog(DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||
| #define info(format, ...) loggerLog(INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||
| #define warn(format, ...) loggerLog(WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||
| #define fatal(format, ...) loggerLog(FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||
| //////////////////// | |||
| // Thread functions | |||
| @@ -7,7 +7,7 @@ | |||
| #include "util/common.hpp" | |||
| #include "events.hpp" | |||
| #include "util/color.hpp" | |||
| #include "color.hpp" | |||
| namespace rack { | |||
| @@ -121,13 +121,13 @@ struct MidiCcChoice : GridChoice { | |||
| void step() override { | |||
| if (module->learningId == id) { | |||
| if (0 <= focusCc) | |||
| text = stringf("%d", focusCc); | |||
| text = string::stringf("%d", focusCc); | |||
| else | |||
| text = "LRN"; | |||
| color.a = 0.5; | |||
| } | |||
| else { | |||
| text = stringf("%d", module->learnedCcs[id]); | |||
| text = string::stringf("%d", module->learnedCcs[id]); | |||
| color.a = 1.0; | |||
| if (gFocusedWidget == this) | |||
| gFocusedWidget = NULL; | |||
| @@ -317,7 +317,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||
| menu->addChild(construct<MenuLabel>()); | |||
| for (int i = 0; i < 2; i++) { | |||
| ClockItem *item = MenuItem::create<ClockItem>(stringf("CLK %d rate", i + 1)); | |||
| ClockItem *item = MenuItem::create<ClockItem>(string::stringf("CLK %d rate", i + 1)); | |||
| item->module = module; | |||
| item->index = i; | |||
| menu->addChild(item); | |||
| @@ -168,7 +168,7 @@ struct MidiTrigChoice : GridChoice { | |||
| }; | |||
| int oct = note / 12 - 1; | |||
| int semi = note % 12; | |||
| text = stringf("%s%d", noteNames[semi], oct); | |||
| text = string::stringf("%s%d", noteNames[semi], oct); | |||
| color.a = 1.0; | |||
| if (gFocusedWidget == this) | |||
| @@ -106,13 +106,13 @@ struct AudioSampleRateChoice : LedDisplayChoice { | |||
| AudioSampleRateItem *item = new AudioSampleRateItem(); | |||
| item->audioIO = audioWidget->audioIO; | |||
| item->sampleRate = sampleRate; | |||
| item->text = stringf("%d Hz", sampleRate); | |||
| item->text = string::stringf("%d Hz", sampleRate); | |||
| item->rightText = CHECKMARK(item->sampleRate == audioWidget->audioIO->sampleRate); | |||
| menu->addChild(item); | |||
| } | |||
| } | |||
| void step() override { | |||
| text = stringf("%g kHz", audioWidget->audioIO->sampleRate / 1000.f); | |||
| text = string::stringf("%g kHz", audioWidget->audioIO->sampleRate / 1000.f); | |||
| } | |||
| }; | |||
| @@ -139,13 +139,13 @@ struct AudioBlockSizeChoice : LedDisplayChoice { | |||
| item->audioIO = audioWidget->audioIO; | |||
| item->blockSize = blockSize; | |||
| float latency = (float) blockSize / audioWidget->audioIO->sampleRate * 1000.0; | |||
| item->text = stringf("%d (%.1f ms)", blockSize, latency); | |||
| item->text = string::stringf("%d (%.1f ms)", blockSize, latency); | |||
| item->rightText = CHECKMARK(item->blockSize == audioWidget->audioIO->blockSize); | |||
| menu->addChild(item); | |||
| } | |||
| } | |||
| void step() override { | |||
| text = stringf("%d", audioWidget->audioIO->blockSize); | |||
| text = string::stringf("%d", audioWidget->audioIO->blockSize); | |||
| } | |||
| }; | |||
| @@ -1,5 +1,5 @@ | |||
| #include "app.hpp" | |||
| #include "util/color.hpp" | |||
| #include "color.hpp" | |||
| namespace rack { | |||
| @@ -18,8 +18,8 @@ static ModelTag sTagFilter = NO_TAG; | |||
| bool isMatch(std::string s, std::string search) { | |||
| s = stringLowercase(s); | |||
| search = stringLowercase(search); | |||
| s = string::lowercase(s); | |||
| search = string::lowercase(search); | |||
| return (s.find(search) != std::string::npos); | |||
| } | |||
| @@ -279,7 +279,7 @@ struct ModuleBrowser : OpaqueWidget { | |||
| SearchModuleField *searchField; | |||
| ScrollWidget *moduleScroll; | |||
| BrowserList *moduleList; | |||
| std::set<std::string, StringCaseInsensitiveCompare> availableAuthors; | |||
| std::set<std::string, string::CaseInsensitiveCompare> availableAuthors; | |||
| std::set<ModelTag> availableTags; | |||
| ModuleBrowser() { | |||
| @@ -203,7 +203,7 @@ void ModuleWidget::load(std::string filename) { | |||
| json_decref(moduleJ); | |||
| } | |||
| else { | |||
| std::string message = stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||
| std::string message = string::stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||
| osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | |||
| } | |||
| @@ -248,7 +248,7 @@ void ModuleWidget::saveDialog() { | |||
| if (path) { | |||
| std::string pathStr = path; | |||
| free(path); | |||
| std::string extension = stringExtension(pathStr); | |||
| std::string extension = string::extension(pathStr); | |||
| if (extension.empty()) { | |||
| pathStr += ".vcvm"; | |||
| } | |||
| @@ -304,7 +304,7 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
| nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.5)); | |||
| nvgFill(vg); | |||
| std::string cpuText = stringf("%.0f mS", module->cpuTime * 1000.f); | |||
| std::string cpuText = string::stringf("%.0f mS", module->cpuTime * 1000.f); | |||
| nvgFontFaceId(vg, gGuiFont->handle); | |||
| nvgFontSize(vg, 12); | |||
| nvgFillColor(vg, nvgRGBf(1, 1, 1)); | |||
| @@ -1,5 +1,5 @@ | |||
| #include "app.hpp" | |||
| #include "util/color.hpp" | |||
| #include "color.hpp" | |||
| namespace rack { | |||
| @@ -44,7 +44,7 @@ void RackScene::step() { | |||
| // Version popup message | |||
| if (!gLatestVersion.empty()) { | |||
| std::string versionMessage = stringf("Rack %s is available.\n\nYou have Rack %s.\n\nClose Rack and download new version on the website?", gLatestVersion.c_str(), gApplicationVersion.c_str()); | |||
| std::string versionMessage = string::stringf("Rack %s is available.\n\nYou have Rack %s.\n\nClose Rack and download new version on the website?", gLatestVersion.c_str(), gApplicationVersion.c_str()); | |||
| if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, versionMessage.c_str())) { | |||
| std::thread t(systemOpenBrowser, "https://vcvrack.com/"); | |||
| t.detach(); | |||
| @@ -116,7 +116,7 @@ void RackScene::onHoverKey(EventHoverKey &e) { | |||
| void RackScene::onPathDrop(EventPathDrop &e) { | |||
| if (e.paths.size() >= 1) { | |||
| const std::string &firstPath = e.paths.front(); | |||
| if (stringExtension(firstPath) == "vcv") { | |||
| if (string::extension(firstPath) == "vcv") { | |||
| gRackWidget->load(firstPath); | |||
| e.consumed = true; | |||
| } | |||
| @@ -75,7 +75,7 @@ void RackWidget::loadDialog() { | |||
| systemCreateDirectory(dir); | |||
| } | |||
| else { | |||
| dir = stringDirectory(lastPath); | |||
| dir = string::directory(lastPath); | |||
| } | |||
| osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | |||
| char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); | |||
| @@ -104,8 +104,8 @@ void RackWidget::saveAsDialog() { | |||
| systemCreateDirectory(dir); | |||
| } | |||
| else { | |||
| dir = stringDirectory(lastPath); | |||
| filename = stringFilename(lastPath); | |||
| dir = string::directory(lastPath); | |||
| filename = string::filename(lastPath); | |||
| } | |||
| osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | |||
| char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters); | |||
| @@ -113,7 +113,7 @@ void RackWidget::saveAsDialog() { | |||
| if (path) { | |||
| std::string pathStr = path; | |||
| free(path); | |||
| std::string extension = stringExtension(pathStr); | |||
| std::string extension = string::extension(pathStr); | |||
| if (extension.empty()) { | |||
| pathStr += ".vcv"; | |||
| } | |||
| @@ -155,7 +155,7 @@ void RackWidget::load(std::string filename) { | |||
| json_decref(rootJ); | |||
| } | |||
| else { | |||
| std::string message = stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||
| std::string message = string::stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||
| osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | |||
| } | |||
| @@ -259,7 +259,7 @@ void RackWidget::fromJson(json_t *rootJ) { | |||
| // Detect old patches with ModuleWidget::params/inputs/outputs indices. | |||
| // (We now use Module::params/inputs/outputs indices.) | |||
| int legacy = 0; | |||
| if (stringStartsWith(version, "0.3.") || stringStartsWith(version, "0.4.") || stringStartsWith(version, "0.5.") || version == "" || version == "dev") { | |||
| if (string::startsWith(version, "0.3.") || string::startsWith(version, "0.4.") || string::startsWith(version, "0.5.") || version == "" || version == "dev") { | |||
| legacy = 1; | |||
| } | |||
| if (legacy) { | |||
| @@ -300,7 +300,7 @@ void RackWidget::fromJson(json_t *rootJ) { | |||
| json_t *modelSlugJ = json_object_get(moduleJ, "model"); | |||
| std::string pluginSlug = json_string_value(pluginSlugJ); | |||
| std::string modelSlug = json_string_value(modelSlugJ); | |||
| message += stringf("Could not find module \"%s\" of plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str()); | |||
| message += string::stringf("Could not find module \"%s\" of plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str()); | |||
| } | |||
| } | |||
| @@ -132,7 +132,7 @@ struct SampleRateButton : TooltipIconButton { | |||
| std::vector<float> sampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; | |||
| for (float sampleRate : sampleRates) { | |||
| SampleRateItem *item = new SampleRateItem(); | |||
| item->text = stringf("%.0f Hz", sampleRate); | |||
| item->text = string::stringf("%.0f Hz", sampleRate); | |||
| item->rightText = CHECKMARK(engineGetSampleRate() == sampleRate); | |||
| item->sampleRate = sampleRate; | |||
| menu->addChild(item); | |||
| @@ -18,8 +18,9 @@ std::vector<int> AudioIO::getDrivers() { | |||
| std::vector<RtAudio::Api> apis; | |||
| RtAudio::getCompiledApi(apis); | |||
| std::vector<int> drivers; | |||
| for (RtAudio::Api api : apis) | |||
| for (RtAudio::Api api : apis) { | |||
| drivers.push_back((int) api); | |||
| } | |||
| // Add fake Bridge driver | |||
| drivers.push_back(BRIDGE_DRIVER); | |||
| return drivers; | |||
| @@ -121,7 +122,7 @@ std::string AudioIO::getDeviceName(int device) { | |||
| return deviceInfo.name; | |||
| } | |||
| else if (driver == BRIDGE_DRIVER) { | |||
| return stringf("%d", device + 1); | |||
| return string::stringf("%d", device + 1); | |||
| } | |||
| return ""; | |||
| } | |||
| @@ -133,19 +134,19 @@ std::string AudioIO::getDeviceDetail(int device, int offset) { | |||
| if (rtAudio) { | |||
| RtAudio::DeviceInfo deviceInfo; | |||
| if (getDeviceInfo(device, &deviceInfo)) { | |||
| std::string deviceDetail = stringf("%s (", deviceInfo.name.c_str()); | |||
| std::string deviceDetail = string::stringf("%s (", deviceInfo.name.c_str()); | |||
| if (offset < (int) deviceInfo.inputChannels) | |||
| deviceDetail += stringf("%d-%d in", offset + 1, std::min(offset + maxChannels, (int) deviceInfo.inputChannels)); | |||
| deviceDetail += string::stringf("%d-%d in", offset + 1, std::min(offset + maxChannels, (int) deviceInfo.inputChannels)); | |||
| if (offset < (int) deviceInfo.inputChannels && offset < (int) deviceInfo.outputChannels) | |||
| deviceDetail += ", "; | |||
| if (offset < (int) deviceInfo.outputChannels) | |||
| deviceDetail += stringf("%d-%d out", offset + 1, std::min(offset + maxChannels, (int) deviceInfo.outputChannels)); | |||
| deviceDetail += string::stringf("%d-%d out", offset + 1, std::min(offset + maxChannels, (int) deviceInfo.outputChannels)); | |||
| deviceDetail += ")"; | |||
| return deviceDetail; | |||
| } | |||
| } | |||
| else if (driver == BRIDGE_DRIVER) { | |||
| return stringf("Port %d", device + 1); | |||
| return string::stringf("Port %d", device + 1); | |||
| } | |||
| return ""; | |||
| } | |||
| @@ -384,7 +384,7 @@ std::vector<int> BridgeMidiDriver::getInputDeviceIds() { | |||
| std::string BridgeMidiDriver::getInputDeviceName(int deviceId) { | |||
| if (deviceId < 0) | |||
| return ""; | |||
| return stringf("Port %d", deviceId + 1); | |||
| return string::stringf("Port %d", deviceId + 1); | |||
| } | |||
| MidiInputDevice *BridgeMidiDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) { | |||
| @@ -1,5 +1,5 @@ | |||
| #include "gamepad.hpp" | |||
| #include <GLFW/glfw3.h> | |||
| #include "gamepad.hpp" | |||
| namespace rack { | |||
| @@ -22,7 +22,7 @@ void GamepadInputDevice::step() { | |||
| ccs.resize(numAxes); | |||
| for (int i = 0; i < numAxes; i++) { | |||
| // Allow CC value to go negative, but math::clamp at -127 instead of -128 for symmetry | |||
| int8_t cc = math::clamp((int) roundf(axes[i] * 127), -127, 127); | |||
| int8_t cc = math::clamp((int) std::round(axes[i] * 127), -127, 127); | |||
| if (cc != ccs[i]) { | |||
| ccs[i] = cc; | |||
| @@ -77,7 +77,7 @@ std::string GamepadDriver::getInputDeviceName(int deviceId) { | |||
| if (name) { | |||
| return name; | |||
| } | |||
| return stringf("Gamepad %d (unavailable)", deviceId + 1); | |||
| return string::stringf("Gamepad %d (unavailable)", deviceId + 1); | |||
| } | |||
| MidiInputDevice *GamepadDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) { | |||
| @@ -0,0 +1,68 @@ | |||
| #include <cstdarg> | |||
| #include <chrono> | |||
| #include "logger.hpp" | |||
| #include "asset.hpp" | |||
| namespace rack { | |||
| namespace logger { | |||
| static FILE *outputFile = NULL; | |||
| static std::chrono::high_resolution_clock::time_point startTime; | |||
| void init(bool devMode) { | |||
| startTime = std::chrono::high_resolution_clock::now(); | |||
| if (devMode) { | |||
| outputFile = stderr; | |||
| } | |||
| else { | |||
| std::string logFilename = assetLocal("log.txt"); | |||
| outputFile = fopen(logFilename.c_str(), "w"); | |||
| } | |||
| } | |||
| void destroy() { | |||
| if (outputFile != stderr) { | |||
| fclose(outputFile); | |||
| } | |||
| } | |||
| static const char* const levelLabels[] = { | |||
| "debug", | |||
| "info", | |||
| "warn", | |||
| "fatal" | |||
| }; | |||
| static const int levelColors[] = { | |||
| 35, | |||
| 34, | |||
| 33, | |||
| 31 | |||
| }; | |||
| static void logVa(Level level, const char *filename, int line, const char *format, va_list args) { | |||
| auto nowTime = std::chrono::high_resolution_clock::now(); | |||
| int duration = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - startTime).count(); | |||
| if (outputFile == stderr) | |||
| fprintf(outputFile, "\x1B[%dm", levelColors[level]); | |||
| fprintf(outputFile, "[%.03f %s %s:%d] ", duration / 1000.0, levelLabels[level], filename, line); | |||
| if (outputFile == stderr) | |||
| fprintf(outputFile, "\x1B[0m"); | |||
| vfprintf(outputFile, format, args); | |||
| fprintf(outputFile, "\n"); | |||
| fflush(outputFile); | |||
| } | |||
| void log(Level level, const char *filename, int line, const char *format, ...) { | |||
| va_list args; | |||
| va_start(args, format); | |||
| logVa(level, filename, line, format, args); | |||
| va_end(args); | |||
| } | |||
| } // namespace logger | |||
| } // namespace rack | |||
| @@ -10,7 +10,7 @@ | |||
| #include "rtmidi.hpp" | |||
| #include "keyboard.hpp" | |||
| #include "gamepad.hpp" | |||
| #include "util/color.hpp" | |||
| #include "color.hpp" | |||
| #include "osdialog.h" | |||
| #include <unistd.h> | |||
| @@ -61,7 +61,7 @@ int main(int argc, char* argv[]) { | |||
| // Initialize environment | |||
| randomInit(); | |||
| assetInit(devMode); | |||
| loggerInit(devMode); | |||
| logger::init(devMode); | |||
| // Log environment | |||
| info("%s %s", gApplicationName.c_str(), gApplicationVersion.c_str()); | |||
| @@ -116,7 +116,7 @@ int main(int argc, char* argv[]) { | |||
| engineDestroy(); | |||
| midiDestroy(); | |||
| pluginDestroy(); | |||
| loggerDestroy(); | |||
| logger::destroy(); | |||
| return 0; | |||
| } | |||
| @@ -79,7 +79,7 @@ std::string MidiIO::getChannelName(int channel) { | |||
| if (channel == -1) | |||
| return "All channels"; | |||
| else | |||
| return stringf("Channel %d", channel + 1); | |||
| return string::stringf("Channel %d", channel + 1); | |||
| } | |||
| json_t *MidiIO::toJson() { | |||
| @@ -216,7 +216,7 @@ static void loadPlugins(std::string path) { | |||
| if (!systemIsDirectory(pluginPath)) | |||
| continue; | |||
| if (!loadPlugin(pluginPath)) { | |||
| message += stringf("Could not load plugin %s\n", pluginPath.c_str()); | |||
| message += string::stringf("Could not load plugin %s\n", pluginPath.c_str()); | |||
| } | |||
| } | |||
| if (!message.empty()) { | |||
| @@ -298,13 +298,13 @@ static void extractPackages(std::string path) { | |||
| std::string message; | |||
| for (std::string packagePath : systemListEntries(path)) { | |||
| if (stringExtension(packagePath) != "zip") | |||
| if (string::extension(packagePath) != "zip") | |||
| continue; | |||
| info("Extracting package %s", packagePath.c_str()); | |||
| // Extract package | |||
| if (extractZip(packagePath.c_str(), path.c_str())) { | |||
| warn("Package %s failed to extract", packagePath.c_str()); | |||
| message += stringf("Could not extract package %s\n", packagePath.c_str()); | |||
| message += string::stringf("Could not extract package %s\n", packagePath.c_str()); | |||
| continue; | |||
| } | |||
| // Remove package | |||
| @@ -1,6 +1,6 @@ | |||
| #include "ui.hpp" | |||
| #include "window.hpp" | |||
| #include "util/color.hpp" | |||
| #include "color.hpp" | |||
| namespace rack { | |||
| @@ -1,100 +0,0 @@ | |||
| #include "util/common.hpp" | |||
| #include "asset.hpp" | |||
| #include <stdarg.h> | |||
| namespace rack { | |||
| static FILE *logFile = NULL; | |||
| static std::chrono::high_resolution_clock::time_point startTime; | |||
| void loggerInit(bool devMode) { | |||
| startTime = std::chrono::high_resolution_clock::now(); | |||
| if (devMode) { | |||
| logFile = stderr; | |||
| } | |||
| else { | |||
| std::string logFilename = assetLocal("log.txt"); | |||
| logFile = fopen(logFilename.c_str(), "w"); | |||
| } | |||
| } | |||
| void loggerDestroy() { | |||
| if (logFile != stderr) { | |||
| fclose(logFile); | |||
| } | |||
| } | |||
| static const char* const loggerText[] = { | |||
| "debug", | |||
| "info", | |||
| "warn", | |||
| "fatal" | |||
| }; | |||
| static const int loggerColor[] = { | |||
| 35, | |||
| 34, | |||
| 33, | |||
| 31 | |||
| }; | |||
| static void loggerLogVa(LoggerLevel level, const char *file, int line, const char *format, va_list args) { | |||
| auto nowTime = std::chrono::high_resolution_clock::now(); | |||
| int duration = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - startTime).count(); | |||
| if (logFile == stderr) | |||
| fprintf(logFile, "\x1B[%dm", loggerColor[level]); | |||
| fprintf(logFile, "[%.03f %s %s:%d] ", duration / 1000.0, loggerText[level], file, line); | |||
| if (logFile == stderr) | |||
| fprintf(logFile, "\x1B[0m"); | |||
| vfprintf(logFile, format, args); | |||
| fprintf(logFile, "\n"); | |||
| fflush(logFile); | |||
| } | |||
| void loggerLog(LoggerLevel level, const char *file, int line, const char *format, ...) { | |||
| va_list args; | |||
| va_start(args, format); | |||
| loggerLogVa(level, file, line, format, args); | |||
| va_end(args); | |||
| } | |||
| /** Deprecated. Included for ABI compatibility */ | |||
| #undef debug | |||
| #undef info | |||
| #undef warn | |||
| #undef fatal | |||
| void debug(const char *format, ...) { | |||
| va_list args; | |||
| va_start(args, format); | |||
| loggerLogVa(DEBUG_LEVEL, "", 0, format, args); | |||
| va_end(args); | |||
| } | |||
| void info(const char *format, ...) { | |||
| va_list args; | |||
| va_start(args, format); | |||
| loggerLogVa(INFO_LEVEL, "", 0, format, args); | |||
| va_end(args); | |||
| } | |||
| void warn(const char *format, ...) { | |||
| va_list args; | |||
| va_start(args, format); | |||
| loggerLogVa(WARN_LEVEL, "", 0, format, args); | |||
| va_end(args); | |||
| } | |||
| void fatal(const char *format, ...) { | |||
| va_list args; | |||
| va_start(args, format); | |||
| loggerLogVa(FATAL_LEVEL, "", 0, format, args); | |||
| va_end(args); | |||
| } | |||
| } // namespace rack | |||
| @@ -27,7 +27,7 @@ void QuantityWidget::setDefaultValue(float defaultValue) { | |||
| std::string QuantityWidget::getText() { | |||
| std::string text = label; | |||
| text += ": "; | |||
| text += stringf("%.*f", precision, value); | |||
| text += string::stringf("%.*f", precision, value); | |||
| text += unit; | |||
| return text; | |||
| } | |||
| @@ -3,7 +3,7 @@ | |||
| #include "asset.hpp" | |||
| #include "gamepad.hpp" | |||
| #include "keyboard.hpp" | |||
| #include "util/color.hpp" | |||
| #include "color.hpp" | |||
| #include <map> | |||
| #include <queue> | |||
| @@ -454,7 +454,7 @@ void windowRun() { | |||
| windowTitle += gApplicationVersion; | |||
| if (!gRackWidget->lastPath.empty()) { | |||
| windowTitle += " - "; | |||
| windowTitle += stringFilename(gRackWidget->lastPath); | |||
| windowTitle += string::filename(gRackWidget->lastPath); | |||
| } | |||
| if (windowTitle != lastWindowTitle) { | |||
| glfwSetWindowTitle(gWindow, windowTitle.c_str()); | |||