@@ -1,7 +1,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "util/common.hpp" | #include "util/common.hpp" | ||||
#include "string.hpp" | |||||
#include "nanovg.h" | #include "nanovg.h" | ||||
@@ -87,9 +87,9 @@ inline std::string toHexString(NVGcolor c) { | |||||
uint8_t b = std::round(c.b * 255); | uint8_t b = std::round(c.b * 255); | ||||
uint8_t a = std::round(c.a * 255); | uint8_t a = std::round(c.a * 255); | ||||
if (a == 255) | if (a == 255) | ||||
return stringf("#%02x%02x%02x", r, g, b); | |||||
return string::stringf("#%02x%02x%02x", r, g, b); | |||||
else | 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 | #pragma once | ||||
#include "util/common.hpp" | |||||
#include <cmath> | #include <cmath> | ||||
#include <cstdlib> | #include <cstdlib> | ||||
#include <algorithm> // for std::min, max | #include <algorithm> // for std::min, max | ||||
#include "macros.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -15,6 +15,8 @@ namespace rack { | |||||
using namespace math; | using namespace math; | ||||
using namespace string; | |||||
using namespace logger; | |||||
} // namespace rack | } // namespace rack |
@@ -1,13 +1,16 @@ | |||||
#pragma once | |||||
#include "util/common.hpp" | #include "util/common.hpp" | ||||
#include <stdarg.h> | |||||
#include <algorithm> | |||||
#include <cstdarg> | |||||
#include <libgen.h> // for dirname and basename | #include <libgen.h> // for dirname and basename | ||||
namespace rack { | 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_list args; | ||||
va_start(args, format); | va_start(args, format); | ||||
// Compute size of required buffer | // Compute size of required buffer | ||||
@@ -24,51 +27,60 @@ std::string stringf(const char *format, ...) { | |||||
return s; | return s; | ||||
} | } | ||||
std::string stringLowercase(std::string s) { | |||||
inline std::string lowercase(std::string s) { | |||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower); | std::transform(s.begin(), s.end(), s.begin(), ::tolower); | ||||
return s; | return s; | ||||
} | } | ||||
std::string stringUppercase(std::string s) { | |||||
inline std::string uppercase(std::string s) { | |||||
std::transform(s.begin(), s.end(), s.begin(), ::toupper); | std::transform(s.begin(), s.end(), s.begin(), ::toupper); | ||||
return s; | 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) | if (s.size() <= len) | ||||
return s; | return s; | ||||
else | else | ||||
return s.substr(0, len - 3) + "..."; | 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; | 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; | 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()); | char *pathDup = strdup(path.c_str()); | ||||
std::string directory = dirname(pathDup); | std::string directory = dirname(pathDup); | ||||
free(pathDup); | free(pathDup); | ||||
return directory; | return directory; | ||||
} | } | ||||
std::string stringFilename(std::string path) { | |||||
inline std::string filename(std::string path) { | |||||
char *pathDup = strdup(path.c_str()); | char *pathDup = strdup(path.c_str()); | ||||
std::string filename = basename(pathDup); | std::string filename = basename(pathDup); | ||||
free(pathDup); | free(pathDup); | ||||
return filename; | 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) | if (!ext) | ||||
return ""; | return ""; | ||||
return ext + 1; | 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 | } // namespace rack |
@@ -13,77 +13,10 @@ | |||||
#include <condition_variable> | #include <condition_variable> | ||||
#include <mutex> | #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 "math.hpp" | ||||
#include "string.hpp" | |||||
#include "logger.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -148,32 +81,6 @@ float randomNormal(); | |||||
DEPRECATED inline float randomf() {return randomUniform();} | 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 | // Operating-system specific utilities | ||||
// system.cpp | // system.cpp | ||||
@@ -196,26 +103,6 @@ void systemOpenBrowser(std::string url); | |||||
// logger.cpp | // 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 | // Thread functions | ||||
@@ -7,7 +7,7 @@ | |||||
#include "util/common.hpp" | #include "util/common.hpp" | ||||
#include "events.hpp" | #include "events.hpp" | ||||
#include "util/color.hpp" | |||||
#include "color.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -121,13 +121,13 @@ struct MidiCcChoice : GridChoice { | |||||
void step() override { | void step() override { | ||||
if (module->learningId == id) { | if (module->learningId == id) { | ||||
if (0 <= focusCc) | if (0 <= focusCc) | ||||
text = stringf("%d", focusCc); | |||||
text = string::stringf("%d", focusCc); | |||||
else | else | ||||
text = "LRN"; | text = "LRN"; | ||||
color.a = 0.5; | color.a = 0.5; | ||||
} | } | ||||
else { | else { | ||||
text = stringf("%d", module->learnedCcs[id]); | |||||
text = string::stringf("%d", module->learnedCcs[id]); | |||||
color.a = 1.0; | color.a = 1.0; | ||||
if (gFocusedWidget == this) | if (gFocusedWidget == this) | ||||
gFocusedWidget = NULL; | gFocusedWidget = NULL; | ||||
@@ -317,7 +317,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||||
menu->addChild(construct<MenuLabel>()); | menu->addChild(construct<MenuLabel>()); | ||||
for (int i = 0; i < 2; i++) { | 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->module = module; | ||||
item->index = i; | item->index = i; | ||||
menu->addChild(item); | menu->addChild(item); | ||||
@@ -168,7 +168,7 @@ struct MidiTrigChoice : GridChoice { | |||||
}; | }; | ||||
int oct = note / 12 - 1; | int oct = note / 12 - 1; | ||||
int semi = note % 12; | int semi = note % 12; | ||||
text = stringf("%s%d", noteNames[semi], oct); | |||||
text = string::stringf("%s%d", noteNames[semi], oct); | |||||
color.a = 1.0; | color.a = 1.0; | ||||
if (gFocusedWidget == this) | if (gFocusedWidget == this) | ||||
@@ -106,13 +106,13 @@ struct AudioSampleRateChoice : LedDisplayChoice { | |||||
AudioSampleRateItem *item = new AudioSampleRateItem(); | AudioSampleRateItem *item = new AudioSampleRateItem(); | ||||
item->audioIO = audioWidget->audioIO; | item->audioIO = audioWidget->audioIO; | ||||
item->sampleRate = sampleRate; | item->sampleRate = sampleRate; | ||||
item->text = stringf("%d Hz", sampleRate); | |||||
item->text = string::stringf("%d Hz", sampleRate); | |||||
item->rightText = CHECKMARK(item->sampleRate == audioWidget->audioIO->sampleRate); | item->rightText = CHECKMARK(item->sampleRate == audioWidget->audioIO->sampleRate); | ||||
menu->addChild(item); | menu->addChild(item); | ||||
} | } | ||||
} | } | ||||
void step() override { | 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->audioIO = audioWidget->audioIO; | ||||
item->blockSize = blockSize; | item->blockSize = blockSize; | ||||
float latency = (float) blockSize / audioWidget->audioIO->sampleRate * 1000.0; | 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); | item->rightText = CHECKMARK(item->blockSize == audioWidget->audioIO->blockSize); | ||||
menu->addChild(item); | menu->addChild(item); | ||||
} | } | ||||
} | } | ||||
void step() override { | void step() override { | ||||
text = stringf("%d", audioWidget->audioIO->blockSize); | |||||
text = string::stringf("%d", audioWidget->audioIO->blockSize); | |||||
} | } | ||||
}; | }; | ||||
@@ -1,5 +1,5 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "util/color.hpp" | |||||
#include "color.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -18,8 +18,8 @@ static ModelTag sTagFilter = NO_TAG; | |||||
bool isMatch(std::string s, std::string search) { | 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); | return (s.find(search) != std::string::npos); | ||||
} | } | ||||
@@ -279,7 +279,7 @@ struct ModuleBrowser : OpaqueWidget { | |||||
SearchModuleField *searchField; | SearchModuleField *searchField; | ||||
ScrollWidget *moduleScroll; | ScrollWidget *moduleScroll; | ||||
BrowserList *moduleList; | BrowserList *moduleList; | ||||
std::set<std::string, StringCaseInsensitiveCompare> availableAuthors; | |||||
std::set<std::string, string::CaseInsensitiveCompare> availableAuthors; | |||||
std::set<ModelTag> availableTags; | std::set<ModelTag> availableTags; | ||||
ModuleBrowser() { | ModuleBrowser() { | ||||
@@ -203,7 +203,7 @@ void ModuleWidget::load(std::string filename) { | |||||
json_decref(moduleJ); | json_decref(moduleJ); | ||||
} | } | ||||
else { | 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()); | osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | ||||
} | } | ||||
@@ -248,7 +248,7 @@ void ModuleWidget::saveDialog() { | |||||
if (path) { | if (path) { | ||||
std::string pathStr = path; | std::string pathStr = path; | ||||
free(path); | free(path); | ||||
std::string extension = stringExtension(pathStr); | |||||
std::string extension = string::extension(pathStr); | |||||
if (extension.empty()) { | if (extension.empty()) { | ||||
pathStr += ".vcvm"; | pathStr += ".vcvm"; | ||||
} | } | ||||
@@ -304,7 +304,7 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||||
nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.5)); | nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.5)); | ||||
nvgFill(vg); | 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); | nvgFontFaceId(vg, gGuiFont->handle); | ||||
nvgFontSize(vg, 12); | nvgFontSize(vg, 12); | ||||
nvgFillColor(vg, nvgRGBf(1, 1, 1)); | nvgFillColor(vg, nvgRGBf(1, 1, 1)); | ||||
@@ -1,5 +1,5 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "util/color.hpp" | |||||
#include "color.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -44,7 +44,7 @@ void RackScene::step() { | |||||
// Version popup message | // Version popup message | ||||
if (!gLatestVersion.empty()) { | 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())) { | if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, versionMessage.c_str())) { | ||||
std::thread t(systemOpenBrowser, "https://vcvrack.com/"); | std::thread t(systemOpenBrowser, "https://vcvrack.com/"); | ||||
t.detach(); | t.detach(); | ||||
@@ -116,7 +116,7 @@ void RackScene::onHoverKey(EventHoverKey &e) { | |||||
void RackScene::onPathDrop(EventPathDrop &e) { | void RackScene::onPathDrop(EventPathDrop &e) { | ||||
if (e.paths.size() >= 1) { | if (e.paths.size() >= 1) { | ||||
const std::string &firstPath = e.paths.front(); | const std::string &firstPath = e.paths.front(); | ||||
if (stringExtension(firstPath) == "vcv") { | |||||
if (string::extension(firstPath) == "vcv") { | |||||
gRackWidget->load(firstPath); | gRackWidget->load(firstPath); | ||||
e.consumed = true; | e.consumed = true; | ||||
} | } | ||||
@@ -75,7 +75,7 @@ void RackWidget::loadDialog() { | |||||
systemCreateDirectory(dir); | systemCreateDirectory(dir); | ||||
} | } | ||||
else { | else { | ||||
dir = stringDirectory(lastPath); | |||||
dir = string::directory(lastPath); | |||||
} | } | ||||
osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | ||||
char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); | char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); | ||||
@@ -104,8 +104,8 @@ void RackWidget::saveAsDialog() { | |||||
systemCreateDirectory(dir); | systemCreateDirectory(dir); | ||||
} | } | ||||
else { | 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()); | osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | ||||
char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters); | char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters); | ||||
@@ -113,7 +113,7 @@ void RackWidget::saveAsDialog() { | |||||
if (path) { | if (path) { | ||||
std::string pathStr = path; | std::string pathStr = path; | ||||
free(path); | free(path); | ||||
std::string extension = stringExtension(pathStr); | |||||
std::string extension = string::extension(pathStr); | |||||
if (extension.empty()) { | if (extension.empty()) { | ||||
pathStr += ".vcv"; | pathStr += ".vcv"; | ||||
} | } | ||||
@@ -155,7 +155,7 @@ void RackWidget::load(std::string filename) { | |||||
json_decref(rootJ); | json_decref(rootJ); | ||||
} | } | ||||
else { | 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()); | 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. | // Detect old patches with ModuleWidget::params/inputs/outputs indices. | ||||
// (We now use Module::params/inputs/outputs indices.) | // (We now use Module::params/inputs/outputs indices.) | ||||
int legacy = 0; | 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; | legacy = 1; | ||||
} | } | ||||
if (legacy) { | if (legacy) { | ||||
@@ -300,7 +300,7 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
json_t *modelSlugJ = json_object_get(moduleJ, "model"); | json_t *modelSlugJ = json_object_get(moduleJ, "model"); | ||||
std::string pluginSlug = json_string_value(pluginSlugJ); | std::string pluginSlug = json_string_value(pluginSlugJ); | ||||
std::string modelSlug = json_string_value(modelSlugJ); | 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}; | std::vector<float> sampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; | ||||
for (float sampleRate : sampleRates) { | for (float sampleRate : sampleRates) { | ||||
SampleRateItem *item = new SampleRateItem(); | SampleRateItem *item = new SampleRateItem(); | ||||
item->text = stringf("%.0f Hz", sampleRate); | |||||
item->text = string::stringf("%.0f Hz", sampleRate); | |||||
item->rightText = CHECKMARK(engineGetSampleRate() == sampleRate); | item->rightText = CHECKMARK(engineGetSampleRate() == sampleRate); | ||||
item->sampleRate = sampleRate; | item->sampleRate = sampleRate; | ||||
menu->addChild(item); | menu->addChild(item); | ||||
@@ -18,8 +18,9 @@ std::vector<int> AudioIO::getDrivers() { | |||||
std::vector<RtAudio::Api> apis; | std::vector<RtAudio::Api> apis; | ||||
RtAudio::getCompiledApi(apis); | RtAudio::getCompiledApi(apis); | ||||
std::vector<int> drivers; | std::vector<int> drivers; | ||||
for (RtAudio::Api api : apis) | |||||
for (RtAudio::Api api : apis) { | |||||
drivers.push_back((int) api); | drivers.push_back((int) api); | ||||
} | |||||
// Add fake Bridge driver | // Add fake Bridge driver | ||||
drivers.push_back(BRIDGE_DRIVER); | drivers.push_back(BRIDGE_DRIVER); | ||||
return drivers; | return drivers; | ||||
@@ -121,7 +122,7 @@ std::string AudioIO::getDeviceName(int device) { | |||||
return deviceInfo.name; | return deviceInfo.name; | ||||
} | } | ||||
else if (driver == BRIDGE_DRIVER) { | else if (driver == BRIDGE_DRIVER) { | ||||
return stringf("%d", device + 1); | |||||
return string::stringf("%d", device + 1); | |||||
} | } | ||||
return ""; | return ""; | ||||
} | } | ||||
@@ -133,19 +134,19 @@ std::string AudioIO::getDeviceDetail(int device, int offset) { | |||||
if (rtAudio) { | if (rtAudio) { | ||||
RtAudio::DeviceInfo deviceInfo; | RtAudio::DeviceInfo deviceInfo; | ||||
if (getDeviceInfo(device, &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) | 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) | if (offset < (int) deviceInfo.inputChannels && offset < (int) deviceInfo.outputChannels) | ||||
deviceDetail += ", "; | deviceDetail += ", "; | ||||
if (offset < (int) deviceInfo.outputChannels) | 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 += ")"; | deviceDetail += ")"; | ||||
return deviceDetail; | return deviceDetail; | ||||
} | } | ||||
} | } | ||||
else if (driver == BRIDGE_DRIVER) { | else if (driver == BRIDGE_DRIVER) { | ||||
return stringf("Port %d", device + 1); | |||||
return string::stringf("Port %d", device + 1); | |||||
} | } | ||||
return ""; | return ""; | ||||
} | } | ||||
@@ -384,7 +384,7 @@ std::vector<int> BridgeMidiDriver::getInputDeviceIds() { | |||||
std::string BridgeMidiDriver::getInputDeviceName(int deviceId) { | std::string BridgeMidiDriver::getInputDeviceName(int deviceId) { | ||||
if (deviceId < 0) | if (deviceId < 0) | ||||
return ""; | return ""; | ||||
return stringf("Port %d", deviceId + 1); | |||||
return string::stringf("Port %d", deviceId + 1); | |||||
} | } | ||||
MidiInputDevice *BridgeMidiDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) { | MidiInputDevice *BridgeMidiDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) { | ||||
@@ -1,5 +1,5 @@ | |||||
#include "gamepad.hpp" | |||||
#include <GLFW/glfw3.h> | #include <GLFW/glfw3.h> | ||||
#include "gamepad.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -22,7 +22,7 @@ void GamepadInputDevice::step() { | |||||
ccs.resize(numAxes); | ccs.resize(numAxes); | ||||
for (int i = 0; i < numAxes; i++) { | for (int i = 0; i < numAxes; i++) { | ||||
// Allow CC value to go negative, but math::clamp at -127 instead of -128 for symmetry | // 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]) { | if (cc != ccs[i]) { | ||||
ccs[i] = cc; | ccs[i] = cc; | ||||
@@ -77,7 +77,7 @@ std::string GamepadDriver::getInputDeviceName(int deviceId) { | |||||
if (name) { | if (name) { | ||||
return 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) { | 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 "rtmidi.hpp" | ||||
#include "keyboard.hpp" | #include "keyboard.hpp" | ||||
#include "gamepad.hpp" | #include "gamepad.hpp" | ||||
#include "util/color.hpp" | |||||
#include "color.hpp" | |||||
#include "osdialog.h" | #include "osdialog.h" | ||||
#include <unistd.h> | #include <unistd.h> | ||||
@@ -61,7 +61,7 @@ int main(int argc, char* argv[]) { | |||||
// Initialize environment | // Initialize environment | ||||
randomInit(); | randomInit(); | ||||
assetInit(devMode); | assetInit(devMode); | ||||
loggerInit(devMode); | |||||
logger::init(devMode); | |||||
// Log environment | // Log environment | ||||
info("%s %s", gApplicationName.c_str(), gApplicationVersion.c_str()); | info("%s %s", gApplicationName.c_str(), gApplicationVersion.c_str()); | ||||
@@ -116,7 +116,7 @@ int main(int argc, char* argv[]) { | |||||
engineDestroy(); | engineDestroy(); | ||||
midiDestroy(); | midiDestroy(); | ||||
pluginDestroy(); | pluginDestroy(); | ||||
loggerDestroy(); | |||||
logger::destroy(); | |||||
return 0; | return 0; | ||||
} | } |
@@ -79,7 +79,7 @@ std::string MidiIO::getChannelName(int channel) { | |||||
if (channel == -1) | if (channel == -1) | ||||
return "All channels"; | return "All channels"; | ||||
else | else | ||||
return stringf("Channel %d", channel + 1); | |||||
return string::stringf("Channel %d", channel + 1); | |||||
} | } | ||||
json_t *MidiIO::toJson() { | json_t *MidiIO::toJson() { | ||||
@@ -216,7 +216,7 @@ static void loadPlugins(std::string path) { | |||||
if (!systemIsDirectory(pluginPath)) | if (!systemIsDirectory(pluginPath)) | ||||
continue; | continue; | ||||
if (!loadPlugin(pluginPath)) { | 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()) { | if (!message.empty()) { | ||||
@@ -298,13 +298,13 @@ static void extractPackages(std::string path) { | |||||
std::string message; | std::string message; | ||||
for (std::string packagePath : systemListEntries(path)) { | for (std::string packagePath : systemListEntries(path)) { | ||||
if (stringExtension(packagePath) != "zip") | |||||
if (string::extension(packagePath) != "zip") | |||||
continue; | continue; | ||||
info("Extracting package %s", packagePath.c_str()); | info("Extracting package %s", packagePath.c_str()); | ||||
// Extract package | // Extract package | ||||
if (extractZip(packagePath.c_str(), path.c_str())) { | if (extractZip(packagePath.c_str(), path.c_str())) { | ||||
warn("Package %s failed to extract", packagePath.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; | continue; | ||||
} | } | ||||
// Remove package | // Remove package | ||||
@@ -1,6 +1,6 @@ | |||||
#include "ui.hpp" | #include "ui.hpp" | ||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "util/color.hpp" | |||||
#include "color.hpp" | |||||
namespace rack { | 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 QuantityWidget::getText() { | ||||
std::string text = label; | std::string text = label; | ||||
text += ": "; | text += ": "; | ||||
text += stringf("%.*f", precision, value); | |||||
text += string::stringf("%.*f", precision, value); | |||||
text += unit; | text += unit; | ||||
return text; | return text; | ||||
} | } | ||||
@@ -3,7 +3,7 @@ | |||||
#include "asset.hpp" | #include "asset.hpp" | ||||
#include "gamepad.hpp" | #include "gamepad.hpp" | ||||
#include "keyboard.hpp" | #include "keyboard.hpp" | ||||
#include "util/color.hpp" | |||||
#include "color.hpp" | |||||
#include <map> | #include <map> | ||||
#include <queue> | #include <queue> | ||||
@@ -454,7 +454,7 @@ void windowRun() { | |||||
windowTitle += gApplicationVersion; | windowTitle += gApplicationVersion; | ||||
if (!gRackWidget->lastPath.empty()) { | if (!gRackWidget->lastPath.empty()) { | ||||
windowTitle += " - "; | windowTitle += " - "; | ||||
windowTitle += stringFilename(gRackWidget->lastPath); | |||||
windowTitle += string::filename(gRackWidget->lastPath); | |||||
} | } | ||||
if (windowTitle != lastWindowTitle) { | if (windowTitle != lastWindowTitle) { | ||||
glfwSetWindowTitle(gWindow, windowTitle.c_str()); | glfwSetWindowTitle(gWindow, windowTitle.c_str()); | ||||