2018-11-14 MIDI音乐格式笔记学习

梁子20163933

reference:
https://www.midifan.com/modulearticle-detailview-901.htm

关于MIDI文件格式

目前已知的:

  1. .mid文件属于二进制文件,可以通过电脑或者手机上的音乐播放器直接播放。
  2. 若需要阅读(观看)midi格式文件,可以使用binary viewer等二进制读取器打开。

通过研究,格式如下:

文件格式

主要包含两部分:首部块(Header chunk)音轨块(track chunk)
其格式分别为:

MThd <数据长度> <Header数据> //首部块
MTrk <数据长度> <Track模块> //音轨块

例如读取到的:

其中:

Header Chunk

/*
4D 54 68 64
00 00 00 06
00 01                 ff ff
00 06                 nn nn 指定轨道数:实际音轨数加上全局的音轨
00 60                 dd dd指定基本时间:一般为120 (00 78),即一个四分音符的tick数,tick是MIDI中的最小时间单位
*/char Midild[4];  //MIDI文件头标志,一般为MThd
long length;     //文件首部数据长度(除它本身和文件头标志专用的字节以外),通常他设置为6,即下面三个部分的字节长度
int format;        //表示MIDI文件存放的格式,包括:
// 0)当format为0时只有一个Track Chunk,
// 1)当format为1时有一个或多个Track Chunk
// 2)当format为2时只有一个或多个各自独立的Track Chunk
int TrackNum;
int division;   //指定了计数的方法

其中对于division的详细说明:

在上面我们注意到以下问题:上面的数据结构默认了:long类型是4Byte,int为2Byte,char为1Byte。

音轨块 track chunk

音轨块用来播放音乐文件的数据信息,每一个track chunk对应着一组MIDI码,它由**头部信息和若干个音轨事件(mtrk event)**构成。
头部为:

/* 头部信息
4D 54 72 6B      ASCII:Mtrk
00 00 00 0C                 表示后面有12位的数据
*/
/*
00 FF 58 04 04 02 18 08 00 FE 2E 00 */char TrackChunkId[4];      //Track Chunk标志MTrk
long TrackChunkMsgLength;  //该Track Chunk信息长度

Mtrk event是由事件计数值(dela-time)和event(MIDI码信息)组合而成的,即
< Mtrk > = < dela-time > < event >

使用WriteLengthToBuf()函数

目的是为了完成时间值二进制的转换

