如何在树莓派上用C脚本实现MIDI文件循环播放列表?
用C实现树莓派MIDI曲目循环播放并自动重启
当然可以实现,既然你已经能用aplaymidi命令播放单个MIDI文件,完全不需要直接解析MIDI协议——直接在C程序里调用这个命令,配合目录遍历和进程等待就能实现循环播放+自动重启的需求。下面是具体实现方案:
核心思路
- 遍历指定目录,收集所有
.mid/.midi格式的文件 - 对每个MIDI文件,通过
fork()+exec()调用aplaymidi命令播放,等待播放完成后再进入下一首 - 一轮曲目全部播放完成后,调用系统重启命令
- 循环上述流程(直到重启执行)
完整代码实现
#include <stdio.h> #include <stdlib.h> #include <dirent.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #define MIDI_DIR "/home/user/Music" // 替换为你的MIDI文件目录 #define MIDI_PORT "14" // 你的MIDI端口号 // 播放单个MIDI文件,等待播放完成 int play_midi(const char *file_path) { pid_t pid = fork(); if (pid == -1) { perror("fork failed"); return -1; } else if (pid == 0) { // 子进程执行aplaymidi命令 execlp("aplaymidi", "aplaymidi", "-p", MIDI_PORT, file_path, NULL); // 如果execlp执行失败,打印错误并退出子进程 perror("execlp failed to run aplaymidi"); exit(EXIT_FAILURE); } else { // 父进程等待子进程结束,确保播放完成 int status; waitpid(pid, &status, 0); return WIFEXITED(status) ? WEXITSTATUS(status) : -1; } } // 遍历目录,收集所有MIDI文件的完整路径 int collect_midi_files(const char *dir, char ***files, size_t *count) { DIR *dir_ptr = opendir(dir); if (!dir_ptr) { perror("failed to open directory"); return -1; } struct dirent *entry; size_t capacity = 8; // 初始容量,自动扩容 *files = malloc(capacity * sizeof(char*)); *count = 0; while ((entry = readdir(dir_ptr)) != NULL) { // 跳过目录和非MIDI文件 if (entry->d_type != DT_REG) continue; const char *ext = strrchr(entry->d_name, '.'); if (ext && (strcmp(ext, ".mid") == 0 || strcmp(ext, ".midi") == 0)) { // 扩容检查 if (*count >= capacity) { capacity *= 2; char **temp = realloc(*files, capacity * sizeof(char*)); if (!temp) { perror("realloc failed"); closedir(dir_ptr); return -1; } *files = temp; } // 拼接完整文件路径 size_t path_len = strlen(dir) + strlen(entry->d_name) + 2; (*files)[*count] = malloc(path_len); snprintf((*files)[*count], path_len, "%s/%s", dir, entry->d_name); (*count)++; } } closedir(dir_ptr); return 0; } int main() { char **midi_files; size_t file_count; // 收集MIDI文件 if (collect_midi_files(MIDI_DIR, &midi_files, &file_count) != 0) { fprintf(stderr, "Failed to collect MIDI files\n"); return EXIT_FAILURE; } if (file_count == 0) { fprintf(stderr, "No MIDI files found in directory: %s\n", MIDI_DIR); free(midi_files); return EXIT_FAILURE; } // 循环播放所有曲目,完成后重启 while (1) { printf("Starting new playback cycle (%zu tracks)\n", file_count); for (size_t i = 0; i < file_count; i++) { printf("Now playing: %s\n", midi_files[i]); if (play_midi(midi_files[i]) != 0) { fprintf(stderr, "Warning: Failed to play %s, skipping...\n", midi_files[i]); } } printf("Playback cycle completed. Initiating reboot...\n"); // 执行重启命令(需要sudo权限) system("sudo reboot"); // 如果重启命令未执行成功,退出循环(避免无限尝试) break; } // 释放内存 for (size_t i = 0; i < file_count; i++) { free(midi_files[i]); } free(midi_files); return EXIT_SUCCESS; }
编译与运行说明
- 编译代码:在终端执行
gcc midi_player.c -o midi_player - 运行程序:由于重启需要管理员权限,执行:
sudo ./midi_player - 前置检查:
- 确保
aplaymidi已安装,若未安装执行sudo apt install alsa-utils - 替换代码中的
MIDI_DIR为你的实际MIDI文件存储目录 - 若不想每次输入sudo密码,可以修改sudoers配置(需谨慎):
- 执行
sudo visudo - 添加一行:
your_username ALL=(ALL) NOPASSWD: /path/to/midi_player(替换为你的用户名和程序路径)
- 执行
- 确保
内容的提问来源于stack exchange,提问作者Brandon McDonald
相关产品推荐
相关产品推荐

