diff options
-rw-r--r-- | music/it2midi.cpp | 122 |
1 files changed, 108 insertions, 14 deletions
diff --git a/music/it2midi.cpp b/music/it2midi.cpp index 2fe3b26..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 <cstdio> #include <cstring> @@ -67,10 +72,12 @@ #include <cmath> #include <stdexcept> #include <algorithm> +#include <functional> #include <map> #include <string> #include <utility> #include <vector> +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<MidiTrack> tracks; - void dumpFile() + void dumpFile(std::vector<size_t> tracksw={}) { puts("Midi File dump"); - for(MidiTrack &i:tracks) + if(tracksw.empty()) + for(size_t i=0;i<tracks.size();++i)tracksw.push_back(i); + for(size_t& ii:tracksw) { + MidiTrack &i=tracks[ii]; puts("==============track=============="); for(MidiEvent &j:i.eventList) if(j.str.length()) @@ -651,6 +661,7 @@ class ITConverter if(mask>>2&1)//vol { if(vol<=64)par->chvelm[ch]=(vol==64?127:2*vol); + if(par->chvelm[ch]==0)par->chvelm[ch]=1; else if(vol>=128&&vol<=192) { par->chpanm[ch]=(vol==192?127:(vol-128)*2); @@ -663,12 +674,17 @@ class ITConverter //default volume uint32_t curnote=par->chnote[ch]; if(mask>>0&1)curnote=note; - if(curnote<120) - par->chvelm[ch]=par->c.sample[par->c.instr[par->chinst[ch]].sampleref[curnote]-1].defvol*2; + if(curnote<120 && curnote>=12) + par->chvelm[ch]=par->c.sample[par->c.instr[par->chinst[ch]].sampleref[curnote-12]-1].defvol*2; if(par->chvelm[ch]>127) par->chvelm[ch]=127; + if(par->chvelm[ch]==0) + { + //printf("!!!!inst %d samp %d note %d def vol %d\n",par->chinst[ch],par->c.instr[par->chinst[ch]].sampleref[curnote]-1,curnote,par->c.sample[par->c.instr[par->chinst[ch]].sampleref[curnote]-1].defvol*2); + par->chvelm[ch]=1; + } } - if((mask>>0&1)&¬e<120)//reset for pitch slides (E/F/G) + if((mask>>0&1)&¬e<120&&!((mask>>3&1)&&efx==7))//reset for pitch slides (E/F, but not G) { if(fabs(par->chpitchm[ch])>1e-6) { @@ -745,23 +761,36 @@ class ITConverter } break; case 7://porta + { if(fxp) { par->chefxmem[ch][efx]=fxp; //if(!(mask>>0&1)||note>=120) // puts("no note to slide to?"); + 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;tk<par->speed;++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 @@ -773,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; @@ -821,6 +851,49 @@ class ITConverter } } break; + case 17://Retrigger + { + if(fxp) + par->chefxmem[ch][efx]=fxp; + else + fxp=par->chefxmem[ch][efx]; + std::vector<std::function<int(int)>> 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) { @@ -870,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; @@ -926,6 +1015,11 @@ class ITConverter par->f.tracks[par->minstch[std::make_pair(ch,par->chinst[ch])]]. eventList.push_back(MidiEvent::noteOn(par->curmiditk+notedelay,0,note,par->chvelm[ch])); } + else + { + par->chportadstnote[ch]=note; + if(debuggxx)printf("dst->%d\n",par->chportadstnote[ch]); + } } } } @@ -942,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; @@ -953,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)); @@ -968,7 +1062,7 @@ class ITConverter if(chnote[i]!=255) f.tracks[minstch[std::make_pair(i,chinst[i])]]. eventList.push_back(MidiEvent::noteOff(curmiditk,0,chnote[i])); - f.dumpFile(); + //f.dumpFile(); for(auto i=f.tracks.begin()+1;i!=f.tracks.end();) { bool df=false; @@ -979,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()); @@ -989,11 +1083,11 @@ class ITConverter { std::vector<size_t> 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); |