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