initial commit

This commit is contained in:
Jay Robson 2024-08-18 00:16:53 +10:00
commit ee49c0794b
5 changed files with 190 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/midi-parser

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "midifile"]
path = midifile
url = ./midifile

6
build.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
cd midifile && cmake . && make && cd ..
g++ src/main.cpp ./midifile/libmidifile.a -o midi-parser -g -O3

1
midifile Submodule

@ -0,0 +1 @@
Subproject commit f79e8ea395b858ceb6e0d8f353885ff80773dc76

177
src/main.cpp Normal file
View File

@ -0,0 +1,177 @@
#include "../midifile/include/MidiFile.h"
#include <cmath>
#include <csignal>
#include <cstring>
#include <fstream>
#include <iterator>
#include <ostream>
#include <thread>
#include <iostream>
#include <chrono>
#include <vector>
#include <fcntl.h>
#include <termios.h>
const double KEYS[] = {16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, 29.14, 30.87};
double get_key_freq(int key) {
int octave = key / std::size(KEYS);
return KEYS[key % std::size(KEYS)] * std::pow(2, octave);
}
volatile std::sig_atomic_t running = true;
void signal_handler(int id) {
std::cout << "Quitting\n";
running = false;
}
int main(int argc, char** argv) {
std::signal(SIGINT, signal_handler);
smf::MidiFile midifile;
midifile.read(argv[2]);
int key_num_max = 0;
for(int i = 0; i < midifile.getNumTracks(); i++) {
for(int j = 0; j < midifile[i].size(); j++) {
auto& event = midifile[i][j];
if(event.isNote()) {
key_num_max = std::max(key_num_max, event.getKeyNumber());
}
}
}
std::vector<int> key_channel_ids(key_num_max + 1);
for(int i = 0; i < key_channel_ids.size(); i++) {
key_channel_ids[i] = -1;
}
midifile.doTimeAnalysis();
midifile.joinTracks();
std::ofstream device {argv[1], std::ios::binary};
device << ":c,";
device.flush();
auto& track = midifile[0];
int current_pressed_keys = 0;
int max_pressed_keys = 0;
double time_end = 0;
for(int i = 0; i < track.size() && running; i++) {
smf::MidiEvent& event = track[i];
if(!event.isNote()) {
continue;
}
int key = event.getKeyNumber();
time_end = std::max(time_end, event.seconds);
if(event.isNoteOn()) {
if(key_channel_ids[key] == -1) {
key_channel_ids[key] = -2;
current_pressed_keys++;
if(current_pressed_keys > max_pressed_keys) {
max_pressed_keys = current_pressed_keys;
}
}
} else {
if(key_channel_ids[key] == -2) {
key_channel_ids[key] = -1;
current_pressed_keys--;
}
}
}
double amplitude = 1.0 / max_pressed_keys;
std::vector<int> channel_key_nums(max_pressed_keys, -1);
auto now = std::chrono::high_resolution_clock::now();
for(int i = 0; i < track.size() && running; i++) {
smf::MidiEvent& event = track[i];
if(!event.isNote()) {
continue;
}
int channel;
int key = event.getKeyNumber();
if(key < 0) {
std::cerr << "Warning: Bad Key (" << key << ")" << std::endl;
continue;
}
if(event.isNoteOn()) {
if(key_channel_ids[key] >= 0) {
continue;
}
for(int i = 0; i < channel_key_nums.size(); i++) {
if(channel_key_nums[i] < 0) {
channel_key_nums[i] = key;
key_channel_ids[key] = i;
channel = i;
goto end;
}
}
channel = channel_key_nums.size();
channel_key_nums.push_back(key);
key_channel_ids[key] = channel;
}
else {
channel = key_channel_ids[key];
if(channel == -1) {
continue;
}
key_channel_ids[key] = -1;
channel_key_nums[channel] = -1;
}
end:
double key_freq = get_key_freq(key);
std::this_thread::sleep_until(now + std::chrono::duration<double>(event.seconds));
device << ":s," << channel << "," << key_freq << ',';
if(event.isNoteOn()) {
device << std::max(amplitude, 1.0/16.0);
} else {
device << '0';
}
device << ',';
device.flush();
for(int i = 0; i < key_channel_ids.size(); i++) {
if(key_channel_ids[i] < 0) {
std::cout << ' ';
} else {
std::cout << '|';
}
}
std::cout << " + ";
for(int i = 0; i < channel_key_nums.size(); i++) {
if(channel_key_nums[i] < 0) {
std::cout << ' ';
} else {
std::cout << '.';
}
}
std::cout << " + " << event.seconds << "s / " << time_end << "s" << std::endl;
}
device << ":c,";
device.flush();
return 0;
}