now generates converted midi data more directly
This commit is contained in:
parent
3bf8d3b91b
commit
251ea220e3
|
@ -8,7 +8,9 @@ set(CMAKE_CXX_FLAGS "-g")
|
||||||
|
|
||||||
set(SRCS
|
set(SRCS
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/device.cpp
|
src/header.cpp
|
||||||
|
src/packet.cpp
|
||||||
|
src/scheduler.cpp
|
||||||
src/key.cpp
|
src/key.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
#include "header.hpp"
|
||||||
|
#include <ios>
|
||||||
|
|
||||||
|
const int COLS = 32;
|
||||||
|
|
||||||
|
void header::generate(std::ostream& dst, const std::vector<char>& src, const char* var_name) {
|
||||||
|
|
||||||
|
dst << "#include <avr/pgmspace.h>\n";
|
||||||
|
dst << "inline const PROGMEM unsigned char " << var_name << "[] = {\n";
|
||||||
|
|
||||||
|
for(int i = 0; i < src.size(); i += COLS) {
|
||||||
|
|
||||||
|
dst << '\t';
|
||||||
|
|
||||||
|
for(int j = i; j < i + COLS && j < src.size(); j++) {
|
||||||
|
dst << std::dec << (src[j] & 0xff) << ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
dst << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
dst << "};\n" << std::dec;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace header {
|
||||||
|
|
||||||
|
void generate(std::ostream& dst, const std::vector<char>& src, const char* var_name);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
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};
|
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};
|
||||||
|
|
||||||
int key::get_freq(int key) {
|
unsigned key::get_freq(unsigned key) {
|
||||||
int octave = key / std::size(KEYS);
|
unsigned octave = key / std::size(KEYS);
|
||||||
return (int)std::round(KEYS[key % std::size(KEYS)] * std::pow(2, octave));
|
return (unsigned)std::round(KEYS[key % std::size(KEYS)] * std::pow(2, octave) * ((1<<20) / 1e6));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
namespace key {
|
namespace key {
|
||||||
|
|
||||||
int get_freq(int key);
|
unsigned get_freq(unsigned key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
148
src/main.cpp
148
src/main.cpp
|
@ -3,158 +3,56 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "device.hpp"
|
#include "header.hpp"
|
||||||
#include "key.hpp"
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
smf::MidiFile midifile;
|
smf::MidiFile midifile;
|
||||||
midifile.read(argv[2]);
|
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.doTimeAnalysis();
|
||||||
midifile.joinTracks();
|
midifile.joinTracks();
|
||||||
|
|
||||||
Device device(argv[1]);
|
Scheduler scheduler(midifile.getTimeInSeconds(1));
|
||||||
|
|
||||||
auto& track = midifile[0];
|
auto& track = midifile[0];
|
||||||
int current_pressed_keys = 0;
|
unsigned ticks_last = 0;
|
||||||
int max_pressed_keys = 0;
|
unsigned notes_added = 0;
|
||||||
double time_end = 0;
|
|
||||||
|
|
||||||
for(int i = 0; i < track.size(); 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--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int tones_total = 0;
|
|
||||||
double amplitude = std::max(1.0 / max_pressed_keys, 1.0/16.0);
|
|
||||||
std::vector<int> channel_key_nums(max_pressed_keys, -1);
|
|
||||||
|
|
||||||
std::cout << "Seconds per tick: " << midifile.getTimeInSeconds(1) << std::endl;
|
std::cout << "Seconds per tick: " << midifile.getTimeInSeconds(1) << std::endl;
|
||||||
|
|
||||||
device.printf(":w,o,%f,%d,", amplitude, (int)(midifile.getTimeInSeconds(1) * 1000000));
|
|
||||||
|
|
||||||
for(int i = 0; i < track.size(); i++) {
|
for(int i = 0; i < track.size(); i++) {
|
||||||
smf::MidiEvent& event = track[i];
|
smf::MidiEvent& note = track[i];
|
||||||
|
|
||||||
if(!event.isNote()) {
|
if(!note.isNote()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int channel;
|
if(note.tick > ticks_last) {
|
||||||
int key = event.getKeyNumber();
|
scheduler.display(std::cout);
|
||||||
|
|
||||||
if(key < 0) {
|
|
||||||
std::cerr << "Warning: Bad Key (" << key << ")" << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event.isNoteOn()) {
|
smf::MidiEvent& note_end = *note.getLinkedEvent();
|
||||||
if(key_channel_ids[key] >= 0) {
|
if(!scheduler.add_note(note.getKeyNumber(), note.tick, note.isNoteOn())) {
|
||||||
continue;
|
// std::cout << "Warning (" << i << "): Failed\n";
|
||||||
}
|
|
||||||
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 {
|
notes_added++;
|
||||||
channel = key_channel_ids[key];
|
|
||||||
if(channel == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
key_channel_ids[key] = -1;
|
|
||||||
channel_key_nums[channel] = -1;
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
|
|
||||||
int key_freq = key::get_freq(key);
|
|
||||||
|
|
||||||
device.read_until();
|
|
||||||
if(event.isNoteOn()) {
|
|
||||||
device.printf("s,%d,%d,%d,", event.tick, channel, key_freq);
|
|
||||||
} else {
|
|
||||||
device.printf("c,%d,%d,", event.tick, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i < track.size() - 1 && event.seconds == track[i + 1].seconds) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
tones_total++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
device.read_until();
|
scheduler.display(std::cout);
|
||||||
device.printf(";,:p,");
|
|
||||||
|
|
||||||
std::cout << "Sent " << tones_total << " tones\n";
|
std::vector<char> bytes;
|
||||||
|
scheduler.finalise(bytes);
|
||||||
|
|
||||||
|
std::cout << "Added " << notes_added << " notes\n";
|
||||||
|
std::cout << "Generated " << bytes.size() << " bytes\n";
|
||||||
|
|
||||||
|
std::ofstream device(argv[1]);
|
||||||
|
header::generate(device, bytes, "MIDI");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
#include "packet.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
constexpr auto bm(auto n) {
|
||||||
|
return (1L << n) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output(std::vector<char>& dst, uint64_t v, int size) {
|
||||||
|
for(int i = size * 8 - 8; i >= 0; i -= 8) {
|
||||||
|
dst.push_back(v >> i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet::Clear::finalise(std::vector<char>& dst) const {
|
||||||
|
dst.push_back((Type::clear << 5) | (index & bm(5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet::ClearTS::finalise(std::vector<char>& dst) const {
|
||||||
|
uint64_t v = Type::clear_ts & bm(3);
|
||||||
|
v = (v << 5) | (index & bm(5));
|
||||||
|
v = (v << 20) | (ticks & bm(20));
|
||||||
|
v <<= 4;
|
||||||
|
output(dst, v, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet::Set::finalise(std::vector<char>& dst) const {
|
||||||
|
uint64_t v = Type::set & bm(3);
|
||||||
|
v = (v << 5) | (index & bm(5));
|
||||||
|
v = (v << 12) | (frequency & bm(12));
|
||||||
|
v <<= 4;
|
||||||
|
output(dst, v, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet::SetTS::finalise(std::vector<char>& dst) const {
|
||||||
|
uint64_t v = Type::set_ts & bm(3);
|
||||||
|
v = (v << 5) | (index & bm(5));
|
||||||
|
v = (v << 20) | (ticks & bm(20));
|
||||||
|
v = (v << 12) | (frequency & bm(12));
|
||||||
|
output(dst, v, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet::Config::finalise(std::vector<char>& dst) const {
|
||||||
|
uint64_t v = Type::config & bm(3);
|
||||||
|
uint8_t* a_data = (uint8_t*)&litude;
|
||||||
|
v = (v << 29) | (us_per_tick & bm(29));
|
||||||
|
output(dst, v, 4);
|
||||||
|
|
||||||
|
for(int i = 0; i < 4; i++) {
|
||||||
|
dst.push_back(a_data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet::Stop::finalise(std::vector<char>& dst) const {
|
||||||
|
dst.push_back(Type::stop << 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void packet::Generic::finalise(std::vector<char>& dst) const {
|
||||||
|
switch(type) {
|
||||||
|
case Type::clear:
|
||||||
|
clear.finalise(dst);
|
||||||
|
break;
|
||||||
|
case Type::clear_ts:
|
||||||
|
clear_ts.finalise(dst);
|
||||||
|
break;
|
||||||
|
case Type::set:
|
||||||
|
set.finalise(dst);
|
||||||
|
break;
|
||||||
|
case Type::set_ts:
|
||||||
|
set_ts.finalise(dst);
|
||||||
|
break;
|
||||||
|
case Type::config:
|
||||||
|
config.finalise(dst);
|
||||||
|
break;
|
||||||
|
case Type::stop:
|
||||||
|
stop.finalise(dst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace packet {
|
||||||
|
|
||||||
|
enum Type : unsigned {
|
||||||
|
stop,
|
||||||
|
config,
|
||||||
|
set,
|
||||||
|
set_ts,
|
||||||
|
clear,
|
||||||
|
clear_ts,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Stop {
|
||||||
|
void finalise(std::vector<char>& dst) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Set {
|
||||||
|
unsigned index;
|
||||||
|
unsigned frequency;
|
||||||
|
|
||||||
|
void finalise(std::vector<char>& dst) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SetTS {
|
||||||
|
unsigned index;
|
||||||
|
unsigned ticks;
|
||||||
|
unsigned frequency;
|
||||||
|
|
||||||
|
void finalise(std::vector<char>& dst) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Clear {
|
||||||
|
unsigned index;
|
||||||
|
|
||||||
|
void finalise(std::vector<char>& dst) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ClearTS {
|
||||||
|
unsigned index;
|
||||||
|
unsigned ticks;
|
||||||
|
|
||||||
|
void finalise(std::vector<char>& dst) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Config {
|
||||||
|
unsigned us_per_tick;
|
||||||
|
float amplitude;
|
||||||
|
|
||||||
|
void finalise(std::vector<char>& dst) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::size_t get_size(Type type) {
|
||||||
|
switch(type) {
|
||||||
|
case Type::clear:
|
||||||
|
return 1;
|
||||||
|
case Type::clear_ts:
|
||||||
|
return 4;
|
||||||
|
case Type::set:
|
||||||
|
return 3;
|
||||||
|
case Type::set_ts:
|
||||||
|
return 5;
|
||||||
|
case Type::stop:
|
||||||
|
return 1;
|
||||||
|
case Type::config:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Generic {
|
||||||
|
Type type;
|
||||||
|
union {
|
||||||
|
Stop stop;
|
||||||
|
Set set;
|
||||||
|
SetTS set_ts;
|
||||||
|
Clear clear;
|
||||||
|
ClearTS clear_ts;
|
||||||
|
Config config;
|
||||||
|
};
|
||||||
|
void finalise(std::vector<char>& dst) const;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "key.hpp"
|
||||||
|
#include "packet.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
Scheduler::Scheduler(double s_per_tick) : us_per_tick(s_per_tick * 1e6) {
|
||||||
|
std::cout << "us_per_tick = " << us_per_tick << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Scheduler::add_note(unsigned id, unsigned ticks, bool state) {
|
||||||
|
|
||||||
|
if(id >= keys.size()) {
|
||||||
|
keys.resize(id + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!state) {
|
||||||
|
unsigned channel = keys[id].channel;
|
||||||
|
if(!keys[id].active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
keys[id].active = false;
|
||||||
|
channels[channel].active = false;
|
||||||
|
channels_in_use--;
|
||||||
|
|
||||||
|
if(ticks_at >= ticks) {
|
||||||
|
packets.push_back({.type=packet::Type::clear, .clear={.index=channel}});
|
||||||
|
} else {
|
||||||
|
packets.push_back({.type=packet::Type::clear_ts, .clear_ts={.index=channel, .ticks=ticks}});
|
||||||
|
ticks_at = ticks;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned channel_id = -1;
|
||||||
|
|
||||||
|
if(keys[id].active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned i = 0;; i++) {
|
||||||
|
if(i >= std::size(channels)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!channels[i].active) {
|
||||||
|
channel_id = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ticks_at >= ticks) {
|
||||||
|
packets.push_back({.type=packet::Type::set, .set={.index=channel_id, .frequency=key::get_freq(id)}});
|
||||||
|
} else {
|
||||||
|
packets.push_back({.type=packet::Type::set_ts, .set_ts={.index=channel_id, .ticks=ticks, .frequency=key::get_freq(id)}});
|
||||||
|
ticks_at = ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
channels[channel_id] = {.key=id, .active=true};
|
||||||
|
keys[id] = {.channel=channel_id, .active=true};
|
||||||
|
channels_in_use++;
|
||||||
|
|
||||||
|
if(channels_in_use > channels_in_use_max) {
|
||||||
|
channels_in_use_max = channels_in_use;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::finalise(std::vector<char>& dst) const {
|
||||||
|
|
||||||
|
float amplitude = std::max(1.f / channels_in_use_max, 1.f/8.f);
|
||||||
|
packet::Generic packet_start {.type=packet::Type::config, .config={.us_per_tick=us_per_tick, .amplitude=amplitude}};
|
||||||
|
packet::Generic packet_end {.type=packet::Type::stop, .stop={}};
|
||||||
|
|
||||||
|
packet_start.finalise(dst);
|
||||||
|
|
||||||
|
for(const packet::Generic& packet : packets) {
|
||||||
|
packet.finalise(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_end.finalise(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::display(std::ostream& os) const {
|
||||||
|
|
||||||
|
os << " + ";
|
||||||
|
|
||||||
|
for(int i = 0; i < keys.size(); i++) {
|
||||||
|
std::cout << (keys[i].active ? '|' : ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
os << " + ";
|
||||||
|
|
||||||
|
for(int i = 0; i < channels.size(); i++) {
|
||||||
|
std::cout << (channels[i].active ? '.' : ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
os << " + \n";
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "packet.hpp"
|
||||||
|
#include <array>
|
||||||
|
#include <ostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct Scheduler {
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
unsigned key;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Key {
|
||||||
|
unsigned channel;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
Scheduler(double s_per_tick);
|
||||||
|
|
||||||
|
bool add_note(unsigned key, unsigned ticks, bool active);
|
||||||
|
void finalise(std::vector<char>& dst) const;
|
||||||
|
void display(std::ostream& os) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned ticks_at = 0;
|
||||||
|
unsigned channels_in_use = 0;
|
||||||
|
unsigned channels_in_use_max = 0;
|
||||||
|
unsigned us_per_tick;
|
||||||
|
|
||||||
|
std::vector<packet::Generic> packets;
|
||||||
|
std::array<Channel, 32> channels;
|
||||||
|
std::vector<Key> keys;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue