C语言解析MIDI文件

qq_53249526 2024-08-20 16:15:08

 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;
}

 

...全文
215 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

70,027

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