[破事水]一个用midi生成视频的小程序
c4droid吧
全部回复
仅看楼主
level 11
qishipai 楼主
[破事水]一个用midi生成视频的小程序
2020年05月18日 04点05分 1
level 11
qishipai 楼主
2020年05月18日 04点05分 2
level 11
qishipai 楼主
//midi.h
#ifndef MIDI
#define MIDI
#include<cstdio>
#include<cstdlib>
class MIDI_HEAD
{
public:
unsigned short type,tracks,ppnq;
void get(FILE*);
void put(FILE*)const;
};
class MIDI_EVENT
{
public:
char*data;
unsigned int tick,size;
short num,val,chan,type,otype;
void get(FILE*);
void put(FILE*)const;
void destroy()const;
private:
void putvlnum(unsigned int,FILE*)const;
void getvlnum(unsigned int*,FILE*)const;
};
unsigned int rev_u32(unsigned int);
unsigned short rev_u16(unsigned short);
#endif
2020年05月18日 04点05分 3
level 11
qishipai 楼主
//midi.cpp
#include"midi.h"
unsigned int rev_u32(unsigned int x)
{
return(x&0xFF)<<24|(x&0xFF00)<<8|(x&0xFF0000)>>8|(x&0xFF000000)>>24;
}
unsigned short rev_u16(unsigned short x)
{
return(x&0xFF)<<8|x>>8;
}
void MIDI_HEAD::get(FILE*inmidi)
{
unsigned short tmp;
fseeko(inmidi,8,SEEK_SET);
fread(&(tmp=0),2,1,inmidi);
type=rev_u16(tmp);
fread(&(tmp=0),2,1,inmidi);
tracks=rev_u16(tmp);
fread(&(tmp=0),2,1,inmidi);
ppnq=rev_u16(tmp);
}
void MIDI_HEAD::put(FILE*outmidi)const
{
unsigned long long tmp;
fwrite(&(tmp=0x60000006468544Dull),8,1,outmidi);
fwrite(&(tmp=rev_u16(type)),2,1,outmidi);
fwrite(&(tmp=rev_u16(tracks)),2,1,outmidi);
fwrite(&(tmp=rev_u16(ppnq)),2,1,outmidi);
}
void MIDI_EVENT::get(FILE*inmidi)
{
short tmp;
getvlnum(&tick,inmidi);
fread(&(tmp=0),1,1,inmidi);
chan=tmp&0xF;
switch(type=tmp&0xF0)
{
case(0x80):
case(0x90):
case(0xA0):
case(0xB0):
fread(&(num=0),1,1,inmidi);
case(0xC0):
case(0xD0):
fread(&(val=0),1,1,inmidi);
break;
case(0xE0):
tmp=0;
fread(&(val=0),1,1,inmidi);
fread(&(tmp=0),1,1,inmidi);
val=val<<7|tmp;
break;
case(0xF0):
if(chan==0xF)
{
chan=0;
type=0xFF;
fread(&otype,1,1,inmidi);
}
getvlnum(&size,inmidi);
data=(char*)malloc(size);
fread(data,size,1,inmidi);
}
}
void MIDI_EVENT::put(FILE*outmidi)const
{
short tmp;
putvlnum(tick,outmidi);
fwrite(&(tmp=type|chan),1,1,outmidi);
switch(type)
{
case(0x80):
case(0x90):
case(0xA0):
case(0xB0):
fwrite(&num,1,1,outmidi);
case(0xC0):
case(0xD0):
fwrite(&val,1,1,outmidi);
break;
case(0xE0):
fwrite(&(tmp=val>>7),1,1,outmidi);
fwrite(&(tmp=val&0x7F),1,1,outmidi);
break;
case(0xFF):
fwrite(&otype,1,1,outmidi);
case(0xF0):
putvlnum(size,outmidi);
fwrite(data,size,1,outmidi);
}
}
void MIDI_EVENT::destroy()const
{
if(type&0xF0==0xF0)
{
free(data);
}
}
2020年05月18日 04点05分 4
level 11
qishipai 楼主
//接上一楼
void MIDI_EVENT::putvlnum(unsigned int vlnum,FILE*outfile)const
{
short p=10;
char tmp[10];
do
{
tmp[--p]=vlnum&0x7F;
if(p<9)
{
tmp[p]|=0x80;
}
}while(vlnum>>=7);
fwrite(tmp+p,10-p,1,outfile);
}
void MIDI_EVENT::getvlnum(unsigned int*vlnum,FILE*infile)const
{
char tmp;
unsigned int resu=0;
do
{
tmp=0;
resu<<=7;
fread(&tmp,1,1,infile);
resu|=tmp&0x7F;
}while(tmp&0x80);
*vlnum=resu;
}
2020年05月18日 04点05分 5
level 11
qishipai 楼主
midi.h midi.cpp
提供一套读写midi的函数
这里只用读文件部分
2020年05月18日 04点05分 6
level 11
qishipai 楼主
//midiload.h
#ifndef MIDILOAD
#define MIDILOAD
#include"midi.h"
typedef struct
{
short chan,key,vol;
unsigned short track;
unsigned long long sta,end;
}EVENT_NOTE;
typedef struct
{
int val;
unsigned long long sta;
}EVENT_SPD;
class MIDI_FILE
{
public:
MIDI_HEAD filehead;
EVENT_NOTE*notelist;
EVENT_SPD*speedlist;
int notecount,speedcount;
unsigned long long filelength;
void init(const char*);
void destroy()const;
private:
void sortnotes(int,int)const;
void sortspds(int,int)const;
};
#endif
2020年05月18日 04点05分 7
level 11
qishipai 楼主
//midiload.cpp
#include"midiload.h"
#define SLIST_RLC_STEP 2000
#define NLIST_RLC_STEP 50000
void MIDI_FILE::init(const char*midifile)
{
off_t i;
MIDI_EVENT event;
unsigned short j=0;
unsigned long long absotick;
FILE*midi=fopen(midifile,"rb");
int k=NLIST_RLC_STEP,l=SLIST_RLC_STEP,stat[128];
filelength=0;
notecount=0;
speedcount=0;
speedlist=(EVENT_SPD*)malloc(sizeof(EVENT_SPD)*l);
notelist=(EVENT_NOTE*)malloc(sizeof(EVENT_NOTE)*k);
filehead.get(midi);
if(filehead.type==2)
{
puts("deprecated midi format!!!");
return;
}
printf("<QMIDICORE 2020>\nmidi info (%s) :\n",midifile);
printf("[format%3d][%5d tracks][%5d ppnq]\n",filehead.type,filehead.tracks,filehead.ppnq);
while(j<filehead.tracks)
{
fseeko(midi,4,SEEK_CUR);
fread(&i,4,1,midi);
i=ftello(midi)+rev_u32(i);
for(absotick=0;ftello(midi)<i;event.destroy())
{
event.get(midi);
absotick+=event.tick;
if(event.type==0xFF&&event.otype==0x51)
{
if(l==speedcount)
{
l+=SLIST_RLC_STEP;
speedlist=(EVENT_SPD*)realloc(speedlist,sizeof(EVENT_SPD)*l);
}
speedlist[speedcount].sta=absotick;
speedlist[speedcount].val=rev_u32(*(int*)(event.data))>>8;
++speedcount;
}
if(event.type==0x90)
{
if(k==notecount)
{
k+=NLIST_RLC_STEP;
notelist=(EVENT_NOTE*)realloc(notelist,sizeof(EVENT_NOTE)*k);
}
notelist[notecount].track=j;
stat[event.num]=notecount;
notelist[notecount].sta=absotick;
notelist[notecount].vol=event.val;
notelist[notecount].key=event.num;
notelist[notecount].chan=event.chan;
++notecount;
}
if(event.type==0x80)
{
notelist[stat[event.num]].end=absotick;
}
}
if(absotick>filelength)
{
filelength=absotick;
}
printf("< track 1 ~%4d > [%9d loaded]\n",++j,notecount);
}
fclose(midi);
puts("sorting events......");
sortnotes(0,notecount-1);
sortspds(0,speedcount-1);
printf("finish loading %9d notes in all %4d tracks!!!\n",notecount,filehead.tracks);
}
2020年05月18日 04点05分 8
level 11
qishipai 楼主
//接上一楼
void MIDI_FILE::destroy()const
{
free(notelist);
free(speedlist);
}
void MIDI_FILE::sortnotes(int l,int r)const
{
if(l<r)
{
int i=l,j=r;
EVENT_NOTE k=notelist[l];
while(i<j)
{
for(;i<j&&(notelist[j].sta>k.sta||(notelist[j].sta==k.sta&¬elist[j].track>=k.track));--j);
i<j?notelist[i++]=notelist[j]:k;
for(;i<j&&(notelist[i].sta<k.sta||(notelist[i].sta==k.sta&¬elist[i].track<=k.track));++i);
i<j?notelist[j--]=notelist[i]:k;
}
notelist[i]=k;
sortnotes(l,i-1);
sortnotes(i+1,r);
}
}
void MIDI_FILE::sortspds(int l,int r)const
{
if(l<r)
{
int i=l,j=r;
EVENT_SPD k=speedlist[l];
while(i<j)
{
for(;i<j&&speedlist[j].sta>=k.sta;--j);
i<j?speedlist[i++]=speedlist[j]:k;
for(;i<j&&speedlist[i].sta<=k.sta;++i);
i<j?speedlist[j--]=speedlist[i]:k;
}
speedlist[i]=k;
sortspds(l,i-1);
sortspds(i+1,r);
}
}
2020年05月18日 04点05分 9
level 11
qishipai 楼主
midiload.h midiload.cpp
提供加载文件的函数
2020年05月18日 04点05分 10
level 11
qishipai 楼主
//drawraw.h
#ifndef DRAWRAW
#define DRAWRAW
#include<cstdio>
#include<cstdlib>
class CANVAS
{
public:
FILE*rawout;
unsigned int**canvas;
int canvasw,canvash,keyh;
void init(int,const char*);
void clean()const;
void drawnote(short,int,int,unsigned int)const;
void drawkeys(unsigned int*)const;
void write()const;
void destroy()const;
private:
short keyx[128],notex[128];
void drawrect(int,int,int,int,unsigned int)const;
void drawfillrect(int,int,int,int,unsigned int)const;
};
#endif
2020年05月18日 04点05分 11
level 11
qishipai 楼主
//drawraw.cpp
#include"drawraw.h"
static short notew[]={8,6,8,6,8,8,6,7,6,7,6,8};
static short drawmap[128]=
{
0,2,4,5,7,9,11,12,14,16,17,19,21,23,24,26,28,29,
31,33,35,36,38,40,41,43,45,47,48,50,52,53,55,57,
59,60,62,64,65,67,69,71,72,74,76,77,79,81,83,84,
86,88,89,91,93,95,96,98,100,101,103,105,107,108,
110,112,113,115,117,119,120,122,124,125,127,1,3,
6,8,10,13,15,18,20,22,25,27,30,32,34,37,39,42,44,
46,49,51,54,56,58,61,63,66,68,70,73,75,78,80,82,
85,87,90,92,94,97,99,102,104,106,109,111,114,116,
118,121,123,126,
};
void CANVAS::init(int h,const char*args)
{
int i;
short genkeyx[]={0,8,12,22,24,36,44,48,57,60,70,72};
short gennotex[]={0,8,14,22,28,36,44,50,57,63,70,76};
keyh=100;
canvash=h;
canvasw=900;
canvas=(unsigned int**)malloc(sizeof(unsigned int*)*canvash);
for(i=0;i<h;++i)
{
canvas[i]=(unsigned int*)malloc(sizeof(unsigned int)*canvasw);
}
for(i=0;i<128;++i)
{
notex[i]=i/12*84+gennotex[i%12];
keyx[i]=drawmap[i]/12*84+genkeyx[drawmap[i]%12];
}
for(i=0;i<12;++i)
{
notew[i]=notew[i];
}
rawout=popen(args,"w");
}
void CANVAS::clean()const
{
int i,j;
for(i=0;i<canvasw;++i)
{
for(j=0;j<canvash;++j)
{
canvas[j][i]=0xFF000000;
}
}
}
void CANVAS::drawkeys(unsigned int*keys)const
{
int i;
for(i=0;i<74;++i)
{
drawfillrect(keyx[i],0,12,100,keys[drawmap[i]]);
drawrect(keyx[i],0,13,100,0xFF000000);
}
drawfillrect(keyx[i],0,12,100,keys[drawmap[i]]);
drawrect(keyx[i],0,12,100,0xFF000000);
for(++i;i<128;++i)
{
drawfillrect(keyx[i],38,6,62,keys[drawmap[i]]);
drawrect(keyx[i],38,7,62,0xFF000000);
}
}
void CANVAS::drawnote(short key,int y,int h,unsigned int c)const
{
int w=key==127?9:notew[key%12],x=notex[key]+1;
drawrect(x,y,w,h,c);
drawfillrect(x,y,w,h,c);
}
void CANVAS::write()const
{
int i;
for(i=0;i<canvash;++i)
{
fwrite(canvas[i],4,canvasw,rawout);
}
}
void CANVAS::destroy()const
{
int i;
printf("%d",canvash);
for(i=0;i<canvash;++i)
{
free(canvas[i]);
}
free(canvas);
pclose(rawout);
}
2020年05月18日 04点05分 12
level 11
qishipai 楼主
//接上一楼
void CANVAS::drawrect(int x,int y,int w,int h,unsigned int c)const
{
int i,j;
if(x<canvasw)
{
for(i=y;i<y+h;++i)
{
canvas[i][x]=c;
}
}
if(y<canvash)
{
for(i=x;i<x+w;++i)
{
canvas[y][i]=c;
}
}
if(w>1)
{
for(i=y;i<y+h;++i)
{
canvas[i][x+w-1]=c;
}
}
if(h>1)
{
for(i=x;i<x+w;++i)
{
canvas[y+h-1][i]=c;
}
}
}
void CANVAS::drawfillrect(int x,int y,int w,int h,unsigned int c)const
{
int i,j;
for(i=x;i<x+w;++i)
{
for(j=y;j<y+h;++j)
{
canvas[j][i]=c;
}
}
}
2020年05月18日 04点05分 13
level 11
qishipai 楼主
drawraw.h drawraw.cpp
提供了生成rawvideo(绘音符,绘键盘)并通过管道输出的函数
2020年05月18日 04点05分 14
level 11
qishipai 楼主
//main.cpp
#include"midiload.h"
#include"drawraw.h"
#include<conio.h>
const int FPS=60;
const float rat=5.0f;
double tick=0,spd;
unsigned int sta=0,*col,keycol[128];
int main()
{
CANVAS paper;
MIDI_FILE target;
int i,j,k=0,l,m,n,o,frms;
target.init("Ouranos.mid");
paper.init(1024,"ffmpeg -y -f rawvideo -pix_fmt rgba -s 900x1024 -r 60 -i - -vf vflip -pix_fmt yuv420p -c:v libx264 out.mp4");
col=(unsigned int*)malloc(sizeof(unsigned int)*target.filehead.tracks);
for(i=0,srand('h');i<target.filehead.tracks;++i)
{
col[i]=0x90000000;
col[i]|=rand()%136+81;
col[i]|=(rand()%136+81)<<8;
col[i]|=(rand()%136+81)<<16;
}
while(tick<target.filelength)
{
paper.clean();
for(i=0;i<128;++i)
{
switch(i%12)
{
case(1):
case(3):
case(6):
case(8):
case(10):
keycol[i]=0xFF000000;
break;
default:
keycol[i]=0xFFFFFFFF;
}
}
for(;k<target.speedcount&&target.speedlist[k].sta<=tick;++k)
{
spd=(double)target.filehead.ppnq*1000000/(target.speedlist[k].val*FPS);
}
for(l=1,j=sta;j<target.notecount&&target.notelist[j].sta<=tick+(paper.canvash-paper.keyh)*rat;++j)
{
if(target.notelist[j].end>=tick)
{
if(l)
{
sta=j;
l=0;
}
if(target.notelist[j].sta<tick)
{
n=paper.keyh;
m=(float)(target.notelist[j].end-tick)/rat;
o=col[target.notelist[j].track]+0x00262626u;
keycol[target.notelist[j].key]=o;
}
else
{
o=col[target.notelist[j].track];
n=(target.notelist[j].sta-tick)/rat+paper.keyh;
m=(float)(target.notelist[j].end-target.notelist[j].sta)/rat;
}
if(m+n>paper.canvash)
{
m=paper.canvash-n;
}
else
{
if(m+n<paper.canvash-4&&m<4)
{
m=4;
}
}
paper.drawnote(target.notelist[j].key,n,m,o);
}
}
tick+=spd;
paper.drawkeys(keycol);
paper.write();
if(_kbhit()&&getch()=='q')
{
break;
}
}
free(col);
target.destroy();
paper.destroy();
return 0;
}
2020年05月18日 04点05分 15
1 2 尾页