Browse Source

Merge branch 'v1' of https://github.com/The-XOR/Rack into v1-x

# Conflicts:
#	.gitignore
pull/1514/head^2
The XOR 5 years ago
parent
commit
c97212c3b2
15 changed files with 250 additions and 165 deletions
  1. +2
    -2
      .gitignore
  2. +7
    -0
      CHANGELOG.md
  3. +30
    -1
      LICENSE-dist.txt
  4. +5
    -5
      Makefile
  5. +45
    -18
      dep/Makefile
  6. +8
    -8
      helper.py
  7. +1
    -1
      include/math.hpp
  8. +5
    -0
      include/system.hpp
  9. +34
    -34
      src/app/ModuleBrowser.cpp
  10. +1
    -0
      src/app/ModuleWidget.cpp
  11. +11
    -4
      src/core/MIDI_Map.cpp
  12. +2
    -72
      src/plugin.cpp
  13. +71
    -0
      src/system.cpp
  14. +18
    -17
      src/tag.cpp
  15. +10
    -3
      src/updater.cpp

+ 2
- 2
.gitignore View File

@@ -12,5 +12,5 @@
/autosave.vcv
/settings.json
/screenshots
/.vs
/_ReSharper.Caches
.vs/
_ReSharper.Caches/

+ 7
- 0
CHANGELOG.md View File

@@ -2,6 +2,13 @@

In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac.

### 1.1.5 (in development)
- Swap order of tags and brands in Module Browser.
- Disable smoothing for MIDI CC buttons in MIDI-Map.
- Automatically unzip update on Mac.
- API
- Add libsamplerate library.

### 1.1.4 (2019-08-22)
- Fix parameter smoothing of MIDI-Map.
- Sort modules within plugin in the Module Browser according to plugin rather than alphabetically.


+ 30
- 1
LICENSE-dist.txt View File

@@ -536,4 +536,33 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.


# libsamplerate

Copyright (c) 2012-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 5
- 5
Makefile View File

@@ -1,6 +1,6 @@
RACK_DIR ?= .
# VERSION := 1.dev.$(shell git rev-parse --short HEAD)
VERSION := 1.1.4
VERSION := 1.dev.$(shell git rev-parse --short HEAD)
# VERSION := 1.1.4

FLAGS += -DVERSION=$(VERSION)
FLAGS += -Iinclude -Idep/include
@@ -21,7 +21,7 @@ ifdef ARCH_LIN
build/dep/osdialog/osdialog_gtk2.c.o: FLAGS += $(shell pkg-config --cflags gtk+-2.0)

LDFLAGS += -rdynamic \
dep/lib/libglfw3.a dep/lib/libGLEW.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libzip.a dep/lib/libz.a dep/lib/librtmidi.a dep/lib/librtaudio.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a \
dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a \
-lpthread -lGL -ldl -lX11 -lasound -ljack \
$(shell pkg-config --libs gtk+-2.0)
TARGET := Rack
@@ -31,14 +31,14 @@ ifdef ARCH_MAC
SOURCES += dep/osdialog/osdialog_mac.m
LDFLAGS += -lpthread -ldl \
-framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -framework CoreAudio -framework CoreMIDI \
dep/lib/libglfw3.a dep/lib/libGLEW.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libzip.a dep/lib/libz.a dep/lib/librtaudio.a dep/lib/librtmidi.a dep/lib/libcrypto.a dep/lib/libssl.a dep/lib/libcurl.a
dep/lib/libGLEW.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/librtmidi.a dep/lib/librtaudio.a
TARGET := Rack
endif

