aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2016-04-07 23:39:02 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2016-04-07 23:39:02 +0800
commit8f8782f43bd838e825f3792b611d27fffa90399b (patch)
tree1fbd96dd7ce061010edacf67f72f0ffb07889edf
parent70fd2d85c1bd6fb8732e680e8fda9d36c317c732 (diff)
downloadQMidiPlayer-8f8782f43bd838e825f3792b611d27fffa90399b.tar.xz
Lunatic Princess.
-rw-r--r--ChangeLog7
-rw-r--r--README.md2
-rw-r--r--core/qmpmidimapperrtmidi.cpp88
-rw-r--r--core/qmpmidimappers.hpp23
-rw-r--r--core/qmpmidiplay.cpp54
-rw-r--r--core/qmpmidiplay.hpp6
-rw-r--r--qmidiplayer-desktop/qmidiplayer-desktop.pro7
-rw-r--r--qmidiplayer-desktop/qmpchannelswindow.cpp16
-rw-r--r--qmidiplayer-desktop/qmpchannelswindow.hpp18
-rw-r--r--qmidiplayer-desktop/qmpimidimapper.hpp16
-rw-r--r--qmidiplayer-desktop/qmpmainwindow.cpp4
-rw-r--r--qmidiplayer-lite/qmidiplayer-lite.pro8
12 files changed, 214 insertions, 35 deletions
diff --git a/ChangeLog b/ChangeLog
index 08e54ae..c92da4f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2016-04-07 0.7.1 beta
+MIDI mapper: first steps.
+
+2016-04-06 0.7.0 beta
+Further works on project integration.
+Perhaps we need a version bump?
+
2016-04-06 0.7.0 beta
Merge qmidiplayerlite. Hope this won't break the repo.
diff --git a/README.md b/README.md
index 1d8319c..9e7f226 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Features:
* Editing synthesizer effects
* Rendering midi to wave file
* Visualization using SMELT (not implemented)
-* MIDI mapping (not implemented)
+* MIDI mapping (experimental, based on RtMidi)
Tested on Debian sid and Windows Vista~10.
diff --git a/core/qmpmidimapperrtmidi.cpp b/core/qmpmidimapperrtmidi.cpp
new file mode 100644
index 0000000..adba267
--- /dev/null
+++ b/core/qmpmidimapperrtmidi.cpp
@@ -0,0 +1,88 @@
+#include <cstdio>
+#include <cstring>
+#include <vector>
+#include "RtMidi.h"
+#include "qmpmidimappers.hpp"
+RtMidiOut* qmpMidiMapperRtMidi::dummy=NULL;
+qmpMidiMapperRtMidi::qmpMidiMapperRtMidi()
+{
+ dummy=new RtMidiOut();
+ memset(ports,0,sizeof(ports));
+}
+qmpMidiMapperRtMidi::~qmpMidiMapperRtMidi()
+{
+ delete dummy;for(int i=0;i<16;++i)if(ports[i])delete ports[i];
+}
+int qmpMidiMapperRtMidi::enumDevices()
+{
+ return dummy->getPortCount();
+}
+std::string qmpMidiMapperRtMidi::deviceName(int id)
+{
+ fprintf(stderr,"port #%d: %s\n",id,dummy->getPortName(id).c_str());
+ return dummy->getPortName(id);
+}
+int qmpMidiMapperRtMidi::deviceInit(int id)
+{
+ int i=0;for(;ports[i]&&i<16;++i);
+ if(i==16)return -1;
+ try
+ {
+ ports[i]=new RtMidiOut();
+ ports[i]->openPort(id);
+ }
+ catch(RtMidiError e)
+ {
+ printf("Device initialization failure: %s\n",e.what());
+ ports[i]=NULL;
+ return -1;
+ }
+ return i;
+}
+void qmpMidiMapperRtMidi::deviceDeinit(int iid)
+{
+ if(ports[iid]){ports[iid]->closePort();delete ports[iid];ports[iid]=NULL;}
+}
+void qmpMidiMapperRtMidi::noteOn(int iid,int ch,int key,int vel)
+{
+ if(!ports[iid])return;ch&=0x0F;
+ std::vector<unsigned char>message;
+ message.push_back(0x90|ch);
+ message.push_back(key);
+ message.push_back(vel);
+ ports[iid]->sendMessage(&message);
+}
+void qmpMidiMapperRtMidi::noteOff(int iid,int ch,int key)
+{
+ if(!ports[iid])return;ch&=0x0F;
+ std::vector<unsigned char>message;
+ message.push_back(0x80|ch);message.push_back(key);message.push_back(0);
+ ports[iid]->sendMessage(&message);
+}
+void qmpMidiMapperRtMidi::ctrlChange(int iid,int ch,int cc,int val)
+{
+ if(!ports[iid])return;ch&=0x0F;
+ std::vector<unsigned char>message;
+ message.push_back(0xB0|ch);message.push_back(cc);message.push_back(val);
+ ports[iid]->sendMessage(&message);
+}
+void qmpMidiMapperRtMidi::progChange(int iid,int ch,int val)
+{
+ if(!ports[iid])return;ch&=0x0F;
+ std::vector<unsigned char>message;
+ message.push_back(0xC0|ch);message.push_back(val);
+ ports[iid]->sendMessage(&message);
+}
+void qmpMidiMapperRtMidi::pitchBend(int iid,int ch,int val)
+{
+ if(!ports[iid])return;ch&=0x0F;
+ std::vector<unsigned char>message;
+ message.push_back(0xE0|ch);message.push_back(val&0xFF);
+ message.push_back(val>>8);ports[iid]->sendMessage(&message);
+}
+void qmpMidiMapperRtMidi::sysEx(int iid,int length,const char *data)
+{
+ if(!ports[iid])return;
+ std::vector<unsigned char>message(data,data+length);
+ ports[iid]->sendMessage(&message);
+}
diff --git a/core/qmpmidimappers.hpp b/core/qmpmidimappers.hpp
new file mode 100644
index 0000000..156caaa
--- /dev/null
+++ b/core/qmpmidimappers.hpp
@@ -0,0 +1,23 @@
+#ifndef QMPMIDIMAPPERS_H
+#define QMPMIDIMAPPERS_H
+#include "RtMidi.h"
+class qmpMidiMapperRtMidi
+{
+private:
+ RtMidiOut *ports[16];
+ static RtMidiOut *dummy;
+public:
+ qmpMidiMapperRtMidi();
+ ~qmpMidiMapperRtMidi();
+ int deviceInit(int id);
+ void deviceDeinit(int iid);
+ void noteOn(int iid,int ch,int key,int vel);
+ void noteOff(int iid,int ch,int key);
+ void ctrlChange(int iid,int ch,int cc,int val);
+ void progChange(int iid,int ch,int val);
+ void pitchBend(int iid,int ch,int val);
+ void sysEx(int iid,int length,const char* data);
+ int enumDevices();
+ std::string deviceName(int id);
+};
+#endif // QMPMIDIMAPPERS_H
diff --git a/core/qmpmidiplay.cpp b/core/qmpmidiplay.cpp
index 6f69e33..e65a2a1 100644
--- a/core/qmpmidiplay.cpp
+++ b/core/qmpmidiplay.cpp
@@ -1,4 +1,5 @@
#include <cstdio>
+#include <cstring>
#include <chrono>
#include <thread>
#include <fluidsynth.h>
@@ -46,12 +47,18 @@ void CMidiPlayer::processEvent(const SEvent *e)
switch(e->type&0xF0)
{
case 0x80://Note off
- fluid_synth_noteoff(synth,e->type&0x0F,e->p1);
+ if(mappedoutput[e->type&0x0F])
+ mapper->noteOff(mappedoutput[e->type&0x0F]-1,e->type*0x0F,e->p1);
+ else
+ fluid_synth_noteoff(synth,e->type&0x0F,e->p1);
break;
case 0x90://Note on
if((mute>>(e->type&0x0F))&1)break;//muted
if(solo&&!((solo>>(e->type&0x0F))&1))break;
- fluid_synth_noteon(synth,e->type&0x0F,e->p1,e->p2);
+ if(mappedoutput[e->type&0x0F])
+ mapper->noteOn(mappedoutput[e->type&0x0F]-1,e->type*0x0F,e->p1,e->p2);
+ else
+ fluid_synth_noteon(synth,e->type&0x0F,e->p1,e->p2);
break;
case 0xB0://CC
if(e->p1==100)rpnid=e->p2;
@@ -61,13 +68,22 @@ void CMidiPlayer::processEvent(const SEvent *e)
if(rpnid==0)fluid_synth_pitch_wheel_sens(synth,e->type&0x0F,rpnval);
rpnid=rpnval=-1;
}
- fluid_synth_cc(synth,e->type&0x0F,e->p1,e->p2);
+ if(mappedoutput[e->type&0x0F])
+ mapper->ctrlChange(mappedoutput[e->type&0x0F]-1,e->type*0x0F,e->p1,e->p2);
+ else
+ fluid_synth_cc(synth,e->type&0x0F,e->p1,e->p2);
break;
case 0xC0://PC
- fluid_synth_program_change(synth,e->type&0x0F,e->p1);
+ if(mappedoutput[e->type&0x0F])
+ mapper->progChange(mappedoutput[e->type&0x0F]-1,e->type*0x0F,e->p1);
+ else
+ fluid_synth_program_change(synth,e->type&0x0F,e->p1);
break;
case 0xE0://PW
- fluid_synth_pitch_bend(synth,e->type&0x0F,e->p1);
+ if(mappedoutput[e->type&0x0F])
+ mapper->pitchBend(mappedoutput[e->type&0x0F]-1,e->type*0x0F,e->p1);
+ else
+ fluid_synth_pitch_bend(synth,e->type&0x0F,e->p1);
break;
case 0xF0://Meta/SysEx
if((e->type&0x0F)==0x0F)
@@ -94,7 +110,11 @@ void CMidiPlayer::processEvent(const SEvent *e)
if((e->type&0x0F)==0x00||(e->type&0x0F)==07)
{
int io=0;
- if(sendSysEx)fluid_synth_sysex(synth,e->str,e->p1,NULL,&io,NULL,0);
+ if(sendSysEx)
+ {
+ for(int i=0;i<16;++i)if(deviceusage[i])mapper->sysEx(i,e->p1,e->str);
+ fluid_synth_sysex(synth,e->str,e->p1,NULL,&io,NULL,0);
+ }
}
break;
}
@@ -231,6 +251,9 @@ CMidiPlayer::CMidiPlayer(bool singleInst)
{
midiFile=NULL;resumed=false;singleInstance=singleInst;
settings=NULL;synth=NULL;adriver=NULL;waitvoice=true;
+ memset(mappedoutput,0,sizeof(mappedoutput));
+ memset(deviceusage,0,sizeof(deviceusage));
+ mapper=new qmpMidiMapperRtMidi();
#ifdef _WIN32
QueryPerformanceFrequency((LARGE_INTEGER*)&pf);
#endif
@@ -238,6 +261,7 @@ CMidiPlayer::CMidiPlayer(bool singleInst)
CMidiPlayer::~CMidiPlayer()
{
if(singleInstance)fluidDeinitialize();
+ delete mapper;
}
void CMidiPlayer::playerPanic(bool reset)
{
@@ -402,3 +426,21 @@ int CMidiPlayer::getSFCount()
{return synth?fluid_synth_sfcount(synth):0;}
fluid_sfont_t* CMidiPlayer::getSFPtr(int sfid)
{return synth&&sfid<getSFCount()?fluid_synth_get_sfont(synth,sfid):NULL;}
+
+qmpMidiMapperRtMidi* CMidiPlayer::getMidiMapper(){return mapper;}
+void CMidiPlayer::setChannelOutput(int ch,int devid)
+{
+ int origoutput=mappedoutput[ch];
+ int newoutput=0;
+ if(devid>0)
+ {
+ if(!deviceusage[devid-1])deviceiid[devid]=newoutput=mapper->deviceInit(devid-1);
+ ++deviceusage[deviceiid[devid]];
+ }
+ mappedoutput[ch]=devid?deviceiid[devid]+1:0;
+ if(origoutput>0)
+ {
+ --deviceusage[origoutput-1];
+ if(!deviceusage[origoutput-1])mapper->deviceDeinit(origoutput-1);
+ }
+}
diff --git a/core/qmpmidiplay.hpp b/core/qmpmidiplay.hpp
index fafd677..a7e7f88 100644
--- a/core/qmpmidiplay.hpp
+++ b/core/qmpmidiplay.hpp
@@ -5,6 +5,7 @@
#include <cstdint>
#include <cstdlib>
#include <fluidsynth.h>
+#include "qmpmidimappers.hpp"
struct SEvent
{
uint32_t iid,time,p1,p2;
@@ -69,6 +70,8 @@ class CMidiPlayer
//thread control
uint32_t tceptr,tcpaused,tcstop;
uint32_t finished,resumed;
+ qmpMidiMapperRtMidi *mapper;
+ int mappedoutput[16],deviceusage[16],deviceiid[128];
void setBit(uint16_t &n,uint16_t bn,uint16_t b);
void processEvent(const SEvent *e);
@@ -133,5 +136,8 @@ class CMidiPlayer
void pushSoundFont(const char* sf);
int getSFCount();
fluid_sfont_t* getSFPtr(int sfid);
+
+ qmpMidiMapperRtMidi* getMidiMapper();
+ void setChannelOutput(int ch,int devid);
};
#endif
diff --git a/qmidiplayer-desktop/qmidiplayer-desktop.pro b/qmidiplayer-desktop/qmidiplayer-desktop.pro
index 328af5d..0ac1b97 100644
--- a/qmidiplayer-desktop/qmidiplayer-desktop.pro
+++ b/qmidiplayer-desktop/qmidiplayer-desktop.pro
@@ -24,7 +24,8 @@ SOURCES += main.cpp\
qmpinfowindow.cpp \
qmpsettingswindow.cpp \
qmphelpwindow.cpp \
- qdialskulpturestyle.cpp
+ qdialskulpturestyle.cpp \
+ ../core/qmpmidimapperrtmidi.cpp
HEADERS += qmpmainwindow.hpp \
../core/qmpmidiplay.hpp \
@@ -37,7 +38,7 @@ HEADERS += qmpmainwindow.hpp \
qmpsettingswindow.hpp \
qmphelpwindow.hpp \
qdialskulpturestyle.hpp \
- qmpimidimapper.hpp
+ ../core/qmpmidimappers.hpp
FORMS += qmpmainwindow.ui \
qmpplistwindow.ui \
@@ -57,7 +58,7 @@ unix{
target.path = $$BINDIR
INSTALLS += target
QMAKE_CXXFLAGS += -std=c++11 -Wall
- LIBS += -lfluidsynth
+ LIBS += -lfluidsynth -lrtmidi
}
win32:LIBS += e:/libs/fluidsynth/fluidsynth.lib winmm.lib #You have to change these
win32:INCLUDEPATH += e:/libs/fluidsynth/include #before building...
diff --git a/qmidiplayer-desktop/qmpchannelswindow.cpp b/qmidiplayer-desktop/qmpchannelswindow.cpp
index a9cf60e..4d5f6ab 100644
--- a/qmidiplayer-desktop/qmpchannelswindow.cpp
+++ b/qmidiplayer-desktop/qmpchannelswindow.cpp
@@ -13,16 +13,19 @@ qmpChannelsWindow::qmpChannelsWindow(QWidget *parent) :
pselectw=new qmpPresetSelector(this);
ceditw=new qmpChannelEditor(this);
connect(this,SIGNAL(dialogClosing()),parent,SLOT(dialogClosed()));
+ mapper=qmpMainWindow::getInstance()->getPlayer()->getMidiMapper();
+ int devc=mapper->enumDevices();
for(int i=0;i<16;++i)
{
ui->twChannels->setCellWidget(i,0,new QCheckBox(""));
connect(ui->twChannels->cellWidget(i,0),SIGNAL(stateChanged(int)),this,SLOT(channelMSChanged()));
ui->twChannels->setCellWidget(i,1,new QCheckBox(""));
connect(ui->twChannels->cellWidget(i,1),SIGNAL(stateChanged(int)),this,SLOT(channelMSChanged()));
- ui->twChannels->setCellWidget(i,2,new QComboBox());
- QComboBox *cb=(QComboBox*)ui->twChannels->cellWidget(i,2);
- //stub
- cb->addItem("Internal FluidSynth");
+ ui->twChannels->setCellWidget(i,2,new QDCComboBox());
+ QDCComboBox *cb=(QDCComboBox*)ui->twChannels->cellWidget(i,2);
+ cb->addItem("Internal FluidSynth");cb->setID(i);
+ for(int j=0;j<devc;++j)cb->addItem(mapper->deviceName(j).c_str());
+ connect(cb,SIGNAL(onChange(int,int)),this,SLOT(changeMidiMapping(int,int)));
ui->twChannels->setCellWidget(i,3,new QDCLabel(""));
((QDCLabel*)ui->twChannels->cellWidget(i,3))->setID(i);
connect(ui->twChannels->cellWidget(i,3),SIGNAL(onDoubleClick(int)),this,SLOT(showPresetWindow(int)));
@@ -130,3 +133,8 @@ void qmpChannelsWindow::showChannelEditorWindow(int chid)
ceditw->show();
ceditw->setupWindow(chid);
}
+
+void qmpChannelsWindow::changeMidiMapping(int chid,int idx)
+{
+ qmpMainWindow::getInstance()->getPlayer()->setChannelOutput(chid,idx);
+}
diff --git a/qmidiplayer-desktop/qmpchannelswindow.hpp b/qmidiplayer-desktop/qmpchannelswindow.hpp
index f0591b6..57f293e 100644
--- a/qmidiplayer-desktop/qmpchannelswindow.hpp
+++ b/qmidiplayer-desktop/qmpchannelswindow.hpp
@@ -4,11 +4,13 @@
#include <QLabel>
#include <QPushButton>
#include <QDialog>
+#include <QComboBox>
#include <QShowEvent>
#include <QCloseEvent>
#include <QMoveEvent>
#include "qmppresetselect.hpp"
#include "qmpchanneleditor.hpp"
+#include "../core/qmpmidimappers.hpp"
namespace Ui {
class qmpChannelsWindow;
@@ -42,6 +44,20 @@ class QDCPushButton:public QPushButton
void onClick(int id);
};
+class QDCComboBox:public QComboBox
+{
+ Q_OBJECT
+ private:
+ int id;
+ public:
+ QDCComboBox():QComboBox(){id=-1;connect(this,SIGNAL(currentIndexChanged(int)),this,SLOT(indexChangedSlot(int)));}
+ void setID(int _id){id=_id;}
+ signals:
+ void onChange(int id,int idx);
+ public slots:
+ void indexChangedSlot(int idx){emit(onChange(id,idx));}
+};
+
class qmpChannelsWindow:public QDialog
{
Q_OBJECT
@@ -59,6 +75,7 @@ class qmpChannelsWindow:public QDialog
void channelMSChanged();
void showPresetWindow(int chid);
void showChannelEditorWindow(int chid);
+ void changeMidiMapping(int chid,int idx);
void on_pbUnmute_clicked();
void on_pbUnsolo_clicked();
@@ -66,6 +83,7 @@ class qmpChannelsWindow:public QDialog
Ui::qmpChannelsWindow *ui;
qmpPresetSelector *pselectw;
qmpChannelEditor *ceditw;
+ qmpMidiMapperRtMidi *mapper;
};
#endif // QMPCHANNELSWINDOW_H
diff --git a/qmidiplayer-desktop/qmpimidimapper.hpp b/qmidiplayer-desktop/qmpimidimapper.hpp
deleted file mode 100644
index d35dbb9..0000000
--- a/qmidiplayer-desktop/qmpimidimapper.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef QMPIMIDIMAPPER_H
-#define QMPIMIDIMAPPER_H
-class qmpIMidiMapper
-{
- virtual void deviceInit(int id)=0;
- virtual void deviceDeinit(int id)=0;
- virtual void noteOn(int ch,int key,int vel)=0;
- virtual void noteOff(int ch,int key)=0;
- virtual void ctrlChange(int ch,int cc,int val)=0;
- virtual void progChange(int ch,int val)=0;
- virtual void pitchBend(int ch,int val)=0;
- virtual void sysEx(int length,const char* data)=0;
- virtual static int enumDevices()=0;
- virtual static char* deviceName(int id)=0;
-};
-#endif // QMPIMIDIMAPPER_H
diff --git a/qmidiplayer-desktop/qmpmainwindow.cpp b/qmidiplayer-desktop/qmpmainwindow.cpp
index 58caea1..32d28f2 100644
--- a/qmidiplayer-desktop/qmpmainwindow.cpp
+++ b/qmidiplayer-desktop/qmpmainwindow.cpp
@@ -20,6 +20,8 @@ qmpMainWindow::qmpMainWindow(QWidget *parent) :
ui->lbFileName->setText("");ref=this;
playing=false;stopped=true;dragging=false;
settingsw=new qmpSettingsWindow(this);
+ singleFS=qmpSettingsWindow::getSettingsIntf()->value("Behavior/SingleInstance",0).toInt();
+ player=new CMidiPlayer(singleFS);
plistw=new qmpPlistWindow(this);
chnlw=new qmpChannelsWindow(this);
efxw=new qmpEfxWindow(this);
@@ -30,8 +32,6 @@ qmpMainWindow::qmpMainWindow(QWidget *parent) :
fnA2=new QAction("Render to Wave",ui->lbFileName);
ui->lbFileName->addAction(fnA1);
ui->lbFileName->addAction(fnA2);
- singleFS=qmpSettingsWindow::getSettingsIntf()->value("Behavior/SingleInstance",0).toInt();
- player=new CMidiPlayer(singleFS);
if(singleFS){player->fluidPreInitialize();playerSetup();player->fluidInitialize();
for(int i=settingsw->getSFWidget()->count()-1;i>=0;--i)
player->pushSoundFont(settingsw->getSFWidget()->item(i)->text().toStdString().c_str());}
diff --git a/qmidiplayer-lite/qmidiplayer-lite.pro b/qmidiplayer-lite/qmidiplayer-lite.pro
index 9fb8712..4307068 100644
--- a/qmidiplayer-lite/qmidiplayer-lite.pro
+++ b/qmidiplayer-lite/qmidiplayer-lite.pro
@@ -7,7 +7,8 @@ CONFIG += c++11
SOURCES += main.cpp \
../core/qmpmidiplay.cpp \
- ../core/qmpmidiread.cpp
+ ../core/qmpmidiread.cpp \
+ ../core/qmpmidimapperrtmidi.cpp
RESOURCES += qml.qrc
@@ -19,6 +20,7 @@ include(deployment.pri)
HEADERS += \
../core/qmpmidiplay.hpp \
- qmpcorewrapper.hpp
+ qmpcorewrapper.hpp \
+ ../core/qmpmidimappers.hpp
-LIBS += -lfluidsynth
+LIBS += -lfluidsynth -lrtmidi