源代码(这个C代码不是我写的,但是值得学习)

  #include <stdlib.h>#include <stdio.h>#include <io.h>#include <string.h>#define C1 60 //C调1的键名值#define FOURPAINUM 64 //1/4音符计数#define MIDICLOCK 24 //每1/64音符的MIDICLOCK数#define JumpNullChar(x) / //跳过空字符{ /while(*x==' ' /||*x=='/t' /||*x=='/n' /||*x=='|') /x++; /};enum ERRORCODE{ //处理错误信息ChangeOK, //转换成功TextFileNotOpen, //文本文件不能打开MidiFileCanNotCreate, //指定的MIDI文件不能建立TextFileToBig, //文本文件太大MallocError, //内存分配错误InvalideChar, //在文本文件中出现了非法字符NotFoundTrack, //没有找到指定的磁道信息NotMIDITextFile, //文本文件不是MIDI文本文件};void SWAP(char *x,char *y) //两数据交换{ char i;i=*x;*x=*y;*y=i;}union LENGHT{ long length;char b[4];} ;struct MH { //MIDI文件头char MidiId[4]; //MIDI文件标志MThdlong length; //头部信息长度int format; //存放的格式int ntracks; //磁道数目int PerPaiNum; //每节计算器值};struct TH //音轨头{ char TrackId[4]; //磁道标志MTrklong length; //信息长度} ;class MIDI{public:char ErrorMsg[100]; //错误信息private:unsigned char *TextFileBuf,*TextFileOldBuf;unsigned char *MidiFileBuf,*MidiFileOldBuf;char OneVal; //某调时,1的健值char PaiNum; //第一小节节拍总数char OnePaiToneNum; //用几分音符作为一基本拍public://将符全MIDI书定格式的文本文件生成MIDI文件int ChangeTextToMidi(char *TextFileName,char *MidiFileName);char *GetErrorMsg() //获取错误信息{ return(ErrorMsg);}private:char GetCurPaiSpeed(int n); //取当前拍的按下强度void WriteSoundSize(char ntrack,unsigned int );void SetOnePaiToneNum(int n){ OnePaiToneNum=n; };void SetOneVal(char *m) ; //取m大调或小调时,1的实际键值char GetToneNum(char c, //取记名对应的键值char flag) ;void WriteMHToFile(long length, //建立MIDI文件头int format,int ntracks,int PerPaiNum,FILE *fp);void WriteTHToFile(long lenght,FILE *fp); //建立MIDI磁道头void WriteTrackMsgToFile(FILE *fp);//将磁道音乐信息定入文件中void WriteSpeed(int speed);void SetPaiNum(int n){ PaiNum=n;}long NewLong(long n); //新的long值int NewInt(int n) //新的int值{ return(n<<8|n>>8);}//将n改为可变长度,内入buf处void WriteLenghtToBuf(unsigned long n,char *buf);void ChangePrommgram(char channel, //设置音色char promgram);void NoteOn (char n, //演奏乐音char speed,unsigned long delaytime);void WriteNoteOn(char,char,char ,unsigned long) ;void WriteTextMsg(char *msg); //定一串文本信息void WriteTimeSignature(char n, //设置时间信息char d);void WriteTrackEndMsg(); //设置磁道结束信息};/**************************************************/* 作用:将符合MIDI文本文件的text文件转换成MIDI *//* 文件. *//* 入口参数:TextFileName 文本文件名 *//* MidiFileName MIDI文件名 *//* 出口参数:见 ERRORCODE 说明 *//*************************************************/int MIDI::ChangeTextToMidi(char *TextFileName,char *MidiFileName){ int tracks,ntrack,delaytime;int speed,IsFirst,nn,dd;unsigned char buf[80],*msgbuf,c;FILE *TextFp,*MidiFp;long FileSize;char SpeedVal;TextFp=fopen(TextFileName,"r");if (TextFp==NULL){sprintf(ErrorMsg,"文本文件[%s]不能打开。/n",TextFileName);return(TextFileNotOpen);}fseek(TextFp,0,SEEK_END); /*测试文件大小*/FileSize=ftell(TextFp);TextFileBuf=(char *)malloc(FileSize);/*为文件分配内存*/if (TextFileBuf==NULL){ sprintf(ErrorMsg,"文本文件[%s]太大,没有足够的内存处理。/n",TextFileName);fclose(TextFp);return(TextFileToBig);}memset(TextFileBuf,0,FileSize);MidiFileBuf=(char *) malloc(FileSize*4);if ( MidiFileBuf==NULL){ sprintf(ErrorMsg,"不能为MIDI文件分配内存。/n");fclose(TextFp);free(TextFileBuf);return(MallocError);}MidiFp=fopen(MidiFileName,"wb");if (MidiFp==NULL){ sprintf(ErrorMsg,"Midi文件[%s]不能建立。/n",MidiFileName);fclose(TextFp);free(MidiFileBuf);free(TextFileBuf);return(MidiFileCanNotCreate);}MidiFileOldBuf=MidiFileBuf;TextFileOldBuf=TextFileBuf;fseek(TextFp,0,SEEK_SET);fread(TextFileBuf,FileSize,1,TextFp);fclose(TextFp);JumpNullChar(TextFileBuf);c=strnicmp(TextFileBuf,"[MIDI]",6);if (c){sprintf(ErrorMsg,"文本文件[%s]不是MIDI文本文件。/n",MidiFileName);fcloseall();free(TextFileOldBuf);free(MidiFileOldBuf);return(NotMIDITextFile);}TextFileBuf+=6;JumpNullChar(TextFileBuf);sscanf(TextFileBuf,"%c,%d/%d,%d,%d", //取调号等信息c,nn,dd,speed,tracks);buf[0]=c;buf[1]=0; SetOneVal(buf); //设置该调1的键值if (nn<1 || nn> 7) nn=4;if (dd<2 || dd>16) dd=4;while(*TextFileBuf!='/n') TextfileBuf++;JumpNullChar(TextFileBuf);if (speed<60 || speed >200) speed=120;JumpNullChar(TextFileBuf);if (tracks<1 || tracks>16) tracks=1;JumpNullChar(TextFileBuf);ntrack=1;WriteMHToFile(6,1,tracks,speed,MidiFp);WriteTimeSignature(nn,dd); //设置时间记录格式SetPaiNum(nn);WriteSpeed(speed); //设置演奏速度while(ntrack<=tracks *TextFileBuf!=0){sprintf(buf,"[%d]",ntrack);TextFileBuf=strstr(TextFileBuf,buf);//查找该磁道起始位置if (TextFileBuf==NULL) //没有找到{ sprintf(ErrorMsg,"在文件[%s]中,第%d磁道音乐信息没找到。/n.",TextFileName,ntrack);free(MidiFileOldBuf);free(TextFileOldBuf);fcloseall();return(NotFoundTrack);}if (ntrack!=1) MidiFileBuf=MidiFileOldBuf;SpeedVal=0;TextFileBuf+=strlen(buf);IsFirst=1;while(*TextFileBuf!=0 *TextFileBuf!='['){ JumpNullChar(TextFileBuf);c=*(TextFileBuf++);if ( (c>='0' c<='7')|| (c>='a' c<='g')|| (c>='A' c<='G')){JumpNullChar(TextFileBuf);if (*TextFileBuf=='b' || *TextFileBuf=='#'){ c=GetToneNum(c,*TextFileBuf);/*取出实际的音符*/TextFileBuf++;JumpNullChar(TextFileBuf);}else c=GetToneNum(c,' ');switch(*(TextFileBuf++)){ case '-': //延长一拍delaytime=2*FOURPAINUM;JumpNullChar(TextFileBuf);while(*TextFileBuf=='-'){ TextFileBuf++;delaytime+=FOURPAINUM;JumpNullChar(TextFileBuf);}break;case '_': //8分音符delaytime=FOURPAINUM/2;JumpNullChar(TextFileBuf);if(*TextFileBuf=='.'){TextFileBuf++;delaytime=delaytime*3/2;}break;case '=': //16分音符delaytime=FOURPAINUM/4;JumpNullChar(TextFileBuf);if(*TextFileBuf=='.'){delaytime=delaytime*3/2;TextFileBuf++;}break;case '.': //附点音符delaytime=FOURPAINUM*3/2;break;case ':': //32分音符delaytime=FOURPAINUM/16;JumpNullChar(TextFileBuf);if(*TextFileBuf=='.'){delaytime=delaytime*3/2;TextFileBuf++;}break;case ';': //64分音符delaytime=FOURPAINUM/32;if(*TextFileBuf=='.'){ delaytime=delaytime*3/2;TextFileBuf++;}break;default:delaytime=FOURPAINUM;TextFileBuf--;break;}if (IsFirst){WriteNoteOn(ntrack,c,GetCurPaiSpeed(SpeedVal/(FOURPAINUM*4/dd)+1),delaytime);IsFirst=0;}elseNoteOn(c,GetCurPaiSpeed(SpeedVal/(FOURPAINUM*4/dd)+1),delaytime);SpeedVal=(SpeedVal+delaytime) //下一音符所处的节拍%(PaiNum*FOURPAINUM*4/dd);}else{switch(c){ case 'S':case 's':case 'p':case 'P': /*设置音色*/sscanf(TextFileBuf,"%d",IsFirst);while(*TextFileBuf>='0' *TextFileBuf<='9')TextFileBuf++;if (c=='P'||c=='p') //若为P,表示改变音色ChangePrommgram(ntrack,(char)IsFirst);else //否则,表示设置音量大小WriteSoundSize(ntrack,(unsigned int)IsFirst);IsFirst=1;break;case '{': /*写歌词*/msgbuf=buf;while(*TextFileBuf!='}'*TextFileBuf!='/n'*TextFileBuf!=0*TextFileBuf!='[')*(msgbuf++)=*(TextFileBuf++);*msgbuf=0;IsFirst=1;WriteTextMsg(buf);if (*TextFileBuf=='}') TextFileBuf++;break;case '//': //降八度OneVal-=12;break;case '/': //升八度OneVal+=12;break;case '[':case 0:TextFileBuf--;break;default:sprintf(ErrorMsg,"文本文件[%s]出现非法字符(%c)。",TextFileName,c);free(MidiFileOldBuf);free(TextFileOldBuf);fcloseall();return(InvalideChar);}}}WriteTrackEndMsg(); //设置磁道结束信息WriteTrackMsgToFile(MidiFp); //将磁道音乐信息定入文件中ntrack++;}free(MidiFileOldBuf);free(TextFileOldBuf);fclose(MidiFp);sprintf(ErrorMsg,"MIDI文件[%s]转换成功。",MidiFileName);return(ChangeOK);}/*****************************************************//*作用:将长整型数据变成可变长度,存入buf处 *//*入口参数:n 数据 buf 结果保存入 *//****************************************************/void MIDI::WriteLenghtToBuf(unsigned long n,char *buf){ unsigned char b[4]={0};int i;b[3]=(unsigned char)(n0x7f);i=2;while(n>>7){ n>>=7;b[i--]=(char)( (n0x7f)|0x80);}for (i=0;i<4;i++)if (b[i]) *(buf++)=b[i];*buf=0;}long MIDI::NewLong(long n) //将长整型数据改成高位在前{ union LENGHT l={0};char i;l.length=n;SWAP(l.b[3],l.b[0]);SWAP(l.b[2],l.b[1]);return(l.length);}//开始演奏音乐void MIDI::WriteNoteOn(char channel, //通道号char note, //音符值char speed, //按键速度unsigned long delaytime) //延时数{ unsigned char buf[5];int i;channel--;*(MidiFileBuf++)=0;*(MidiFileBuf++)=0x90|channel0x7f;//Write Channel*(MidiFileBuf++)=note;*(MidiFileBuf++)=speed;WriteLenghtToBuf(delaytime*MIDICLOCK,buf);i=0;while(buf[i]>=0x80) //Write Delay Time*(MidiFileBuf++)=buf[i++];*(MidiFileBuf++)=buf[i];*(MidiFileBuf++)=note;*(MidiFileBuf++)=0;}void MIDI::NoteOn(char note,char speed,unsigned long delaytime) //发音{ unsigned char buf[5];int i;*(MidiFileBuf++)=0;*(MidiFileBuf++)=note;*(MidiFileBuf++)=speed;WriteLenghtToBuf(delaytime*MIDICLOCK,buf);i=0;while(buf[i]>0x80)*(MidiFileBuf++)=buf[i++];*(MidiFileBuf++)=buf[i];*(MidiFileBuf++)=note;*(MidiFileBuf++)=0;}void MIDI::ChangePrommgram(char channel,char n) //改变音色{ *(MidiFileBuf++)=0;*(MidiFileBuf++)=0xc0|(channel-1)0x7f;*(MidiFileBuf++)=n;}void MIDI::WriteTextMsg(char *msg) //向内存写入一文本信息{ char bufmsg[100]={0xff,5,0,0,0};int len;*(MidiFileBuf++)=0;bufmsg[2]=(char)strlen(msg);strcpy(bufmsg[3],msg);strcpy(MidiFileBuf,bufmsg);MidiFileBuf+=strlen(bufmsg)+3;}void MIDI::WriteTrackEndMsg() //磁道结束信息{ *(MidiFileBuf++)=0;*(MidiFileBuf++)=0xff;*(MidiFileBuf++)=0x2f;*(MidiFileBuf++)=0;}char MIDI::GetToneNum(char n,char flag)/*入口参数: n 音高flag 升降记号返回值: 该乐音的实际标号值*/{ static char val[7]={9 ,11,0,2,4,5,7};static char one[7]={0,2,4,5,7,9,11};int i;i=OneVal;if (n<='7' n>='1') i=i+one[n-'1'];elseif (n>='a' n<='g')i=i+val[n-'a']-12; //低音,降12个半音elseif (n>='A' n<='G') //高音,升12个半音i=i+val[n-'A']+12;else //否则,识为休止符i=0;if (flag=='b') i--;else if (flag=='#') i++;return(i);}if (IsFirst){WriteNoteOn(ntrack,c,GetCurPaiSpeed(SpeedVal/(FOURPAINUM*4/dd)+1),delaytime);IsFirst=0;}elseNoteOn(c,GetCurPaiSpeed(SpeedVal/(FOURPAINUM*4/dd)+1),delaytime);SpeedVal=(SpeedVal+delaytime) //下一音符所处的节拍%(PaiNum*FOURPAINUM*4/dd);}else{switch(c){ case 'S':case 's':case 'p':case 'P': /*设置音色*/sscanf(TextFileBuf,"%d",IsFirst);while(*TextFileBuf>='0' *TextFileBuf<='9')TextFileBuf++;if (c=='P'||c=='p') //若为P,表示改变音色ChangePrommgram(ntrack,(char)IsFirst);else //否则,表示设置音量大小WriteSoundSize(ntrack,(unsigned int)IsFirst);IsFirst=1;break;case '{': /*写歌词*/msgbuf=buf;while(*TextFileBuf!='}'*TextFileBuf!='/n'*TextFileBuf!=0*TextFileBuf!='[')*(msgbuf++)=*(TextFileBuf++);*msgbuf=0;IsFirst=1;WriteTextMsg(buf);if (*TextFileBuf=='}') TextFileBuf++;break;case '//': //降八度OneVal-=12;break;case '/': //升八度OneVal+=12;break;case '[':case 0:TextFileBuf--;break;default:sprintf(ErrorMsg,"文本文件[%s]出现非法字符(%c)。",TextFileName,c);free(MidiFileOldBuf);free(TextFileOldBuf);fcloseall();return(InvalideChar);}}}WriteTrackEndMsg(); //设置磁道结束信息WriteTrackMsgToFile(MidiFp); //将磁道音乐信息定入文件中ntrack++;}free(MidiFileOldBuf);free(TextFileOldBuf);fclose(MidiFp);sprintf(ErrorMsg,"MIDI文件[%s]转换成功。",MidiFileName);return(ChangeOK);}/*****************************************************//*作用:将长整型数据变成可变长度,存入buf处 *//*入口参数:n 数据 buf 结果保存入 *//****************************************************/void MIDI::WriteLenghtToBuf(unsigned long n,char *buf){ unsigned char b[4]={0};int i;b[3]=(unsigned char)(n0x7f);i=2;while(n>>7){ n>>=7;b[i--]=(char)( (n0x7f)|0x80);}for (i=0;i<4;i++)if (b[i]) *(buf++)=b[i];*buf=0;}long MIDI::NewLong(long n) //将长整型数据改成高位在前{ union LENGHT l={0};char i;l.length=n;SWAP(l.b[3],l.b[0]);SWAP(l.b[2],l.b[1]);return(l.length);}//开始演奏音乐void MIDI::WriteNoteOn(char channel, //通道号char note, //音符值char speed, //按键速度unsigned long delaytime) //延时数{ unsigned char buf[5];int i;channel--;*(MidiFileBuf++)=0;*(MidiFileBuf++)=0x90|channel0x7f;//Write Channel*(MidiFileBuf++)=note;*(MidiFileBuf++)=speed;WriteLenghtToBuf(delaytime*MIDICLOCK,buf);i=0;while(buf[i]>=0x80) //Write Delay Time*(MidiFileBuf++)=buf[i++];*(MidiFileBuf++)=buf[i];*(MidiFileBuf++)=note;*(MidiFileBuf++)=0;}void MIDI::NoteOn(char note,char speed,unsigned long delaytime) //发音{ unsigned char buf[5];int i;*(MidiFileBuf++)=0;*(MidiFileBuf++)=note;*(MidiFileBuf++)=speed;WriteLenghtToBuf(delaytime*MIDICLOCK,buf);i=0;while(buf[i]>0x80)*(MidiFileBuf++)=buf[i++];*(MidiFileBuf++)=buf[i];*(MidiFileBuf++)=note;*(MidiFileBuf++)=0;}void MIDI::ChangePrommgram(char channel,char n) //改变音色{ *(MidiFileBuf++)=0;*(MidiFileBuf++)=0xc0|(channel-1)0x7f;*(MidiFileBuf++)=n;}void MIDI::WriteTextMsg(char *msg) //向内存写入一文本信息{ char bufmsg[100]={0xff,5,0,0,0};int len;*(MidiFileBuf++)=0;bufmsg[2]=(char)strlen(msg);strcpy(bufmsg[3],msg);strcpy(MidiFileBuf,bufmsg);MidiFileBuf+=strlen(bufmsg)+3;}void MIDI::WriteTrackEndMsg() //磁道结束信息{ *(MidiFileBuf++)=0;*(MidiFileBuf++)=0xff;*(MidiFileBuf++)=0x2f;*(MidiFileBuf++)=0;}char MIDI::GetToneNum(char n,char flag)/*入口参数: n 音高flag 升降记号返回值: 该乐音的实际标号值*/{ static char val[7]={9 ,11,0,2,4,5,7};static char one[7]={0,2,4,5,7,9,11};int i;i=OneVal;if (n<='7' n>='1') i=i+one[n-'1'];elseif (n>='a' n<='g')i=i+val[n-'a']-12; //低音,降12个半音elseif (n>='A' n<='G') //高音,升12个半音i=i+val[n-'A']+12;else //否则,识为休止符i=0;if (flag=='b') i--;else if (flag=='#') i++;return(i);}

