@@ -16,3 +16,6 @@ | |||||
[submodule "dep/rtaudio"] | [submodule "dep/rtaudio"] | ||||
path = dep/rtaudio | path = dep/rtaudio | ||||
url = https://github.com/thestk/rtaudio.git | url = https://github.com/thestk/rtaudio.git | ||||
[submodule "dep/argagg"] | |||||
path = dep/argagg | |||||
url = https://github.com/vietjtnguyen/argagg.git |
@@ -50,8 +50,9 @@ nanosvg = include/nanosvg.h | |||||
oui-blendish = include/blendish.h | oui-blendish = include/blendish.h | ||||
osdialog = include/osdialog.h | osdialog = include/osdialog.h | ||||
pffft = include/pffft.h | pffft = include/pffft.h | ||||
argagg = include/argagg.hpp | |||||
DEPS += $(glew) $(glfw) $(jansson) $(libspeexdsp) $(libcurl) $(libzip) $(rtmidi) $(rtaudio) $(nanovg) $(nanosvg) $(oui-blendish) $(osdialog) $(pffft) | |||||
DEPS += $(glew) $(glfw) $(jansson) $(libspeexdsp) $(libcurl) $(libzip) $(rtmidi) $(rtaudio) $(nanovg) $(nanosvg) $(oui-blendish) $(osdialog) $(pffft) $(argagg) | |||||
include $(RACK_DIR)/dep.mk | include $(RACK_DIR)/dep.mk | ||||
@@ -163,6 +164,9 @@ $(pffft): | |||||
$(WGET) "https://bitbucket.org/jpommier/pffft/get/29e4f76ac53b.zip" | $(WGET) "https://bitbucket.org/jpommier/pffft/get/29e4f76ac53b.zip" | ||||
$(UNZIP) 29e4f76ac53b.zip | $(UNZIP) 29e4f76ac53b.zip | ||||
cp jpommier-pffft-29e4f76ac53b/*.h include/ | cp jpommier-pffft-29e4f76ac53b/*.h include/ | ||||
$(argagg): $(wildcard argagg/include/argagg/*.hpp) | |||||
cp argagg/include/argagg/*.hpp include/ | |||||
clean: | clean: | ||||
git clean -fdx | git clean -fdx | ||||
@@ -0,0 +1 @@ | |||||
Subproject commit 24f1dd7fd222b2c70b1f02109761f6bc698e5f68 |
@@ -7,7 +7,7 @@ | |||||
namespace rack { | namespace rack { | ||||
void assetInit(bool devMode); | |||||
void assetInit(bool devMode, std::string customGlobalDir = std::string(), std::string customLocalDir = std::string()); | |||||
/** Returns the path of a global resource. Should only read files from this location. */ | /** Returns the path of a global resource. Should only read files from this location. */ | ||||
std::string assetGlobal(std::string filename); | std::string assetGlobal(std::string filename); | ||||
/** Returns the path of a local resource. Can read and write files to this location. */ | /** Returns the path of a local resource. Can read and write files to this location. */ | ||||
@@ -22,7 +22,7 @@ struct BridgeMidiDriver : MidiDriver { | |||||
}; | }; | ||||
void bridgeInit(); | |||||
void bridgeInit(int customPort = BRIDGE_PORT); | |||||
void bridgeDestroy(); | void bridgeDestroy(); | ||||
void bridgeAudioSubscribe(int channel, AudioIO *audio); | void bridgeAudioSubscribe(int channel, AudioIO *audio); | ||||
void bridgeAudioUnsubscribe(int channel, AudioIO *audio); | void bridgeAudioUnsubscribe(int channel, AudioIO *audio); | ||||
@@ -19,6 +19,7 @@ | |||||
#include <pwd.h> | #include <pwd.h> | ||||
#endif | #endif | ||||
#include <iostream> | |||||
namespace rack { | namespace rack { | ||||
@@ -27,58 +28,88 @@ static std::string globalDir; | |||||
static std::string localDir; | static std::string localDir; | ||||
void assetInit(bool devMode) { | |||||
void assetInit(bool devMode, std::string customGlobalDir, std::string customLocalDir) { | |||||
if (devMode) { | if (devMode) { | ||||
// Use current working directory if running in development mode | |||||
globalDir = "."; | |||||
localDir = "."; | |||||
return; | |||||
// Use current working directory by default if running in development mode | |||||
if (customGlobalDir.empty()) { | |||||
customGlobalDir = "."; | |||||
} | |||||
if (customLocalDir.empty()) { | |||||
customLocalDir = "."; | |||||
} | |||||
} | } | ||||
if (customGlobalDir.empty()) { | |||||
#if ARCH_MAC | #if ARCH_MAC | ||||
CFBundleRef bundle = CFBundleGetMainBundle(); | |||||
assert(bundle); | |||||
CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(bundle); | |||||
char resourcesBuf[PATH_MAX]; | |||||
Boolean success = CFURLGetFileSystemRepresentation(resourcesUrl, TRUE, (UInt8*) resourcesBuf, sizeof(resourcesBuf)); | |||||
assert(success); | |||||
CFRelease(resourcesUrl); | |||||
globalDir = resourcesBuf; | |||||
// Get home directory | |||||
struct passwd *pw = getpwuid(getuid()); | |||||
assert(pw); | |||||
localDir = pw->pw_dir; | |||||
localDir += "/Documents/Rack"; | |||||
CFBundleRef bundle = CFBundleGetMainBundle(); | |||||
assert(bundle); | |||||
CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(bundle); | |||||
char resourcesBuf[PATH_MAX]; | |||||
Boolean success = CFURLGetFileSystemRepresentation(resourcesUrl, TRUE, (UInt8*) resourcesBuf, sizeof(resourcesBuf)); | |||||
assert(success); | |||||
CFRelease(resourcesUrl); | |||||
globalDir = resourcesBuf; | |||||
#endif | #endif | ||||
#if ARCH_WIN | #if ARCH_WIN | ||||
char moduleBuf[MAX_PATH]; | |||||
DWORD length = GetModuleFileName(NULL, moduleBuf, sizeof(moduleBuf)); | |||||
assert(length > 0); | |||||
PathRemoveFileSpec(moduleBuf); | |||||
globalDir = moduleBuf; | |||||
// Get "My Documents" folder | |||||
char documentsBuf[MAX_PATH]; | |||||
HRESULT result = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, documentsBuf); | |||||
assert(result == S_OK); | |||||
localDir = documentsBuf; | |||||
localDir += "/Rack"; | |||||
char moduleBuf[MAX_PATH]; | |||||
DWORD length = GetModuleFileName(NULL, moduleBuf, sizeof(moduleBuf)); | |||||
assert(length > 0); | |||||
PathRemoveFileSpec(moduleBuf); | |||||
globalDir = moduleBuf; | |||||
#endif | #endif | ||||
#if ARCH_LIN | #if ARCH_LIN | ||||
// TODO For now, users should launch Rack from their terminal in the global directory | |||||
globalDir = "."; | |||||
// TODO For now, users should launch Rack from their terminal in the global directory | |||||
globalDir = "."; | |||||
#endif | |||||
} | |||||
else { | |||||
if (!systemIsDirectory(customGlobalDir)) { | |||||
std::cerr << "Selected global directory \"" << customGlobalDir << "\" does not exist or is not a directory, default to current directory." << std::endl; | |||||
globalDir = "."; | |||||
} | |||||
else { | |||||
globalDir = customGlobalDir; | |||||
} | |||||
} | |||||
// Get home directory | |||||
const char *homeBuf = getenv("HOME"); | |||||
if (!homeBuf) { | |||||
if (customLocalDir.empty()) { | |||||
#if ARCH_MAC | |||||
// Get home directory | |||||
struct passwd *pw = getpwuid(getuid()); | struct passwd *pw = getpwuid(getuid()); | ||||
assert(pw); | assert(pw); | ||||
homeBuf = pw->pw_dir; | |||||
} | |||||
localDir = homeBuf; | |||||
localDir += "/.Rack"; | |||||
localDir = pw->pw_dir; | |||||
localDir += "/Documents/Rack"; | |||||
#endif | |||||
#if ARCH_WIN | |||||
// Get "My Documents" folder | |||||
char documentsBuf[MAX_PATH]; | |||||
HRESULT result = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, documentsBuf); | |||||
assert(result == S_OK); | |||||
localDir = documentsBuf; | |||||
localDir += "/Rack"; | |||||
#endif | #endif | ||||
#if ARCH_LIN | |||||
// Get home directory | |||||
const char *homeBuf = getenv("HOME"); | |||||
if (!homeBuf) { | |||||
struct passwd *pw = getpwuid(getuid()); | |||||
assert(pw); | |||||
homeBuf = pw->pw_dir; | |||||
} | |||||
localDir = homeBuf; | |||||
localDir += "/.Rack"; | |||||
#endif | |||||
} | |||||
else { | |||||
if (!systemIsDirectory(customLocalDir)) { | |||||
std::cerr << "Selected local directory \"" << customLocalDir << "\" does not exist or is not a directory, default to current directory." << std::endl; | |||||
localDir = "."; | |||||
} | |||||
else { | |||||
localDir = customLocalDir; | |||||
} | |||||
} | |||||
} | } | ||||
@@ -282,7 +282,7 @@ static void clientRun(int client) { | |||||
} | } | ||||
static void serverConnect() { | |||||
static void serverConnect(int customPort) { | |||||
// Initialize sockets | // Initialize sockets | ||||
#if ARCH_WIN | #if ARCH_WIN | ||||
WSADATA wsaData; | WSADATA wsaData; | ||||
@@ -299,7 +299,8 @@ static void serverConnect() { | |||||
struct sockaddr_in addr; | struct sockaddr_in addr; | ||||
memset(&addr, 0, sizeof(addr)); | memset(&addr, 0, sizeof(addr)); | ||||
addr.sin_family = AF_INET; | addr.sin_family = AF_INET; | ||||
addr.sin_port = htons(BRIDGE_PORT); | |||||
warn("Bridge server port: %i", customPort); | |||||
addr.sin_port = htons(customPort); | |||||
#if ARCH_WIN | #if ARCH_WIN | ||||
addr.sin_addr.s_addr = inet_addr(BRIDGE_HOST); | addr.sin_addr.s_addr = inet_addr(BRIDGE_HOST); | ||||
#else | #else | ||||
@@ -365,10 +366,10 @@ static void serverConnect() { | |||||
} | } | ||||
} | } | ||||
static void serverRun() { | |||||
static void serverRun(int customPort) { | |||||
while (serverRunning) { | while (serverRunning) { | ||||
std::this_thread::sleep_for(std::chrono::duration<double>(0.1)); | std::this_thread::sleep_for(std::chrono::duration<double>(0.1)); | ||||
serverConnect(); | |||||
serverConnect(customPort); | |||||
} | } | ||||
} | } | ||||
@@ -403,9 +404,9 @@ void BridgeMidiDriver::unsubscribeInputDevice(int deviceId, MidiInput *midiInput | |||||
} | } | ||||
void bridgeInit() { | |||||
void bridgeInit(int customPort) { | |||||
serverRunning = true; | serverRunning = true; | ||||
serverThread = std::thread(serverRun); | |||||
serverThread = std::thread(serverRun, customPort); | |||||
driver = new BridgeMidiDriver(); | driver = new BridgeMidiDriver(); | ||||
midiDriverAdd(BRIDGE_DRIVER, driver); | midiDriverAdd(BRIDGE_DRIVER, driver); | ||||
@@ -13,6 +13,8 @@ | |||||
#include "util/color.hpp" | #include "util/color.hpp" | ||||
#include "osdialog.h" | #include "osdialog.h" | ||||
#include "argagg.hpp" | |||||
#include <unistd.h> | #include <unistd.h> | ||||
@@ -22,25 +24,58 @@ using namespace rack; | |||||
int main(int argc, char* argv[]) { | int main(int argc, char* argv[]) { | ||||
bool devMode = false; | bool devMode = false; | ||||
std::string patchFile; | std::string patchFile; | ||||
std::string customLocalDir; | |||||
std::string customGlobalDir; | |||||
int customBridgePort = -1; | |||||
// Parse command line arguments | // Parse command line arguments | ||||
int c; | |||||
opterr = 0; | |||||
while ((c = getopt(argc, argv, "d")) != -1) { | |||||
switch (c) { | |||||
case 'd': { | |||||
devMode = true; | |||||
} break; | |||||
default: break; | |||||
} | |||||
argagg::parser argparser {{ | |||||
{ "help", {"-h", "--help"}, "shows this help message", 0}, | |||||
{ "devmod", {"-d", "--devmod"}, "enables dev mode (will default local/global folders to current folder)", 0}, | |||||
{ "global", {"-g", "--globaldir"}, "set golbalDir", 1}, | |||||
{ "local", {"-l", "--localdir"}, "set localDir", 1}, | |||||
{ "port", {"-p", "--port"}, "Bridge port number", 1}, | |||||
}}; | |||||
argagg::parser_results args; | |||||
try { | |||||
args = argparser.parse(argc, argv); | |||||
} catch (const std::exception& e) { | |||||
std::cerr << "Encountered exception while parsing arguments: " << e.what() << std::endl; | |||||
return EXIT_FAILURE; | |||||
} | |||||
if (args["help"]) { | |||||
std::cerr << "Usage: program [options] [FILENAME]" << std::endl; | |||||
std::cerr << argparser; | |||||
return EXIT_SUCCESS; | |||||
} | |||||
if (args["devmod"]) { | |||||
devMode = true; | |||||
} | |||||
if (args["global"]) { | |||||
customGlobalDir = args["global"].as<std::string>(); | |||||
} | } | ||||
if (optind < argc) { | |||||
patchFile = argv[optind]; | |||||
if (args["local"]) { | |||||
customLocalDir = args["local"].as<std::string>(); | |||||
} | |||||
if (args["port"]) { | |||||
customBridgePort = args["port"].as<int>(); | |||||
} | |||||
// Filename as first positional argument | |||||
if (args.pos.size() > 0) { | |||||
patchFile = args.as<std::string>(0); | |||||
} | } | ||||
// Initialize environment | // Initialize environment | ||||
randomInit(); | randomInit(); | ||||
assetInit(devMode); | |||||
assetInit(devMode, customGlobalDir, customLocalDir); | |||||
loggerInit(devMode); | loggerInit(devMode); | ||||
// Log environment | // Log environment | ||||
@@ -54,7 +89,12 @@ int main(int argc, char* argv[]) { | |||||
pluginInit(devMode); | pluginInit(devMode); | ||||
engineInit(); | engineInit(); | ||||
rtmidiInit(); | rtmidiInit(); | ||||
bridgeInit(); | |||||
if (customBridgePort > 0) { | |||||
bridgeInit(customBridgePort); | |||||
} | |||||
else { | |||||
bridgeInit(); | |||||
} | |||||
keyboardInit(); | keyboardInit(); | ||||
gamepadInit(); | gamepadInit(); | ||||
windowInit(); | windowInit(); | ||||