aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2021-09-05 00:18:55 -0400
committerGravatar Chris Xiong <chirs241097@gmail.com> 2021-09-05 00:18:55 -0400
commitb13f9868da3799ace9468c7bebcece7a7df40f43 (patch)
tree7f2f9f6a9e4c1a959913f804b59e069ae1308d0a
parentc3a84c9b5f3c3a9b95f95b1d6693ef186cf0fa7d (diff)
downloadoddities-b13f9868da3799ace9468c7bebcece7a7df40f43.tar.xz
Implement retrigger and fine vibrato.HEADmaster
Further fixes for portamento, no more sliding to oblivion. This thing will get a complete rewrite eventually.
-rw-r--r--music/it2midi.cpp101
1 files 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 <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())
@@ -751,28 +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)//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;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
@@ -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<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)
{
@@ -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<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);