ifdef ARCH_WIN
SOURCES += dep/osdialog/osdialog_win.c
LDFLAGS += -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \
dep/lib/libglew32.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/librtaudio.a dep/lib/librtmidi.a \
dep/lib/libglew32.a dep/lib/libglfw3.a dep/lib/libjansson.a dep/lib/libspeexdsp.a dep/lib/libsamplerate.a dep/lib/libzip.a dep/lib/libz.a dep/lib/libcurl.a dep/lib/libssl.a dep/lib/libcrypto.a dep/lib/librtaudio.a dep/lib/librtmidi.a \
-lpthread -lopengl32 -lgdi32 -lws2_32 -lcomdlg32 -lole32 -ldsound -lwinmm -lksuser -lshlwapi -lmfplat -lmfuuid -lwmcodecdspuuid -ldbghelp
TARGET := Rack.exe
OBJECTS += Rack.res


+ 45
- 18
dep/Makefile View File

@@ -10,39 +10,42 @@ ifdef ARCH_LIN
glew = lib/libGLEW.a
glfw = lib/libglfw3.a
jansson = lib/libjansson.a
libspeexdsp = lib/libspeexdsp.a
openssl = lib/libssl.a
libcurl = lib/libcurl.a
libzip = lib/libzip.a
zlib = lib/libz.a
libspeexdsp = lib/libspeexdsp.a
libsamplerate = lib/libsamplerate.a
rtmidi = lib/librtmidi.a
rtaudio = lib/librtaudio.a
openssl = lib/libssl.a
endif

ifdef ARCH_MAC
glew = lib/libGLEW.a
glfw = lib/libglfw3.a
jansson = lib/libjansson.a
libspeexdsp = lib/libspeexdsp.a
openssl = lib/libssl.a
libcurl = lib/libcurl.a
libzip = lib/libzip.a
zlib = lib/libz.a
libspeexdsp = lib/libspeexdsp.a
libsamplerate = lib/libsamplerate.a
rtmidi = lib/librtmidi.a
rtaudio = lib/librtaudio.a
openssl = lib/libssl.a
endif

ifdef ARCH_WIN
glew = lib/libglew32.a
glfw = lib/libglfw3.a
jansson = lib/libjansson.a
libspeexdsp = lib/libspeexdsp.a
openssl = lib/libssl.a
libcurl = lib/libcurl.a
libzip = lib/libzip.a
zlib = lib/libz.a
libspeexdsp = lib/libspeexdsp.a
libsamplerate = lib/libsamplerate.a
rtmidi = lib/librtmidi.a
rtaudio = lib/librtaudio.a
openssl = lib/libssl.a
endif

nanovg = include/nanovg.h
@@ -51,7 +54,20 @@ oui-blendish = include/blendish.h
osdialog = include/osdialog.h
pffft = include/pffft.h

DEPS += $(glew) $(glfw) $(jansson) $(libspeexdsp) $(libcurl) $(libzip) $(rtmidi) $(rtaudio) $(nanovg) $(nanosvg) $(oui-blendish) $(osdialog) $(pffft)
DEPS += $(glew)
DEPS += $(glfw)
DEPS += $(jansson)
DEPS += $(libcurl)
DEPS += $(libzip)
DEPS += $(libspeexdsp)
DEPS += $(libsamplerate)
DEPS += $(rtmidi)
DEPS += $(rtaudio)
DEPS += $(nanovg)
DEPS += $(nanosvg)
DEPS += $(oui-blendish)
DEPS += $(osdialog)
DEPS += $(pffft)


DEP_LOCAL := .
@@ -90,17 +106,6 @@ $(jansson): jansson-2.12
$(MAKE) -C jansson-2.12
$(MAKE) -C jansson-2.12 install

speexdsp-SpeexDSP-1.2rc3:
$(WGET) "https://vcvrack.com/downloads/dep/speexdsp-SpeexDSP-1.2rc3.tgz"
$(SHA256) speexdsp-SpeexDSP-1.2rc3.tgz c8dded1454747f65956f981c95e7f89a06abdaa2a53e8aeaa66bab2a3d59cebd
$(UNTAR) speexdsp-SpeexDSP-1.2rc3.tgz
rm speexdsp-SpeexDSP-1.2rc3.tgz

