70,027
社区成员




C语言做MIDI解析,想把多轨的MIDI文件改成按时间顺序交叉输出,有没有大佬教一下啊
main.c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "midi.h"
int main(int argc, char** argv) {
int i;
uint32_t s;
uint8_t* data;
struct SFM_header header;
struct SFM_chunk chunk;
char buff[100];
if (argc < 2) {
fprintf(stderr, "Usage: %s <midi_file>\n", argv[0]);
return 1;
}
FILE* in = fopen(argv[1], "rb");
FILE* out = fopen("midi解析.txt", "w");
printf("\nmidi解析.txt文件已生成\n\n");
if (in == NULL) {
fprintf(stderr, "Could not open file: %s\r\n", argv[1]);
return 1;
}
if (out == NULL) {
fprintf(stderr, "Could not create output file.\r\n");
fclose(in);
return 1;
}
if (SFM_ReadHeader(in, &header) == -1) {
fprintf(stderr, "Bad SFM header.\r\n");
fclose(in);
fclose(out);
exit(1);
}
fprintf(out, "---- SMF header ---\r\n");
fprintf(out, "%c%c%c%c\r\n",
header.str[0],
header.str[1],
header.str[2],
header.str[3]);
fprintf(out, "Length:\t\t%u\r\n", header.length);
fprintf(out, "Format:\t\t%u\r\n", header.format);
fprintf(out, "Tracks:\t\t%u\r\n", header.n);
fprintf(out, "Division:\t%u\r\n", header.division);
for (i = 0; i < header.n; i++) {
data = SFM_ReadChunk(in, &chunk);
if (data == NULL) {
fprintf(stderr, "SFM chunk no %u is bad.\r\n", i);
fclose(in);
fclose(out);
exit(1);
}
fprintf(out, "---- SMF chunk (%u / %u) ---\r\n", i + 1, header.n);
fprintf(out, "%c%c%c%c\r\n",
chunk.str[0],
chunk.str[1],
chunk.str[2],
chunk.str[3]);
fprintf(out, "Length:\t\t%u\r\n", chunk.length);
if (SFM_ParseChunk(out, &chunk, data) == -1) {
fprintf(out, "Invalid midi chunk detected.\r\n");
fclose(in);
fclose(out);
exit(1);
}
free(data);
}
s = fread((void*)buff, sizeof(buff), sizeof(buff[0]), in);
if (s == 0 && feof(in)) {
fprintf(out, "EOF\r\n");
}
else {
fprintf(stderr, "Invalid filesize\r\n");
fprintf(stderr, "Extra %u bytes at end of file\r\n", s);
fclose(in);
fclose(out);
exit(1);
}
fclose(in);
fclose(out); // Close the output file
return 0;
}
midi.h
#pragma once
#ifndef SFM_H_
#define SFM_H_
#include <stdint.h>
struct SFM_header
{
char str[4];
uint32_t length;
uint16_t format;
uint16_t n;
uint16_t division;
};
struct SFM_chunk
{
char str[4];
uint32_t length;
};
int SFM_ReadHeader(FILE* file, struct SFM_header* header);
uint8_t* SFM_ReadChunk(FILE* in, struct SFM_chunk* chunk);
int SFM_ParseChunk(FILE* out, struct SFM_chunk* chunk, uint8_t* data);
#endif
midi.c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include "midi.h"
static void ReverseInt16(uint16_t* d); //反转大小端
static void ReverseInt32(uint32_t* d); //反转大小端
static uint32_t VData(uint8_t** p, uint32_t* bytesleft); //解析可变长度数据字段
static uint8_t Data(uint8_t** p, uint32_t* bytesleft); //解析单字节数据段
static uint8_t Peek(uint8_t* p, uint32_t* bytesleft); //查看不增加指针的单字节数据字段
static uint8_t SkipData(uint8_t** p, uint32_t* bytesleft, uint32_t skip); //跳过多个字段
static char* Terminate(char* dst, char* src, uint32_t len); //终止指定长度的字符串
static float mpb = 500000, tpb, tick;
//读取并验证标准 MIDI 文件的标头
// 1失败,0正确
int SFM_ReadHeader(FILE* in, struct SFM_header* header)
{
fread((void*)&header->str, sizeof(header->str), 1, in);
fread((void*)&header->length, sizeof(header->length), 1, in);
fread((void*)&header->format, sizeof(header->format), 1, in);
fread((void*)&header->n, sizeof(header->n), 1, in);
fread((void*)&header->division, sizeof(header->division), 1, in);
ReverseInt32(&header->length);
ReverseInt16(&header->format);
ReverseInt16(&header->n);
ReverseInt16(&header->division);
tpb = header->division;
return 0;
}
//读取并验证标准 MIDI 文件的轨道块
//失败时返回 NULL,成功时返回指向块数据的指针
uint8_t* SFM_ReadChunk(FILE* in, struct SFM_chunk* chunk)
{
uint8_t* data = NULL;
fread((void*)&chunk->str, sizeof(chunk->str), 1, in);
fread((void*)&chunk->length, sizeof(chunk->length), 1, in);
ReverseInt32(&chunk->length);
data = (uint8_t*)malloc(chunk->length);
if (data != NULL)
{
fread((void*)data, chunk->length, 1, in);
}
return data;
}
int SFM_ParseChunk(FILE* out, struct SFM_chunk* chunk, uint8_t* data)
{
float time = 0;
uint32_t dt, length, left;
uint8_t event, type, p1, p2;
char str[100];
left = chunk->length;
while (left)
{
/* 计算时间偏移量 */
if (Peek(data, &left))
{
p1 = 0;
p2 = 0;
p1 = p2;
}
dt = VData(&data, &left);
time += dt * tick / 1000000;
if (!(Peek(data, &left) & 0x80))
{
}
else
{
event = Data(&data, &left);
}
fprintf(out, "%.1f s, ", time);
switch (event)
{
case 0xF0:
case 0xF7:
length = 0;
while (Data(&data, &left) != 0xF7)
{
++length;
}
fprintf(out, "SYEX 0x%02x[%u]\r\n", event, length);
break;
case 0xFF:
type = Data(&data, &left);
length = VData(&data, &left);
fprintf(out, "META 0x%02x[%u] \r\n", type, length);
if (type == 0x51 && length == 3)
{
mpb = 0;
mpb = (Data(&data, &left) << 16) | (Data(&data, &left) << 8) | (Data(&data, &left) << 0);
tick = mpb / tpb;
}
else
{
SkipData(&data, &left, length);
}
break;
default:
fprintf(out, "MIDI 0x%02x[], : ", event);
switch (event & 0xF0)
{
case 0xF0:
fprintf(out, "Unhandled midi event 0x%02x.\r\n", event);
return -1;
break;
case 0x90:
p1 = Data(&data, &left);
p2 = Data(&data, &left);
fprintf(out, "Note On %u, %u\r\n", p1, p2);
break;
case 0xA0:
p1 = Data(&data, &left);
p2 = Data(&data, &left);
fprintf(out, "p1 pressure %u, %u\r\n", p1, p2);
break;
case 0xB0:
p1 = Data(&data, &left);
p2 = Data(&data, &left);
fprintf(out, "Control change %u, %u\r\n", p1, p2);
break;
case 0xC0:
p1 = Data(&data, &left);
fprintf(out, "Program change %u\r\n", p1);
break;
case 0xD0:
p1 = Data(&data, &left);
fprintf(out, "Channel pressure %u\r\n", p1);
break;
case 0xE0:
p1 = Data(&data, &left);
p2 = Data(&data, &left);
fprintf(out, "Pitch wheel change %u, %u\r\n", p1, p2);
break;
}
break;
}
}
if (!(event == 0xFF && type == 0x2F))
{
fprintf(out, "Notice: Last message was not META 0x2F (End of track)\r\n");
}
return 0;
}
static void ReverseInt16(uint16_t* d)
{
uint8_t help;
help = *(((uint8_t*)d) + 0);
*(((uint8_t*)d) + 0) = *(((uint8_t*)d) + 1);
*(((uint8_t*)d) + 1) = help;
}
static void ReverseInt32(uint32_t* d)
{
uint8_t help1, help2;
help1 = *(((uint8_t*)d) + 0);
help2 = *(((uint8_t*)d) + 1);
*(((uint8_t*)d) + 0) = *(((uint8_t*)d) + 3);
*(((uint8_t*)d) + 1) = *(((uint8_t*)d) + 2);
*(((uint8_t*)d) + 2) = help2;
*(((uint8_t*)d) + 3) = help1;
}
static uint32_t VData(uint8_t** p, uint32_t* bytesleft)
{
uint32_t data = 0;
int i;
if (bytesleft)
{
for (i = 0; i < 4; i++)
{
data = (data << 7) + (**p & 0x7F);
if (!(**p & 0x80))
{
++* p;
--* bytesleft;
break;
}
++* p;
--* bytesleft;
}
}
return data;
}
static uint8_t Data(uint8_t** p, uint32_t* bytesleft)
{
uint8_t data = **p;
if (bytesleft)
{
++* p;
--* bytesleft;
return data;
}
return 0;
}
static uint8_t Peek(uint8_t* p, uint32_t* bytesleft)
{
return *p;
}
static uint8_t SkipData(uint8_t** p, uint32_t* bytesleft, uint32_t skip)
{
while (skip && *bytesleft)
{
Data(p, bytesleft);
--skip;
}
return 0;
}
static char* Terminate(char* dst, char* src, uint32_t len)
{
uint32_t i;
char* result = dst;
/* 复制字符串 */
for (i = 0; i < len; i++)
{
*dst = *src;
++src;
++dst;
}
/* 追加终止字符 */
*dst = 0;
return result;
}