@@ -14,7 +14,7 @@ ifeq ($(ARCH), lin) | |||||
LDFLAGS += -rdynamic \ | LDFLAGS += -rdynamic \ | ||||
-lpthread -lGL -ldl \ | -lpthread -lGL -ldl \ | ||||
$(shell pkg-config --libs gtk+-2.0) \ | $(shell pkg-config --libs gtk+-2.0) \ | ||||
-Ldep/lib -lGLEW -lglfw -ljansson -lsamplerate -lcurl -lzip -lportaudio -lportmidi | |||||
-Ldep/lib -lGLEW -lglfw -ljansson -lsamplerate -lcurl -lzip -lportaudio -lrtmidi | |||||
TARGET = Rack | TARGET = Rack | ||||
endif | endif | ||||
@@ -23,7 +23,7 @@ ifeq ($(ARCH), mac) | |||||
CXXFLAGS += -DAPPLE -stdlib=libc++ | CXXFLAGS += -DAPPLE -stdlib=libc++ | ||||
LDFLAGS += -stdlib=libc++ -lpthread -ldl \ | LDFLAGS += -stdlib=libc++ -lpthread -ldl \ | ||||
-framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo \ | -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo \ | ||||
-Ldep/lib -lGLEW -lglfw -ljansson -lsamplerate -lcurl -lzip -lportaudio -lportmidi | |||||
-Ldep/lib -lGLEW -lglfw -ljansson -lsamplerate -lcurl -lzip -lportaudio -lrtmidi | |||||
TARGET = Rack | TARGET = Rack | ||||
endif | endif | ||||
@@ -32,7 +32,7 @@ ifeq ($(ARCH), win) | |||||
LDFLAGS += -static-libgcc -static-libstdc++ -lpthread \ | LDFLAGS += -static-libgcc -static-libstdc++ -lpthread \ | ||||
-Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \ | -Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \ | ||||
-lgdi32 -lopengl32 -lcomdlg32 -lole32 \ | -lgdi32 -lopengl32 -lcomdlg32 -lole32 \ | ||||
-Ldep/lib -lglew32 -lglfw3dll -lcurl -lzip -lportaudio_x64 -lportmidi \ | |||||
-Ldep/lib -lglew32 -lglfw3dll -lcurl -lzip -lportaudio_x64 -lrtmidi \ | |||||
-Wl,-Bstatic -ljansson -lsamplerate | -Wl,-Bstatic -ljansson -lsamplerate | ||||
TARGET = Rack.exe | TARGET = Rack.exe | ||||
OBJECTS = Rack.res | OBJECTS = Rack.res | ||||
@@ -98,7 +98,7 @@ ifeq ($(ARCH), mac) | |||||
cp dep/lib/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/ | cp dep/lib/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/ | ||||
cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ | cp dep/lib/libzip.5.dylib $(BUNDLE)/Contents/MacOS/ | ||||
cp dep/lib/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/ | cp dep/lib/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/ | ||||
cp dep/lib/libportmidi.dylib $(BUNDLE)/Contents/MacOS/ | |||||
cp dep/lib/librtmidi.dylib $(BUNDLE)/Contents/MacOS/ | |||||
install_name_tool -change /usr/local/lib/libGLEW.2.1.0.dylib @executable_path/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/Rack | install_name_tool -change /usr/local/lib/libGLEW.2.1.0.dylib @executable_path/libGLEW.2.1.0.dylib $(BUNDLE)/Contents/MacOS/Rack | ||||
install_name_tool -change lib/libglfw.3.dylib @executable_path/libglfw.3.dylib $(BUNDLE)/Contents/MacOS/Rack | install_name_tool -change lib/libglfw.3.dylib @executable_path/libglfw.3.dylib $(BUNDLE)/Contents/MacOS/Rack | ||||
@@ -107,7 +107,7 @@ ifeq ($(ARCH), mac) | |||||
install_name_tool -change $(PWD)/dep/lib/libcurl.4.dylib @executable_path/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/Rack | install_name_tool -change $(PWD)/dep/lib/libcurl.4.dylib @executable_path/libcurl.4.dylib $(BUNDLE)/Contents/MacOS/Rack | ||||
install_name_tool -change $(PWD)/dep/lib/libzip.5.dylib @executable_path/libzip.5.dylib $(BUNDLE)/Contents/MacOS/Rack | install_name_tool -change $(PWD)/dep/lib/libzip.5.dylib @executable_path/libzip.5.dylib $(BUNDLE)/Contents/MacOS/Rack | ||||
install_name_tool -change $(PWD)/dep/lib/libportaudio.2.dylib @executable_path/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/Rack | install_name_tool -change $(PWD)/dep/lib/libportaudio.2.dylib @executable_path/libportaudio.2.dylib $(BUNDLE)/Contents/MacOS/Rack | ||||
install_name_tool -change @rpath/libportmidi.dylib @executable_path/libportmidi.dylib $(BUNDLE)/Contents/MacOS/Rack | |||||
install_name_tool -change @rpath/librtmidi.dylib @executable_path/librtmidi.dylib $(BUNDLE)/Contents/MacOS/Rack | |||||
otool -L $(BUNDLE)/Contents/MacOS/Rack | otool -L $(BUNDLE)/Contents/MacOS/Rack | ||||
@@ -126,7 +126,7 @@ ifeq ($(ARCH), win) | |||||
cp dep/bin/glfw3.dll dist/Rack/ | cp dep/bin/glfw3.dll dist/Rack/ | ||||
cp dep/bin/libcurl-4.dll dist/Rack/ | cp dep/bin/libcurl-4.dll dist/Rack/ | ||||
cp dep/bin/libjansson-4.dll dist/Rack/ | cp dep/bin/libjansson-4.dll dist/Rack/ | ||||
cp dep/bin/libportmidi.dll dist/Rack/ | |||||
cp dep/bin/librtmidi.dll dist/Rack/ | |||||
cp dep/bin/libsamplerate-0.dll dist/Rack/ | cp dep/bin/libsamplerate-0.dll dist/Rack/ | ||||
cp dep/bin/libzip-5.dll dist/Rack/ | cp dep/bin/libzip-5.dll dist/Rack/ | ||||
cp dep/bin/portaudio_x64.dll dist/Rack/ | cp dep/bin/portaudio_x64.dll dist/Rack/ | ||||
@@ -144,7 +144,7 @@ ifeq ($(ARCH), lin) | |||||
cp dep/lib/libcurl.so.4 dist/Rack/ | cp dep/lib/libcurl.so.4 dist/Rack/ | ||||
cp dep/lib/libzip.so.5 dist/Rack/ | cp dep/lib/libzip.so.5 dist/Rack/ | ||||
cp dep/lib/libportaudio.so.2 dist/Rack/ | cp dep/lib/libportaudio.so.2 dist/Rack/ | ||||
cp dep/lib/libportmidi.so dist/Rack/ | |||||
cp dep/lib/librtmidi.so dist/Rack/ | |||||
mkdir -p dist/Rack/plugins | mkdir -p dist/Rack/plugins | ||||
cp -R plugins/Fundamental/dist/Fundamental dist/Rack/plugins/ | cp -R plugins/Fundamental/dist/Fundamental dist/Rack/plugins/ | ||||
endif | endif | ||||
@@ -1,4 +1,3 @@ | |||||
LOCAL = $(shell pwd) | LOCAL = $(shell pwd) | ||||
# Arch-specifics | # Arch-specifics | ||||
@@ -28,7 +27,7 @@ ifeq ($(ARCH),lin) | |||||
libsamplerate = lib/libsamplerate.so | libsamplerate = lib/libsamplerate.so | ||||
libcurl = lib/libcurl.so | libcurl = lib/libcurl.so | ||||
libzip = lib/libzip.so | libzip = lib/libzip.so | ||||
portmidi = lib/libportmidi.so | |||||
rtmidi = lib/librtmidi.so | |||||
portaudio = lib/libportaudio.so | portaudio = lib/libportaudio.so | ||||
endif | endif | ||||
@@ -39,7 +38,7 @@ ifeq ($(ARCH),mac) | |||||
libsamplerate = lib/libsamplerate.dylib | libsamplerate = lib/libsamplerate.dylib | ||||
libcurl = lib/libcurl.dylib | libcurl = lib/libcurl.dylib | ||||
libzip = lib/libzip.dylib | libzip = lib/libzip.dylib | ||||
portmidi = lib/libportmidi.dylib | |||||
rtmidi = lib/librtmidi.dylib | |||||
portaudio = lib/libportaudio.dylib | portaudio = lib/libportaudio.dylib | ||||
endif | endif | ||||
@@ -50,14 +49,14 @@ ifeq ($(ARCH),win) | |||||
libsamplerate = bin/libsamplerate-0.dll | libsamplerate = bin/libsamplerate-0.dll | ||||
libcurl = bin/libcurl-4.dll | libcurl = bin/libcurl-4.dll | ||||
libzip = bin/libzip-5.dll | libzip = bin/libzip-5.dll | ||||
portmidi = bin/libportmidi.dll | |||||
rtmidi = bin/rtmidi.dll | |||||
portaudio = bin/libportaudio_x64.dll | portaudio = bin/libportaudio_x64.dll | ||||
endif | endif | ||||
.NOTPARALLEL: | .NOTPARALLEL: | ||||
all: $(glew) $(glfw) $(jansson) $(libsamplerate) $(libcurl) $(libzip) $(portmidi) $(portaudio) | |||||
all: $(glew) $(glfw) $(jansson) $(libsamplerate) $(libcurl) $(libzip) $(rtmidi) $(portaudio) | |||||
@echo "" | @echo "" | ||||
@echo "#######################################" | @echo "#######################################" | ||||
@echo "# Built all dependencies successfully #" | @echo "# Built all dependencies successfully #" | ||||
@@ -114,11 +113,11 @@ $(libzip): | |||||
$(MAKE) -C libzip-1.2.0 | $(MAKE) -C libzip-1.2.0 | ||||
$(MAKE) -C libzip-1.2.0 install | $(MAKE) -C libzip-1.2.0 install | ||||
$(portmidi): | |||||
git clone https://github.com/AndrewBelt/portmidi.git portmidi | |||||
cd portmidi && $(CMAKE) . -DCMAKE_INSTALL_PREFIX="$(LOCAL)" -DCMAKE_BUILD_TYPE=Release | |||||
$(MAKE) -C portmidi | |||||
$(MAKE) -C portmidi install | |||||
$(rtmidi): | |||||
git clone https://github.com/thestk/rtmidi.git rtmidi | |||||
cd rtmidi && ./autogen.sh && ./configure --prefix="$(LOCAL)" | |||||
$(MAKE) -C rtmidi | |||||
$(MAKE) -C rtmidi install | |||||
$(portaudio): | $(portaudio): | ||||
ifeq ($(ARCH),win) | ifeq ($(ARCH),win) | ||||
@@ -1,28 +1,14 @@ | |||||
#include <assert.h> | #include <assert.h> | ||||
#include <list> | #include <list> | ||||
#include <algorithm> | #include <algorithm> | ||||
#include <portmidi.h> | |||||
#include "rtmidi/RtMidi.h" | |||||
#include "core.hpp" | #include "core.hpp" | ||||
#include "gui.hpp" | #include "gui.hpp" | ||||
#include "../../include/engine.hpp" | |||||
using namespace rack; | using namespace rack; | ||||
static bool initialized = false; | |||||
void midiInit() { | |||||
if (initialized) | |||||
return; | |||||
PmError err = Pm_Initialize(); | |||||
if (err) { | |||||
printf("Failed to initialize PortMidi: %s\n", Pm_GetErrorText(err)); | |||||
return; | |||||
} | |||||
initialized = true; | |||||
} | |||||
struct MidiInterface : Module { | struct MidiInterface : Module { | ||||
enum ParamIds { | enum ParamIds { | ||||
NUM_PARAMS | NUM_PARAMS | ||||
@@ -39,7 +25,7 @@ struct MidiInterface : Module { | |||||
}; | }; | ||||
int portId = -1; | int portId = -1; | ||||
PortMidiStream *stream = NULL; | |||||
RtMidiIn *midiIn = NULL; | |||||
std::list<int> notes; | std::list<int> notes; | ||||
/** Filter MIDI channel | /** Filter MIDI channel | ||||
-1 means all MIDI channels | -1 means all MIDI channels | ||||
@@ -53,7 +39,12 @@ struct MidiInterface : Module { | |||||
bool retriggered = false; | bool retriggered = false; | ||||
MidiInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | MidiInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | ||||
midiInit(); | |||||
try { | |||||
midiIn = new RtMidiIn(RtMidi::UNSPECIFIED, "VCVRack"); | |||||
} | |||||
catch ( RtMidiError &error ) { | |||||
fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str()); | |||||
} | |||||
} | } | ||||
~MidiInterface() { | ~MidiInterface() { | ||||
setPortId(-1); | setPortId(-1); | ||||
@@ -70,7 +61,7 @@ struct MidiInterface : Module { | |||||
} | } | ||||
void pressNote(int note); | void pressNote(int note); | ||||
void releaseNote(int note); | void releaseNote(int note); | ||||
void processMidi(long msg); | |||||
void processMidi(std::vector<unsigned char> msg); | |||||
json_t *toJson() { | json_t *toJson() { | ||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
@@ -107,11 +98,14 @@ struct MidiInterface : Module { | |||||
void MidiInterface::step() { | void MidiInterface::step() { | ||||
if (stream) { | |||||
// Read MIDI events | |||||
PmEvent event; | |||||
while (Pm_Read(stream, &event, 1) > 0) { | |||||
processMidi(event.message); | |||||
if (midiIn->isPortOpen()) { | |||||
std::vector<unsigned char> message; | |||||
// midiIn->getMessage returns empty vector if there are no messages in the queue | |||||
double stamp = midiIn->getMessage( &message ); | |||||
while (message.size() > 0) { | |||||
processMidi(message); | |||||
stamp = midiIn->getMessage( &message ); | |||||
} | } | ||||
} | } | ||||
@@ -128,36 +122,30 @@ void MidiInterface::step() { | |||||
} | } | ||||
int MidiInterface::getPortCount() { | int MidiInterface::getPortCount() { | ||||
return Pm_CountDevices(); | |||||
return midiIn->getPortCount(); | |||||
} | } | ||||
std::string MidiInterface::getPortName(int portId) { | std::string MidiInterface::getPortName(int portId) { | ||||
const PmDeviceInfo *info = Pm_GetDeviceInfo(portId); | |||||
if (!info) | |||||
return ""; | |||||
return stringf("%s: %s (%s)", info->interf, info->name, info->input ? "input" : "output"); | |||||
std::string portName; | |||||
try { | |||||
portName = midiIn->getPortName(portId); | |||||
} | |||||
catch ( RtMidiError &error ) { | |||||
fprintf(stderr, "Failed to get Port Name: %d, %s\n", portId, error.getMessage().c_str()); | |||||
} | |||||
return portName; | |||||
} | } | ||||
void MidiInterface::setPortId(int portId) { | void MidiInterface::setPortId(int portId) { | ||||
PmError err; | |||||
// Close existing port | |||||
if (stream) { | |||||
err = Pm_Close(stream); | |||||
if (err) { | |||||
printf("Failed to close MIDI port: %s\n", Pm_GetErrorText(err)); | |||||
} | |||||
stream = NULL; | |||||
// Close port if it was previously opened | |||||
if (midiIn->isPortOpen()) { | |||||
midiIn->closePort(); | |||||
} | } | ||||
this->portId = -1; | this->portId = -1; | ||||
// Open new port | // Open new port | ||||
if (portId >= 0) { | if (portId >= 0) { | ||||
err = Pm_OpenInput(&stream, portId, NULL, 128, NULL, NULL); | |||||
if (err) { | |||||
printf("Failed to open MIDI port: %s\n", Pm_GetErrorText(err)); | |||||
return; | |||||
} | |||||
midiIn->openPort(portId, "Midi Interface"); | |||||
} | } | ||||
this->portId = portId; | this->portId = portId; | ||||
} | } | ||||
@@ -191,45 +179,46 @@ void MidiInterface::releaseNote(int note) { | |||||
} | } | ||||
} | } | ||||
void MidiInterface::processMidi(long msg) { | |||||
int channel = msg & 0xf; | |||||
int status = (msg >> 4) & 0xf; | |||||
int data1 = (msg >> 8) & 0xff; | |||||
int data2 = (msg >> 16) & 0xff; | |||||
// printf("channel %d status %d data1 %d data2 %d\n", channel, status, data1, data2); | |||||
void MidiInterface::processMidi(std::vector<unsigned char> msg) { | |||||
int channel = msg[0] & 0xf; | |||||
int status = (msg[0] >> 4) & 0xf; | |||||
int data1 = msg[1]; | |||||
int data2 = msg[2]; | |||||
//fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1,data2); | |||||
// Filter channels | // Filter channels | ||||
if (this->channel >= 0 && this->channel != channel) | if (this->channel >= 0 && this->channel != channel) | ||||
return; | return; | ||||
switch (status) { | switch (status) { | ||||
// note off | |||||
case 0x8: { | |||||
// note off | |||||
case 0x8: { | |||||
releaseNote(data1); | |||||
} break; | |||||
case 0x9: // note on | |||||
if (data2 > 0) { | |||||
pressNote(data1); | |||||
} | |||||
else { | |||||
// For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. | |||||
releaseNote(data1); | releaseNote(data1); | ||||
} break; | |||||
case 0x9: // note on | |||||
if (data2 > 0) { | |||||
pressNote(data1); | |||||
} | |||||
else { | |||||
// For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. | |||||
releaseNote(data1); | |||||
} | |||||
break; | |||||
case 0xb: // cc | |||||
switch (data1) { | |||||
case 0x01: // mod | |||||
this->mod = data2; | |||||
break; | |||||
case 0x40: // sustain | |||||
pedal = (data2 >= 64); | |||||
releaseNote(-1); | |||||
break; | |||||
} | |||||
} | |||||
break; | |||||
case 0xb: // cc | |||||
switch (data1) { | |||||
case 0x01: // mod | |||||
this->mod = data2; | |||||
break; | break; | ||||
case 0xe: // pitch wheel | |||||
this->pitchWheel = data2; | |||||
case 0x40: // sustain | |||||
pedal = (data2 >= 64); | |||||
releaseNote(-1); | |||||
break; | break; | ||||
} | |||||
break; | |||||
case 0xe: // pitch wheel | |||||
this->pitchWheel = data2; | |||||
break; | |||||
} | } | ||||
} | } | ||||
@@ -310,7 +299,7 @@ struct ChannelChoice : ChoiceButton { | |||||
MidiInterfaceWidget::MidiInterfaceWidget() { | MidiInterfaceWidget::MidiInterfaceWidget() { | ||||
MidiInterface *module = new MidiInterface(); | MidiInterface *module = new MidiInterface(); | ||||
setModule(module); | setModule(module); | ||||
box.size = Vec(15*6, 380); | |||||
box.size = Vec(15 * 6, 380); | |||||
{ | { | ||||
Panel *panel = new LightPanel(); | Panel *panel = new LightPanel(); | ||||