From b13f9868da3799ace9468c7bebcece7a7df40f43 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Sun, 5 Sep 2021 00:18:55 -0400 Subject: Implement retrigger and fine vibrato. Further fixes for portamento, no more sliding to oblivion. This thing will get a complete rewrite eventually. --- music/it2midi.cpp | 101 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 90 insertions(+), 11 deletions(-) diff --git a/music/it2midi.cpp b/music/it2midi.cpp index f31c8b2..ac64bee 100644 --- a/music/it2midi.cpp +++ b/music/it2midi.cpp @@ -25,6 +25,9 @@ * portamento to -> pitch wheel ~~ or MIDI CC 5 (portamento) (which?)~~ * vibrato -> MIDI CC 1 (modulation) * + * tempo stuff: + * 1 it tick <==> 40 midi ticks, converted file is always 960t/div + * * Effect column: * A Set Speed -> handled by per-pattern conversion * B Jump to Ord -> handled by per-pattern conversion and assembler @@ -60,6 +63,8 @@ * instrument max ticks from IT instrument * option for IT vol -> midi vel/midi expression * implement the reset of the vol/effect column effects + * custom controller mapping for each instrument + * */ #include #include @@ -67,10 +72,12 @@ #include #include #include +#include #include #include #include #include +const int debuggxx=0; const char* IMPHdr="IMPM"; uint16_t readSW(FILE* f) { @@ -525,11 +532,14 @@ class MidiFile public: uint16_t divs; std::vector tracks; - void dumpFile() + void dumpFile(std::vector tracksw={}) { puts("Midi File dump"); - for(MidiTrack &i:tracks) + if(tracksw.empty()) + for(size_t i=0;ichefxmem[ch][efx]=fxp; //if(!(mask>>0&1)||note>=120) // puts("no note to slide to?"); - if(par->chnote[ch]>=120)//Gxx without a prior note doesn't seem to have any effect? + if(par->chnote[ch]>=120 || previnst != par->chinst[ch])//Gxx without a prior note doesn't seem to have any effect? Also doesn't carry through inst changes? { efx=par->chefxmem[ch][efx]=0; break; } par->chportasrcnote[ch]=par->chnote[ch]; - par->chportadstnote[ch]=note; + if(mask&1) + par->chportadstnote[ch]=note; } else + { fxp=par->chefxmem[ch][efx]; + if(mask&1) + par->chportadstnote[ch]=note; + } + if(debuggxx)printf("src %d dst %d\n", par->chportasrcnote[ch], par->chportadstnote[ch]); for(int tk=0;tkspeed;++tk) { if(fabs(par->chpitchm[ch]+par->chportasrcnote[ch]-par->chportadstnote[ch])<1e-6) break; - int portadir=(par->chpitchm[ch]+par->chportasrcnote[ch])>par->chportadstnote[ch]?-1:1; + if(debuggxx)printf("pitch %f\n",par->chpitchm[ch]); + int portadir=((par->chpitchm[ch]+par->chportasrcnote[ch])>par->chportadstnote[ch])?-1:1; double nextpitchm=par->chpitchm[ch]+1.*portadir*fxp/16.; - int nextportadir=(nextpitchm+par->chportasrcnote[ch])>par->chportadstnote[ch]?-1:1; + int nextportadir=((nextpitchm+par->chportasrcnote[ch])>par->chportadstnote[ch])?-1:1; if(nextportadir*portadir<0)//detect overshoot par->chpitchm[ch]=par->chportadstnote[ch]-par->chportasrcnote[ch]; else @@ -784,6 +802,7 @@ class ITConverter par->f.tracks[par->minstch[std::make_pair(ch,par->chinst[ch])]]. eventList.push_back(MidiEvent::pb(40*tk+par->curmiditk,0,pbvi)); } + } break; case 8://vib par->chefxflg[ch][efx]=1; @@ -832,6 +851,49 @@ class ITConverter } } break; + case 17://Retrigger + { + if(fxp) + par->chefxmem[ch][efx]=fxp; + else + fxp=par->chefxmem[ch][efx]; + std::vector> retrigmod = { + [](int a){return a;}, + [](int a){return a-1>0?a-1:0;}, + [](int a){return a-2>0?a-2:0;}, + [](int a){return a-4>0?a-4:0;}, + [](int a){return a-8>0?a-8:0;}, + [](int a){return a-16>0?a-16:0;}, + [](int a){return a*2/3;}, + [](int a){return a/2;}, + [](int a){return a;}, + [](int a){return a+1>63?63:a+1;}, + [](int a){return a+2>63?63:a+2;}, + [](int a){return a+4>63?63:a+4;}, + [](int a){return a+8>63?63:a+8;}, + [](int a){return a+16>63?63:a+16;}, + [](int a){return a*3/2>63?63:a*3/2;}, + [](int a){return a*2>63?63:a*2;}, + }; + int tspan = fxp & 0xF; + int vmod = fxp >> 4; + int ctk = 0; + int vel = par->chvelm[ch]; + uint32_t curnote=par->chnote[ch]; + if (tspan==0) break; //can't handle that! + if (curnote==255) curnote=note; + auto &eventList = par->f.tracks[par->minstch[std::make_pair(ch,par->chinst[ch])]].eventList; + + while (ctk + tspan < par->speed) + { + eventList.push_back(MidiEvent::noteOff(par->curmiditk+40*ctk,0,curnote)); + vel = retrigmod[vmod](vel); + eventList.push_back(MidiEvent::noteOn(par->curmiditk+40*ctk,0,curnote, vel)); + ctk += tspan; + } + eventList.push_back(MidiEvent::noteOff(par->curmiditk+40*ctk,0,curnote)); + } + break; case 19://Sxx controls switch(fxp&0xf0) { @@ -881,6 +943,22 @@ class ITConverter } } break; + case 21://fine vib + par->chefxflg[ch][efx]=1; + //memory implementation is non-compliant + if(fxp) + par->chefxmem[ch][efx]=fxp; + else + { + //fxp=par->chefxmem[ch][efx]; + //actually midi has us covered already, + //no need to save the memory manually. + break; + } + //vibrato rate is currently ignored, even it is supported on some synths as CC76 + par->f.tracks[par->minstch[std::make_pair(ch,par->chinst[ch])]]. + eventList.push_back(MidiEvent::cc(par->curmiditk,0,1,(fxp&0x0f)*2)); + break; case 24://set pan { uint64_t param=fxp/2; @@ -940,6 +1018,7 @@ class ITConverter else { par->chportadstnote[ch]=note; + if(debuggxx)printf("dst->%d\n",par->chportadstnote[ch]); } } } @@ -957,7 +1036,7 @@ class ITConverter for(auto &i:minstch) { i.second=trcnt++; - printf("%d, %d -> %d\n",i.first.first,i.first.second,i.second); + printf("ch %d, inst %d -> %d\n",i.first.first,i.first.second,i.second); } for(int i=0;i<64;++i)chvelm[i]=chvolm[i]=100,chpanm[i]=64,chnote[i]=255,chinst[i]=1; speed=c.initspeed,tempo=c.inittempo; @@ -968,7 +1047,7 @@ class ITConverter { f.tracks.push_back(MidiTrack()); char buf[48]; - sprintf(buf,"%s @ ch%d",c.instr[i->first.second].name.c_str(),i->first.first); + snprintf(buf,48,"%s @ ch%d",c.instr[i->first.second].name.c_str(),i->first.first); f.tracks.back().eventList.push_back(MidiEvent::trkname(0,buf)); f.tracks.back().eventList.push_back(MidiEvent::cc(0,0,0x65,0)); f.tracks.back().eventList.push_back(MidiEvent::cc(0,0,0x64,0)); @@ -994,7 +1073,7 @@ class ITConverter for(auto &j:i->eventList) if((j.type&0xF0)==0x90)df=false; } - if(df){auto j=i+1;f.tracks.erase(i);i=j;} + if(df&&!single_instr_mode){auto j=i+1;f.tracks.erase(i);i=j;} else ++i; } printf("final file has %lu tracks.\n",f.tracks.size()); @@ -1004,11 +1083,11 @@ class ITConverter { std::vector tr={0}; for(auto &pr:minstch) - if(pr.first.second==instr) + if(pr.first.second==instr && pr.second != 0) tr.push_back(pr.second); if(tr.size()==1) { - printf("instr#%d seems unused, skipped\n",instr); + printf("instr #%d seems unused, skipped\n",instr); continue; } f.writeFile((std::string(path)+"."+std::to_string(instr)+".mid").c_str(),tr); -- cgit v1.2.3