C语言 读取 MIDI文件头部

在C语言中直接读取MIDI文件并不简单,因为MIDI文件是一种包含音乐事件(如音符的开始和结束、控制信号等)的二进制格式,而不是像文本文件那样容易解析。不过,你可以通过以下步骤来实现:

  1. 了解MIDI文件格式
    • MIDI文件有多种格式,最常见的是SMF (Standard MIDI File) 格式,也被称为Type 0、Type 1或Type 2。
    • 你需要了解MIDI文件的各个部分,包括文件头、轨道头和轨道事件等。
  2. 编写解析器
    • 编写一个C程序来读取MIDI文件的二进制内容。
    • 首先,你需要读取文件头以获取文件的整体信息(如文件类型、轨道数等)。
    • 然后,逐个读取轨道头,了解每个轨道的详细信息。
    • 接下来,解析轨道中的MIDI事件,这通常涉及解析时间戳和事件数据。
  3. 处理MIDI事件
    • 对于每个MIDI事件,你需要解析事件数据以了解它是什么类型的事件(如音符开始、音符结束、控制更改等)。
    • 根据事件类型,你可以执行相应的操作,如播放音符、更改音量或发送其他MIDI消息。
  4. 使用库
    • 考虑到MIDI文件的复杂性,你可能希望使用现有的库来帮助解析和处理MIDI文件。
    • 例如,你可以查找支持C语言的MIDI库,如libsmflibmidi(请注意,这些库可能不存在或已过时,因为MIDI文件处理在C语言中并不常见)。
  5. 集成到项目中
    • 一旦你能够解析和处理MIDI文件,你可以将其集成到你的C项目中。
    • 这可能涉及将MIDI事件转换为音频输出(如使用MIDI输出设备或软件合成器)或将MIDI数据转换为其他格式(如MIDI转乐谱)。
  6. 测试和调试
    • 编写测试用例来验证你的MIDI解析器是否能够正确处理各种MIDI文件。
    • 使用调试工具来查找和修复任何错误或问题。

请注意,直接处理MIDI文件需要深入了解MIDI规范和相关文件格式。如果你不熟悉这些概念,可能需要花费一些时间来学习它们。此外,由于MIDI文件的复杂性,编写一个健壮且可靠的MIDI解析器可能是一个相当复杂的任务。

如果你只是想在C语言项目中播放MIDI文件,而不是解析和处理MIDI文件的内部细节,那么一个更简单的方法是使用外部MIDI播放器或库来播放MIDI文件,并通过命令行或API接口与你的C程序进行交互。

 请先看百度百科:MIDI文件格式

编写 mid_head.c 读取 midi 文件头部:

#include <stdio.h>
#include <stdint.h>

typedef struct {
    char ctag[4]; // chunk_tag: MThd
    int32_t chunk_size;
    // 指定Midi的格式: 00 00单音轨; 00 01多音轨,且同步; 00 02多音轨,但不同步
    uint16_t geshi; 
    uint16_t tracks; // 轨道数:=实际音轨数字 +1个全局音轨
    // 指定基本时间格式类型: 类型1:定义一个四分音符的tick数; 
    //     类型2:定义每秒中SMTPE帧的数量及每个SMTPE帧的ticks
    uint16_t ticks; 
    char ttag[4]; // track_tag: MTrk
    uint8_t t_id;
    uint16_t track_size;
} MidiHeader;

uint16_t swapUint16(uint16_t shortValue){
    return ((shortValue & 0x00FF ) <<8) | ((shortValue & 0xFF00)>>8);
}

int32_t swapInt32(int32_t intValue){
    int32_t temp = 0;
    temp = ((intValue & 0x000000FF) <<24) +
             ((intValue & 0x0000FF00) <<8) +
             ((intValue & 0x00FF0000) >>8) +
             ((intValue & 0xFF000000) >>24);
    return temp;
}

int main(int argc, char *argv[])
{
    if (argc < 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return 1;
    }
 
    const char *f1 = argv[1]; // filename
    FILE *file = fopen(f1, "rb");
    if (!file) {
        perror("Error opening file");
        return -1;
    }
 
    MidiHeader hd;
    if (fread(&hd, sizeof(MidiHeader), 1, file) != 1) {
        fclose(file);
        perror("Error reading file head");
        return -1;
    }
 
    // 打印读取到的数据,验证读取成功
    printf("Chunk tag: %s\n", hd.ctag);
    printf("Chunk Size: %04d\n", swapInt32(hd.chunk_size));
    printf("geshi:%d, tracks:%d, ticks:%d\n",swapUint16(hd.geshi),swapUint16(hd.tracks),swapUint16(hd.ticks));
    printf("Track tag: %s\n", hd.ttag);
    printf("track id: %x, track Size: %d\n", hd.t_id, swapUint16(hd.track_size));
    
    if (fseek(file, swapUint16(hd.track_size)-2, 1) !=0) {
        fclose(file);
        perror("Error fseek file ");
        return -1;
    }
    char t1tag[5];
    if (fread(&t1tag, sizeof(char), 4, file) != 4) {
        fclose(file);
        perror("Error reading file head");
        return -1;
    }
    printf("track1 tag: %s\n", t1tag);
    uint32_t track1_size;
    if (fread(&track1_size, sizeof(uint32_t), 1, file) != 1) {
        fclose(file);
        perror("Error reading file head");
        return -1;
    }
    printf("track1 size: %d\n", swapInt32(track1_size));    
    fclose(file);
    return 0;
}

where gcc
D:\Strawberry\c\bin\gcc.exe

编译 gcc mid_head.c -o mid_head.exe

运行 mid_head  happy_birthday.mid

mid_head happy_birthday.mid
Chunk tag: MThd
Chunk Size: 0006
geshi:1, tracks:2, ticks:1024
Track tag: MTrk
Track id: 0, Track Size: 20
Track1 tag: MTrk
track1 size: 247

为了对单个几十MB的.mid 文件采样数据,读取.mid 文件头部 4080 bytes 

Unix 命令 head -c 4080 sample1.mid > temp1.mid

运行 strings temp1.mid

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中的ELF(Executable and Linkable Format)文件头部表是用来描述ELF文件的各个段的信息的数据结构。ELF文件是一种可执行文件和可链接文件的标准格式,用于在Linux和其他Unix系统上执行和链接程序。 ELF文件头部表位于ELF文件头部,用于描述ELF文件的各个段(段是一组相关的数据或代码的有序集合),包括代码段、数据段、BSS段等等。每个段头部表项都包含了段的一些重要信息,如起始地址、大小、访问权限等等。 段头部表的作用是让操作系统或程序加载器能够正确地加载和执行ELF文件的各个段。通过读取头部表,系统可以确定每个段应该被放置在内存的哪个位置,并且可以根据段的访问权限进行适当的内存保护。此外,段头部表还包含其他一些元数据,如字符串表的偏移量、符号表的偏移量等,这些信息可以帮助调试器和其他工具分析和查找ELF文件的内容。 段头部表是一个固定大小的数据结构,每个表项的大小是固定的,并且表项的数量也是固定的。ELF文件的第一个段头部表项被保留用于描述ELF文件本身,其他表项则用于描述ELF文件中的各个段。程序员可以使用c语言中的结构体来表示段头部表项,并通过读取ELF文件头部来获取段头部表的起始地址,从而遍历和分析整个段头部表。 总之,C语言中的ELF文件头部表是用来描述ELF文件的各个段的信息的数据结构,它能够帮助操作系统或程序加载器正确地加载和执行ELF文件,并提供了一些用于分析和查找ELF文件内容的元数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值