$(libspeexdsp): speexdsp-SpeexDSP-1.2rc3
cd speexdsp-SpeexDSP-1.2rc3 && $(CONFIGURE)
$(MAKE) -C speexdsp-SpeexDSP-1.2rc3
$(MAKE) -C speexdsp-SpeexDSP-1.2rc3 install

openssl-1.1.1b:
$(WGET) "https://www.openssl.org/source/openssl-1.1.1b.tar.gz"
$(SHA256) openssl-1.1.1b.tar.gz 5c557b023230413dfb0756f3137a13e6d726838ccd1430888ad15bfb2b43ea4b
@@ -157,6 +162,28 @@ else
$(MAKE) -C zlib-1.2.11 install
endif

speexdsp-SpeexDSP-1.2rc3:
$(WGET) "https://vcvrack.com/downloads/dep/speexdsp-SpeexDSP-1.2rc3.tgz"
$(SHA256) speexdsp-SpeexDSP-1.2rc3.tgz c8dded1454747f65956f981c95e7f89a06abdaa2a53e8aeaa66bab2a3d59cebd
$(UNTAR) speexdsp-SpeexDSP-1.2rc3.tgz
rm speexdsp-SpeexDSP-1.2rc3.tgz

$(libspeexdsp): speexdsp-SpeexDSP-1.2rc3
cd speexdsp-SpeexDSP-1.2rc3 && $(CONFIGURE)
$(MAKE) -C speexdsp-SpeexDSP-1.2rc3
$(MAKE) -C speexdsp-SpeexDSP-1.2rc3 install

libsamplerate-0.1.9:
$(WGET) "http://www.mega-nerd.com/SRC/libsamplerate-0.1.9.tar.gz"
$(SHA256) libsamplerate-0.1.9.tar.gz 0a7eb168e2f21353fb6d84da152e4512126f7dc48ccb0be80578c565413444c1
$(UNTAR) libsamplerate-0.1.9.tar.gz
rm libsamplerate-0.1.9.tar.gz

$(libsamplerate): libsamplerate-0.1.9
cd libsamplerate-0.1.9 && $(CONFIGURE) --disable-fftw --disable-sndfile
$(MAKE) -C libsamplerate-0.1.9
$(MAKE) -C libsamplerate-0.1.9 install

rtmidi-4.0.0:
$(WGET) "http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-4.0.0.tar.gz"
$(SHA256) rtmidi-4.0.0.tar.gz 370cfe710f43fbeba8d2b8c8bc310f314338c519c2cf2865e2d2737b251526cd


+ 8
- 8
helper.py View File

@@ -100,10 +100,10 @@ include $(RACK_DIR)/plugin.mk
using namespace rack;

// Declare the Plugin, defined in plugin.cpp
extern Plugin *pluginInstance;
extern Plugin* pluginInstance;

// Declare each Model, defined in each module source file
// extern Model *modelMyModule;
// extern Model* modelMyModule;
"""
with open(os.path.join(plugin_dir, "src/plugin.hpp"), "w") as f:
f.write(plugin_hpp)
@@ -112,10 +112,10 @@ extern Plugin *pluginInstance;
plugin_cpp = """#include "plugin.hpp"


Plugin *pluginInstance;
Plugin* pluginInstance;


