@@ -14,7 +14,7 @@ ifeq ($(ARCH), lin) | |||
LDFLAGS += -rdynamic \ | |||
-lpthread -lGL -ldl \ | |||
$(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 | |||
endif | |||
@@ -23,7 +23,7 @@ ifeq ($(ARCH), mac) | |||
CXXFLAGS += -DAPPLE -stdlib=libc++ | |||
LDFLAGS += -stdlib=libc++ -lpthread -ldl \ | |||
-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 | |||
endif | |||
@@ -32,7 +32,7 @@ ifeq ($(ARCH), win) | |||
LDFLAGS += -static-libgcc -static-libstdc++ -lpthread \ | |||
-Wl,--export-all-symbols,--out-implib,libRack.a -mwindows \ | |||
-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 | |||
TARGET = Rack.exe | |||
OBJECTS = Rack.res | |||
@@ -98,7 +98,7 @@ ifeq ($(ARCH), mac) | |||
cp dep/lib/libcurl.4.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/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 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/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 @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 | |||
@@ -126,7 +126,7 @@ ifeq ($(ARCH), win) | |||
cp dep/bin/glfw3.dll dist/Rack/ | |||
cp dep/bin/libcurl-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/libzip-5.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/libzip.so.5 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 | |||
cp -R plugins/Fundamental/dist/Fundamental dist/Rack/plugins/ | |||
endif | |||
@@ -1,4 +1,3 @@ | |||
LOCAL = $(shell pwd) | |||
# Arch-specifics | |||
@@ -28,7 +27,7 @@ ifeq ($(ARCH),lin) | |||
libsamplerate = lib/libsamplerate.so | |||
libcurl = lib/libcurl.so | |||
libzip = lib/libzip.so | |||
portmidi = lib/libportmidi.so | |||
rtmidi = lib/librtmidi.so | |||
portaudio = lib/libportaudio.so | |||
endif | |||
@@ -39,7 +38,7 @@ ifeq ($(ARCH),mac) | |||
libsamplerate = lib/libsamplerate.dylib | |||
libcurl = lib/libcurl.dylib | |||
libzip = lib/libzip.dylib | |||
portmidi = lib/libportmidi.dylib | |||
rtmidi = lib/librtmidi.dylib | |||
portaudio = lib/libportaudio.dylib | |||
endif | |||
@@ -50,14 +49,14 @@ ifeq ($(ARCH),win) | |||
libsamplerate = bin/libsamplerate-0.dll | |||
libcurl = bin/libcurl-4.dll | |||
libzip = bin/libzip-5.dll | |||
portmidi = bin/libportmidi.dll | |||
rtmidi = bin/rtmidi.dll | |||
portaudio = bin/libportaudio_x64.dll | |||
endif | |||
.NOTPARALLEL: | |||
all: $(glew) $(glfw) $(jansson) $(libsamplerate) $(libcurl) $(libzip) $(portmidi) $(portaudio) | |||
all: $(glew) $(glfw) $(jansson) $(libsamplerate) $(libcurl) $(libzip) $(rtmidi) $(portaudio) | |||
@echo "" | |||
@echo "#######################################" | |||
@echo "# Built all dependencies successfully #" | |||
@@ -114,11 +113,11 @@ $(libzip): | |||
$(MAKE) -C libzip-1.2.0 | |||
$(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): | |||
ifeq ($(ARCH),win) | |||
@@ -1,28 +1,14 @@ | |||
#include <assert.h> | |||
#include <list> | |||
#include <algorithm> | |||
#include <portmidi.h> | |||
#include "rtmidi/RtMidi.h" | |||
#include "core.hpp" | |||
#include "gui.hpp" | |||
#include "../../include/engine.hpp" | |||
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 { | |||
enum ParamIds { | |||
NUM_PARAMS | |||
@@ -39,7 +25,7 @@ struct MidiInterface : Module { | |||
}; | |||
int portId = -1; | |||
PortMidiStream *stream = NULL; | |||
RtMidiIn *midiIn = NULL; | |||
std::list<int> notes; | |||
/** Filter MIDI channel | |||
-1 means all MIDI channels | |||
@@ -53,7 +39,12 @@ struct MidiInterface : Module { | |||
bool retriggered = false; | |||
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() { | |||
setPortId(-1); | |||
@@ -70,7 +61,7 @@ struct MidiInterface : Module { | |||
} | |||
void pressNote(int note); | |||
void releaseNote(int note); | |||
void processMidi(long msg); | |||
void processMidi(std::vector<unsigned char> msg); | |||
json_t *toJson() { | |||
json_t *rootJ = json_object(); | |||
@@ -107,11 +98,14 @@ struct MidiInterface : Module { | |||
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() { | |||
return Pm_CountDevices(); | |||
return midiIn->getPortCount(); | |||
} | |||
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) { | |||
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; | |||
// Open new port | |||
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; | |||
} | |||
@@ -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 | |||
if (this->channel >= 0 && this->channel != channel) | |||
return; | |||
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); | |||
} 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; | |||
case 0xe: // pitch wheel | |||
this->pitchWheel = data2; | |||
case 0x40: // sustain | |||
pedal = (data2 >= 64); | |||
releaseNote(-1); | |||
break; | |||
} | |||
break; | |||
case 0xe: // pitch wheel | |||
this->pitchWheel = data2; | |||
break; | |||
} | |||
} | |||
@@ -310,7 +299,7 @@ struct ChannelChoice : ChoiceButton { | |||
MidiInterfaceWidget::MidiInterfaceWidget() { | |||
MidiInterface *module = new MidiInterface(); | |||
setModule(module); | |||
box.size = Vec(15*6, 380); | |||
box.size = Vec(15 * 6, 380); | |||
{ | |||
Panel *panel = new LightPanel(); | |||