commit ee49c0794bd4e55a9126b9f1c9ada6c5ebe94044 Author: Jay Robson Date: Sun Aug 18 00:16:53 2024 +1000 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0347e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ + +/midi-parser + diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..43708e2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "midifile"] + path = midifile + url = ./midifile diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7bab46c --- /dev/null +++ b/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +cd midifile && cmake . && make && cd .. + +g++ src/main.cpp ./midifile/libmidifile.a -o midi-parser -g -O3 + diff --git a/midifile b/midifile new file mode 160000 index 0000000..f79e8ea --- /dev/null +++ b/midifile @@ -0,0 +1 @@ +Subproject commit f79e8ea395b858ceb6e0d8f353885ff80773dc76 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..148b4ab --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,177 @@ + +#include "../midifile/include/MidiFile.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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(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; +} + +