From 8f8782f43bd838e825f3792b611d27fffa90399b Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Thu, 7 Apr 2016 23:39:02 +0800 Subject: Lunatic Princess. --- ChangeLog | 7 +++ README.md | 2 +- core/qmpmidimapperrtmidi.cpp | 88 +++++++++++++++++++++++++++++ core/qmpmidimappers.hpp | 23 ++++++++ core/qmpmidiplay.cpp | 54 ++++++++++++++++-- core/qmpmidiplay.hpp | 6 ++ qmidiplayer-desktop/qmidiplayer-desktop.pro | 7 ++- qmidiplayer-desktop/qmpchannelswindow.cpp | 16 ++++-- qmidiplayer-desktop/qmpchannelswindow.hpp | 18 ++++++ qmidiplayer-desktop/qmpimidimapper.hpp | 16 ------ qmidiplayer-desktop/qmpmainwindow.cpp | 4 +- qmidiplayer-lite/qmidiplayer-lite.pro | 8 ++- 12 files changed, 214 insertions(+), 35 deletions(-) create mode 100644 core/qmpmidimapperrtmidi.cpp create mode 100644 core/qmpmidimappers.hpp delete mode 100644 qmidiplayer-desktop/qmpimidimapper.hpp 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 +#include +#include +#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::vectormessage; + 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::vectormessage; + 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::vectormessage; + 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::vectormessage; + 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::vectormessage; + 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::vectormessage(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 +#include #include #include #include @@ -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&&sfid0) + { + 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 #include #include +#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;jaddItem(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 #include #include +#include #include #include #include #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 -- cgit v1.2.3