From 251ea220e3bc16da1a14d192ff9dddb03d2dfc59 Mon Sep 17 00:00:00 2001 From: Jay Robson Date: Sun, 25 Aug 2024 16:53:43 +1000 Subject: [PATCH] now generates converted midi data more directly --- CMakeLists.txt | 4 +- src/header.cpp | 25 ++++++++ src/header.hpp | 12 ++++ src/key.cpp | 6 +- src/key.hpp | 2 +- src/main.cpp | 148 +++++++--------------------------------------- src/packet.cpp | 81 +++++++++++++++++++++++++ src/packet.hpp | 88 +++++++++++++++++++++++++++ src/scheduler.cpp | 103 ++++++++++++++++++++++++++++++++ src/scheduler.hpp | 37 ++++++++++++ 10 files changed, 376 insertions(+), 130 deletions(-) create mode 100644 src/header.cpp create mode 100644 src/header.hpp create mode 100644 src/packet.cpp create mode 100644 src/packet.hpp create mode 100644 src/scheduler.cpp create mode 100644 src/scheduler.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e9a5e22..44b0fee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/src/header.cpp b/src/header.cpp new file mode 100644 index 0000000..eeb7655 --- /dev/null +++ b/src/header.cpp @@ -0,0 +1,25 @@ + +#include "header.hpp" +#include + +const int COLS = 32; + +void header::generate(std::ostream& dst, const std::vector& src, const char* var_name) { + + dst << "#include \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; +} + diff --git a/src/header.hpp b/src/header.hpp new file mode 100644 index 0000000..3a696c4 --- /dev/null +++ b/src/header.hpp @@ -0,0 +1,12 @@ + +#pragma once + +#include +#include + +namespace header { + +void generate(std::ostream& dst, const std::vector& src, const char* var_name); + +}; + diff --git a/src/key.cpp b/src/key.cpp index 8836332..38e7873 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -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)); } diff --git a/src/key.hpp b/src/key.hpp index 7db4cd1..66744fa 100644 --- a/src/key.hpp +++ b/src/key.hpp @@ -3,6 +3,6 @@ namespace key { - int get_freq(int key); + unsigned get_freq(unsigned key); }; diff --git a/src/main.cpp b/src/main.cpp index 82e3f0c..9c49b17 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,158 +3,56 @@ #include #include #include +#include #include #include #include -#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 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 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()) { - continue; - } - - int channel; - int key = event.getKeyNumber(); - - if(key < 0) { - std::cerr << "Warning: Bad Key (" << key << ")" << std::endl; + if(!note.isNote()) { 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: - - 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(note.tick > ticks_last) { + scheduler.display(std::cout); } - if(i < track.size() - 1 && event.seconds == track[i + 1].seconds) { - continue; + smf::MidiEvent& note_end = *note.getLinkedEvent(); + if(!scheduler.add_note(note.getKeyNumber(), note.tick, note.isNoteOn())) { +// std::cout << "Warning (" << i << "): Failed\n"; } - - 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++; } + + scheduler.display(std::cout); - device.read_until(); - device.printf(";,:p,"); + std::vector bytes; + scheduler.finalise(bytes); - std::cout << "Sent " << tones_total << " tones\n"; + 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; } diff --git a/src/packet.cpp b/src/packet.cpp new file mode 100644 index 0000000..ecebcfe --- /dev/null +++ b/src/packet.cpp @@ -0,0 +1,81 @@ + +#include "packet.hpp" +#include +#include + +constexpr auto bm(auto n) { + return (1L << n) - 1; +} + +static void output(std::vector& 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& dst) const { + dst.push_back((Type::clear << 5) | (index & bm(5))); +} + +void packet::ClearTS::finalise(std::vector& 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& 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& 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& 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& dst) const { + dst.push_back(Type::stop << 5); +} + +void packet::Generic::finalise(std::vector& 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; + } +} + diff --git a/src/packet.hpp b/src/packet.hpp new file mode 100644 index 0000000..2df1799 --- /dev/null +++ b/src/packet.hpp @@ -0,0 +1,88 @@ + +#pragma once + +#include +#include + +namespace packet { + + enum Type : unsigned { + stop, + config, + set, + set_ts, + clear, + clear_ts, + }; + + struct Stop { + void finalise(std::vector& dst) const; + }; + + struct Set { + unsigned index; + unsigned frequency; + + void finalise(std::vector& dst) const; + }; + + struct SetTS { + unsigned index; + unsigned ticks; + unsigned frequency; + + void finalise(std::vector& dst) const; + }; + + struct Clear { + unsigned index; + + void finalise(std::vector& dst) const; + }; + + struct ClearTS { + unsigned index; + unsigned ticks; + + void finalise(std::vector& dst) const; + }; + + struct Config { + unsigned us_per_tick; + float amplitude; + + void finalise(std::vector& 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& dst) const; + }; +}; + diff --git a/src/scheduler.cpp b/src/scheduler.cpp new file mode 100644 index 0000000..9dbec1f --- /dev/null +++ b/src/scheduler.cpp @@ -0,0 +1,103 @@ + +#include "scheduler.hpp" +#include "key.hpp" +#include "packet.hpp" +#include +#include +#include +#include + +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& 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"; +} + diff --git a/src/scheduler.hpp b/src/scheduler.hpp new file mode 100644 index 0000000..d171392 --- /dev/null +++ b/src/scheduler.hpp @@ -0,0 +1,37 @@ + +#pragma once + +#include "packet.hpp" +#include +#include +#include + +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& 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 packets; + std::array channels; + std::vector keys; +}; +