void init(Plugin *p) {
void init(Plugin* p) {
pluginInstance = p;

// Add modules here
@@ -245,7 +245,7 @@ def create_module(slug, panel_filename=None, source_filename=None):
# Tell user to add model to plugin.hpp and plugin.cpp
print(f"""
To enable the module, add
extern Model *model{identifier};
extern Model* model{identifier};
to plugin.hpp, and add
p->addModel(model{identifier});
to the init() function in plugin.cpp.""")
@@ -396,7 +396,7 @@ struct {identifier} : Module {{"""
source += """
}

void process(const ProcessArgs &args) override {
void process(const ProcessArgs& args) override {
}
};"""

@@ -404,7 +404,7 @@ struct {identifier} : Module {{"""


struct {identifier}Widget : ModuleWidget {{
{identifier}Widget({identifier} *module) {{
{identifier}Widget({identifier}* module) {{
setModule(module);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/{slug}.svg")));

@@ -475,7 +475,7 @@ struct {identifier}Widget : ModuleWidget {{
}};


Model *model{identifier} = createModel<{identifier}, {identifier}Widget>("{slug}");"""
Model* model{identifier} = createModel<{identifier}, {identifier}Widget>("{slug}");"""

return source



+ 1
- 1
include/math.hpp View File

@@ -147,7 +147,7 @@ inline float crossfade(float a, float b, float p) {
}

/** Linearly interpolates an array `p` with index `x`.
Assumes that the array at `p` is of length at least `floor(x) + 1`.
The array at `p` must be at least length `floor(x) + 2`.
*/
inline float interpolateLinear(const float* p, float x) {
int xi = x;


+ 5
- 0
include/system.hpp View File

@@ -47,6 +47,11 @@ The launched process will continue running if the current process is closed.
*/
void runProcessDetached(const std::string& path);
std::string getOperatingSystemInfo();
/** Unzips a ZIP file to a folder.
The folder must exist.
Returns 0 if successful.
*/
int unzipToFolder(const std::string& zipPath, const std::string& dir);


} // namespace system


+ 34
- 34
src/app/ModuleBrowser.cpp View File

@@ -320,12 +320,12 @@ struct ClearButton : ui::Button {
struct BrowserSidebar : widget::Widget {
BrowserSearchField* searchField;
ClearButton* clearButton;
ui::Label* brandLabel;
ui::List* brandList;
ui::ScrollWidget* brandScroll;
ui::Label* tagLabel;
ui::List* tagList;
ui::ScrollWidget* tagScroll;
ui::Label* brandLabel;
ui::List* brandList;
ui::ScrollWidget* brandScroll;

BrowserSidebar() {
// Search
@@ -337,7 +337,28 @@ struct BrowserSidebar : widget::Widget {
clearButton->text = "Reset filters";
addChild(clearButton);

// Bbrand label
// Tag label
tagLabel = new ui::Label;
// tagLabel->fontSize = 16;
tagLabel->color = nvgRGB(0x80, 0x80, 0x80);
tagLabel->text = "Tags";
addChild(tagLabel);

// Tag list
tagScroll = new ui::ScrollWidget;
addChild(tagScroll);

tagList = new ui::List;
tagScroll->container->addChild(tagList);

for (int tagId = 0; tagId < (int) tag::tagAliases.size(); tagId++) {
TagItem* item = new TagItem;
item->text = tag::tagAliases[tagId][0];
item->tagId = tagId;
tagList->addChild(item);
}

// Brand label
brandLabel = new ui::Label;
// brandLabel->fontSize = 16;
brandLabel->color = nvgRGB(0x80, 0x80, 0x80);
@@ -362,27 +383,6 @@ struct BrowserSidebar : widget::Widget {
item->text = brand;
brandList->addChild(item);
}

// Tag label
tagLabel = new ui::Label;
// tagLabel->fontSize = 16;
tagLabel->color = nvgRGB(0x80, 0x80, 0x80);
tagLabel->text = "Tags";
addChild(tagLabel);

// Tag list
tagScroll = new ui::ScrollWidget;
addChild(tagScroll);

tagList = new ui::List;
tagScroll->container->addChild(tagList);

for (int tagId = 0; tagId < (int) tag::tagAliases.size(); tagId++) {
TagItem* item = new TagItem;
item->text = tag::tagAliases[tagId][0];
item->tagId = tagId;
tagList->addChild(item);
}
}

void step() override {
@@ -393,20 +393,20 @@ struct BrowserSidebar : widget::Widget {
float listHeight = (box.size.y - clearButton->box.getBottom()) / 2;
listHeight = std::floor(listHeight);

brandLabel->box.pos = clearButton->box.getBottomLeft();
brandLabel->box.size.x = box.size.x;
brandScroll->box.pos = brandLabel->box.getBottomLeft();
brandScroll->box.size.y = listHeight - brandLabel->box.size.y;
brandScroll->box.size.x = box.size.x;
brandList->box.size.x = brandScroll->box.size.x;

tagLabel->box.pos = brandScroll->box.getBottomLeft();
tagLabel->box.pos = clearButton->box.getBottomLeft();
tagLabel->box.size.x = box.size.x;
tagScroll->box.pos = tagLabel->box.getBottomLeft();
tagScroll->box.size.y = listHeight - tagLabel->box.size.y;
tagScroll->box.size.x = box.size.x;
tagList->box.size.x = tagScroll->box.size.x;

brandLabel->box.pos = tagScroll->box.getBottomLeft();
brandLabel->box.size.x = box.size.x;
brandScroll->box.pos = brandLabel->box.getBottomLeft();
brandScroll->box.size.y = listHeight - brandLabel->box.size.y;
brandScroll->box.size.x = box.size.x;
brandList->box.size.x = brandScroll->box.size.x;

Widget::step();
}
};
@@ -615,7 +615,7 @@ inline void TagItem::step() {
}

inline void BrowserSearchField::onSelectKey(const event::SelectKey& e) {
if (e.action == GLFW_PRESS) {
if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
switch (e.key) {
case GLFW_KEY_ESCAPE: {
BrowserOverlay* overlay = getAncestorOfType<BrowserOverlay>();


+ 1
- 0
src/app/ModuleWidget.cpp View File

@@ -246,6 +246,7 @@ ModuleWidget::ModuleWidget() {
}

ModuleWidget::~ModuleWidget() {
clearChildren();
setModule(NULL);
}



+ 11
- 4
src/core/MIDI_Map.cpp View File

@@ -103,13 +103,20 @@ struct MIDI_Map : Module {
if (!filterInitialized[id]) {
valueFilters[id].out = paramQuantity->getScaledValue();
filterInitialized[id] = true;
continue;
}
// Set param if value has been initialized
if (values[cc] >= 0) {
float v = values[cc] / 127.f;
v = valueFilters[id].process(args.sampleTime * divider.getDivision(), v);
paramQuantity->setScaledValue(v);
float value = values[cc] / 127.f;
// Detect behavior from MIDI buttons.
if (std::fabs(valueFilters[id].out - value) >= 1.f) {
// Jump value
valueFilters[id].out = value;
}
else {
// Smooth value with filter
valueFilters[id].process(args.sampleTime * divider.getDivision(), value);
}
paramQuantity->setScaledValue(valueFilters[id].out);
}
}
}


+ 2
- 72
src/plugin.cpp View File

@@ -17,8 +17,6 @@
#include <map>
#include <stdexcept>

#define ZIP_STATIC
#include <zip.h>
#include <jansson.h>

#if defined ARCH_WIN
@@ -174,74 +172,6 @@ static void loadPlugins(std::string path) {
}
}

/** Returns 0 if successful */
static int extractZipHandle(zip_t* za, std::string dir) {
int err;
for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
zip_stat_t zs;
err = zip_stat_index(za, i, 0, &zs);
if (err) {
WARN("zip_stat_index() failed: error %d", err);
return err;
}

std::string path = dir + "/" + zs.name;

if (path[path.size() - 1] == '/') {
system::createDirectory(path);
// HACK
// Create and delete file to update the directory's mtime.
std::string tmpPath = path + "/.tmp";
FILE* tmpFile = fopen(tmpPath.c_str(), "w");
fclose(tmpFile);
std::remove(tmpPath.c_str());
}
else {
zip_file_t* zf = zip_fopen_index(za, i, 0);
if (!zf) {
WARN("zip_fopen_index() failed");
return -1;
}

FILE* outFile = fopen(path.c_str(), "wb");
if (!outFile)
continue;

while (1) {
char buffer[1 << 15];
int len = zip_fread(zf, buffer, sizeof(buffer));
if (len <= 0)
break;
fwrite(buffer, 1, len, outFile);
}

err = zip_fclose(zf);
if (err) {
WARN("zip_fclose() failed: error %d", err);
return err;
}
fclose(outFile);
}
}
return 0;
}

/** Returns 0 if successful */
static int extractZip(std::string filename, std::string path) {
int err;
zip_t* za = zip_open(filename.c_str(), 0, &err);
if (!za) {
WARN("Could not open zip %s: error %d", filename.c_str(), err);
return err;
}
DEFER({
zip_close(za);
});

err = extractZipHandle(za, path);
return err;
}

static void extractPackages(std::string path) {
std::string message;

@@ -250,7 +180,7 @@ static void extractPackages(std::string path) {
continue;
INFO("Extracting package %s", packagePath.c_str());
// Extract package
if (extractZip(packagePath, path)) {
if (system::unzipToFolder(packagePath, path)) {
WARN("Package %s failed to extract", packagePath.c_str());
message += string::f("Could not extract package %s\n", packagePath.c_str());
continue;
@@ -289,7 +219,7 @@ void init() {
std::string fundamentalDir = asset::pluginsPath + "/Fundamental";
if (!settings::devMode && !getPlugin("Fundamental") && system::isFile(fundamentalSrc)) {
INFO("Extracting bundled Fundamental package");
extractZip(fundamentalSrc.c_str(), asset::pluginsPath.c_str());
system::unzipToFolder(fundamentalSrc.c_str(), asset::pluginsPath.c_str());
loadPlugin(fundamentalDir);
}



+ 71
- 0
src/system.cpp View File

@@ -27,6 +27,9 @@
#include <dbghelp.h>
#endif

#define ZIP_STATIC
#include <zip.h>


namespace rack {
namespace system {
@@ -324,5 +327,73 @@ std::string getOperatingSystemInfo() {
}


int unzipToFolder(const std::string& zipPath, const std::string& dir) {
int err;
// Open ZIP file
zip_t* za = zip_open(zipPath.c_str(), 0, &err);
if (!za) {
WARN("Could not open ZIP file %s: error %d", zipPath.c_str(), err);
return err;
}
DEFER({
zip_close(za);
});

// Iterate ZIP entries
for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
zip_stat_t zs;
err = zip_stat_index(za, i, 0, &zs);
if (err) {
WARN("zip_stat_index() failed: error %d", err);
return err;
}

std::string path = dir + "/" + zs.name;

if (path[path.size() - 1] == '/') {
// Create directory
system::createDirectory(path);
// HACK
// Create and delete file to update the directory's mtime.
std::string tmpPath = path + "/.tmp";
FILE* tmpFile = fopen(tmpPath.c_str(), "w");
fclose(tmpFile);
std::remove(tmpPath.c_str());
}
else {
// Open ZIP entry
zip_file_t* zf = zip_fopen_index(za, i, 0);
if (!zf) {
WARN("zip_fopen_index() failed");
return -1;
}
DEFER({
zip_fclose(zf);
});

// Create file
FILE* outFile = fopen(path.c_str(), "wb");
if (!outFile) {
WARN("Could not create file %s", path.c_str());
return -1;
}
DEFER({
fclose(outFile);
});

// Read buffer and copy to file
while (true) {
char buffer[1 << 15];
int len = zip_fread(zf, buffer, sizeof(buffer));
if (len <= 0)
break;
fwrite(buffer, 1, len, outFile);
}
}
}
return 0;
}


} // namespace system
} // namespace rack

+ 18
- 17
src/tag.cpp View File

@@ -8,30 +8,31 @@ namespace tag {


const std::vector<std::vector<std::string>> tagAliases = {
{"Arpeggiator"},
{"Attenuator"}, // With a level knob and not much else.
{"Blank"}, // No parameters or ports. Serves no purpose except visual.
{"Arpeggiator"}, // With a level knob and not much else.
{"Attenuator"}, // No parameters or ports. Serves no purpose except visual.
{"Blank"},
{"Chorus"},
{"Clock generator", "Clock"},
{"Clock modulator"}, // Clock dividers, multipliers, etc.
{"Compressor"}, // With threshold, ratio, knee, etc parameters.
{"Controller"}, // Use only if the artist "performs" with this module. Simply having knobs is not enough. Examples: on-screen keyboard, XY pad.
{"Clock generator", "Clock"}, // Clock dividers, multipliers, etc.
{"Clock modulator"}, // With threshold, ratio, knee, etc parameters.
{"Compressor"}, // Use only if the artist "performs" with this module. Simply having knobs is not enough. Examples: on-screen keyboard, XY pad.
{"Controller"},
{"Delay"},
{"Digital"},
{"Distortion"},
{"Drum", "Drums", "Percussion"},
{"Dual"}, // The core functionality times two. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Dual module.
{"Drum", "Drums", "Percussion"}, // The core functionality times two. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Dual module.
{"Dual"},
{"Dynamics"},
{"Effect"},
{"Envelope follower"},
{"Envelope generator"},
{"Equalizer", "EQ"},
{"Expander"}, // Expands the functionality of a "mother" module when placed next to it. Expanders should inherit the tags of its mother module.
{"Equalizer", "EQ"}, // Expands the functionality of a "mother" module when placed next to it. Expanders should inherit the tags of its mother module.
{"Expander"},
{"External"},
{"Filter", "VCF", "Voltage controlled filter"},
{"Flanger"},
{"Function generator"},
{"Granular"},
{"Hardware clone", "Hardware"}, // Clones the functionality *and* appearance of a real-world hardware module.
{"Limiter"},
{"Logic"},
{"Low-frequency oscillator", "LFO", "Low frequency oscillator"},
@@ -44,8 +45,8 @@ const std::vector<std::vector<std::string>> tagAliases = {
{"Panning", "Pan"},
{"Phaser"},
{"Physical modeling"},
{"Polyphonic", "Poly"},
{"Quad"}, // The core functionality times four. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Quad module.
{"Polyphonic", "Poly"}, // The core functionality times four. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Quad module.
{"Quad"},
{"Quantizer"},
{"Random"},
{"Recording"},
@@ -55,10 +56,10 @@ const std::vector<std::vector<std::string>> tagAliases = {
{"Sampler"},
{"Sequencer"},
{"Slew limiter"},
{"Switch"},
{"Synth voice"}, // A synth voice must have, at the minimum, a built-in oscillator and envelope.
{"Tuner"},
{"Utility"}, // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc.
{"Switch"}, // A synth voice must have, at the minimum, a built-in oscillator and envelope.
{"Synth voice"},
{"Tuner"}, // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc.
{"Utility"},
{"Visual"},
{"Vocoder"},
{"Voltage-controlled amplifier", "Amplifier", "VCA", "Voltage controlled amplifier"},


+ 10
- 3
src/updater.cpp View File

@@ -61,16 +61,23 @@ void update() {
if (downloadUrl == "")
return;

#if defined ARCH_WIN
// Download and launch the installer on Windows
#if defined ARCH_WIN || defined ARCH_MAC
// Download update
std::string filename = string::filename(network::urlPath(downloadUrl));
std::string path = asset::user(filename);
INFO("Download update %s to %s", downloadUrl.c_str(), path.c_str());
network::requestDownload(downloadUrl, path, &progress);
#endif

#if defined ARCH_WIN
// Launch the installer
INFO("Launching update %s", path.c_str());
system::runProcessDetached(path);
#elif defined ARCH_MAC
// Unzip app using Apple's unzipper, since Rack's unzipper doesn't handle the metadata stuff correctly.
std::string cmd = "open \"" + path + "\"";
std::system(cmd.c_str());
#else
// Open the browser on Mac and Linux. The user will know what to do.
system::openBrowser(downloadUrl);
#endif



Loading…
Cancel
Save