now generates converted midi data more directly

This commit is contained in:
Jay Robson 2024-08-25 16:53:43 +10:00
parent 3bf8d3b91b
commit 251ea220e3
10 changed files with 376 additions and 130 deletions

View File

@ -8,7 +8,9 @@ set(CMAKE_CXX_FLAGS "-g")
set(SRCS
src/main.cpp
src/device.cpp
src/header.cpp
src/packet.cpp
src/scheduler.cpp
src/key.cpp
)

25
src/header.cpp Normal file
View File

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

12
src/header.hpp Normal file
View File

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

View File

@ -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};
int key::get_freq(int key) {
int octave = key / std::size(KEYS);
return (int)std::round(KEYS[key % std::size(KEYS)] * std::pow(2, octave));
unsigned key::get_freq(unsigned key) {
unsigned octave = key / std::size(KEYS);
return (unsigned)std::round(KEYS[key % std::size(KEYS)] * std::pow(2, octave) * ((1<<20) / 1e6));
}

View File

@ -3,6 +3,6 @@
namespace key {
int get_freq(int key);
unsigned get_freq(unsigned key);
};

View File

@ -3,158 +3,56 @@
#include <cmath>
#include <csignal>
#include <cstring>
#include <fstream>
#include <ostream>
#include <iostream>
#include <vector>
#include "device.hpp"
#include "key.hpp"
#include "header.hpp"
#include "scheduler.hpp"
int main(int argc, char** argv) {
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();
Device device(argv[1]);
Scheduler scheduler(midifile.getTimeInSeconds(1));
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(); 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);
unsigned ticks_last = 0;
unsigned notes_added = 0;
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++) {
smf::MidiEvent& event = track[i];
smf::MidiEvent& note = track[i];
if(!event.isNote()) {
if(!note.isNote()) {
continue;
}
int channel;
int key = event.getKeyNumber();
if(key < 0) {
std::cerr << "Warning: Bad Key (" << key << ")" << std::endl;
continue;
if(note.tick > ticks_last) {
scheduler.display(std::cout);
}
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;
smf::MidiEvent& note_end = *note.getLinkedEvent();
if(!scheduler.add_note(note.getKeyNumber(), note.tick, note.isNoteOn())) {
// std::cout << "Warning (" << i << "): Failed\n";
}
else {
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++;
notes_added++;
}
device.read_until();
device.printf(";,:p,");
scheduler.display(std::cout);
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;
}

81
src/packet.cpp Normal file
View File

@ -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*)&amplitude;
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;
}
}

88
src/packet.hpp Normal file
View File

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

103
src/scheduler.cpp Normal file
View File

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

37
src/scheduler.hpp Normal file
View File

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