转载于:https://www.cnblogs.com/liangzid/p/10910026.html

2018-11-14 MIDI音乐格式笔记学习相关推荐

  1. 2018.11.14成立我的博客

    2018.11.14成立我的博客 转载于:https://www.cnblogs.com/zengxx/p/9957509.html

  2. 训练日志 2018.11.14

    这周的计划是 Ford.SPFA 看完了,2-SAT 看了一半,并查集没按计划完成,但把最小生成树看了,还算可以... 下周 2-SAT.次短路径.并查集... 今晚打比赛..菜的一批...做了 4. ...

  3. [2018/11/14]关于学习的思考

    今天早上的操作系统课, 头盔哥(操作老师, 因为有车但是还是经常的骑电瓶车来给我们上课, 而且头盔看起来极其的结实, 故我个人称他为头盔哥)又开始给我们灌鸡汤了, 不过今天的鸡汤味道好极了. 让我对大 ...

  4. [2018/11/14] 学习的思考

    早上头盔哥的鸡汤让我有了思考, 在学校里上个课程, 有些你觉得没有用的, 就没有必要在意知识的本身, 而是将这门课当作锻炼思维, 锻炼学习能力的课程. 转载于:https://www.cnblogs. ...

  5. [2018/11/14]思考

    我因该下定决心给每一门专业课都按照自己的需要程度排一下序列了, 重要的就必须多花点时间, 不重要的想办法通过考试, 大致的泛泛了解一下就好了, 平均主义只会产生庸才. 对于一门课的重视程度, 最直接的 ...

  6. Flask框架后端开发常见错误处理(2018/11/14)

    Flask常见错误与解决方法(By Wdx) 1.ValueError: urls must start with a leading slash 这个错误原因可能发生在所有路由相关地方,少加了一个' ...

  7. 2018.11.14 Chopin’s

    The lineaments of Chopin's肖邦 short, dramatic life are familiar to most classical-music enthusiasts. ...

  8. QIIME 2用户文档. 4人体各部位微生物组分析实战Moving Pictures(2018.11)

    文章目录 前情提要 QIIME 2用户文档. 4人体各部位微生物组 启动QIIME2运行环境 样本元数据 下载和导入数据 拆分样品 序列质控和生成特征表 方法1. DADA2 方法2. Deblur ...

  9. 步进电机 MIDI 音乐 MATLAB+STM32

    在网上看到了很多用步进电机演奏音乐的小视频,感觉很有意思.正好也是2017年的最后几天了,那就鼓捣一下. 整个制作周期耗费将近一天,分为两块:1.MIDI音乐格式的编码及提取:2.Stm32控制步进电 ...

