@@ -16,3 +16,6 @@ | |||
[submodule "dep/rtaudio"] | |||
path = dep/rtaudio | |||
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 | |||
osdialog = include/osdialog.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 | |||
@@ -163,6 +164,9 @@ $(pffft): | |||
$(WGET) "https://bitbucket.org/jpommier/pffft/get/29e4f76ac53b.zip" | |||
$(UNZIP) 29e4f76ac53b.zip | |||
cp jpommier-pffft-29e4f76ac53b/*.h include/ | |||
$(argagg): $(wildcard argagg/include/argagg/*.hpp) | |||
cp argagg/include/argagg/*.hpp include/ | |||
clean: | |||
git clean -fdx | |||
@@ -0,0 +1 @@ | |||
Subproject commit 24f1dd7fd222b2c70b1f02109761f6bc698e5f68 |
@@ -7,7 +7,7 @@ | |||
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. */ | |||
std::string assetGlobal(std::string filename); | |||
/** 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 bridgeAudioSubscribe(int channel, AudioIO *audio); | |||
void bridgeAudioUnsubscribe(int channel, AudioIO *audio); | |||
@@ -19,6 +19,7 @@ | |||
#include <pwd.h> | |||
#endif | |||
#include <iostream> | |||
namespace rack { | |||
@@ -27,58 +28,88 @@ static std::string globalDir; | |||
static std::string localDir; | |||
void assetInit(bool devMode) { | |||
void assetInit(bool devMode, std::string customGlobalDir, std::string customLocalDir) { | |||
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 | |||
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 | |||
#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 | |||
#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()); | |||
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 | |||
#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 | |||
#if ARCH_WIN | |||
WSADATA wsaData; | |||
@@ -299,7 +299,8 @@ static void serverConnect() { | |||
struct sockaddr_in addr; | |||
memset(&addr, 0, sizeof(addr)); | |||
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 | |||
addr.sin_addr.s_addr = inet_addr(BRIDGE_HOST); | |||
#else | |||
@@ -365,10 +366,10 @@ static void serverConnect() { | |||
} | |||
} | |||
static void serverRun() { | |||
static void serverRun(int customPort) { | |||
while (serverRunning) { | |||
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; | |||
serverThread = std::thread(serverRun); | |||
serverThread = std::thread(serverRun, customPort); | |||
driver = new BridgeMidiDriver(); | |||
midiDriverAdd(BRIDGE_DRIVER, driver); | |||
@@ -13,6 +13,8 @@ | |||
#include "util/color.hpp" | |||
#include "osdialog.h" | |||
#include "argagg.hpp" | |||
#include <unistd.h> | |||
@@ -22,25 +24,58 @@ using namespace rack; | |||
int main(int argc, char* argv[]) { | |||
bool devMode = false; | |||
std::string patchFile; | |||
std::string customLocalDir; | |||
std::string customGlobalDir; | |||
int customBridgePort = -1; | |||
// 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 | |||
randomInit(); | |||
assetInit(devMode); | |||
assetInit(devMode, customGlobalDir, customLocalDir); | |||
loggerInit(devMode); | |||
// Log environment | |||
@@ -54,7 +89,12 @@ int main(int argc, char* argv[]) { | |||
pluginInit(devMode); | |||
engineInit(); | |||
rtmidiInit(); | |||
bridgeInit(); | |||
if (customBridgePort > 0) { | |||
bridgeInit(customBridgePort); | |||
} | |||
else { | |||
bridgeInit(); | |||
} | |||
keyboardInit(); | |||
gamepadInit(); | |||
windowInit(); | |||