最新文章

  1. 临床医生如何解读Meta分析论文?
  2. [Python图像处理] 九.形态学之图像开运算、闭运算、梯度运算
  3. 区块链将如何影响你的生活?链圈大佬、美图董事长蔡文胜这样说
  4. 顺序程序设计02 - 零基础入门学习C语言08
  5. 深度学习项目:歌词的自动生成
  6. springboot+自定义注解实现灵活的切面配置
  7. 使用samba完成Linux服务器与Windows之间的映射
  8. 抽象代数之拉格朗日定理的证明
  9. 大规模分布式爬虫系统中Kafka和rabbitMQ消息中间件的技术实践分享
  10. 快速彻底删除页眉或页脚横线
  11. python画球面投影_球面投影图片 python opencv实现[附代码]
  12. vim中实现大小写转换
  13. linux系统处理excel,Apache POI处理Excel文档
  14. C++中string类函数常用函数大全
  15. UVM response_handler和get_response机制
  16. springcloud.3.服务注册与发现
  17. 计算机视觉检测 白皓月,基于视线跟踪的操作界面的人机交互方法研究
  18. 计算机管理删除卷灰色,win10磁盘上的“删除卷”按钮是灰色无法删除怎么解决...
  19. C语言笔记(1.3版本,目前3.5w字)----未完待续
  20. String的intern()

热门文章

  1. 这个“火星人”做到天猫行业第一,现在还要冲刺上市
  2. flexible wincc 弹窗_wincc flexible弹出小窗口怎么做?
  3. Surface Pro双指三指触摸手势失效解决方法
  4. navicat for mysql 注册码
  5. java调用个人微信API接口收发朋友圈,删除评论朋友圈
  6. Android开发技术路线
  7. Shell - 查看目录文件(夹)大小并清理磁盘空间
  8. Protobuf 学习(二)编译proto文件并测试
  9. 朴素贝叶斯Naive Bayesian分类器 (NBC)
  10. 图片抠图怎么抠?这几种抠图方法简单又轻松