From 9e606ae532457a1d97677821ccde8069bed48507 Mon Sep 17 00:00:00 2001 From: Jay Robson Date: Mon, 26 Aug 2024 13:02:53 +1000 Subject: [PATCH] split into multiple components; switched amplitude to integer math --- CMakeLists.txt | 12 ++++++---- src/binary.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++ src/binary.hpp | 12 ++++++++++ src/device.cpp | 19 +++++++++++++++ src/device.hpp | 12 ++++++++++ src/header.cpp | 20 +++++++++++++--- src/header.hpp | 12 ---------- src/main.cpp | 59 ---------------------------------------------- src/packet.cpp | 30 ++++++++++-------------- src/packet.hpp | 20 ++++++++-------- src/parser.cpp | 23 ++++++++++++++++++ src/scheduler.cpp | 15 ++++++------ src/scheduler.hpp | 2 +- src/streamer.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 239 insertions(+), 114 deletions(-) create mode 100644 src/binary.cpp create mode 100644 src/binary.hpp delete mode 100644 src/header.hpp delete mode 100644 src/main.cpp create mode 100644 src/parser.cpp create mode 100644 src/streamer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 44b0fee..a624c9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,19 @@ set(CMAKE_CXX_STANDARD 26) set(CMAKE_CXX_FLAGS "-g") set(SRCS - src/main.cpp - src/header.cpp src/packet.cpp + src/binary.cpp src/scheduler.cpp src/key.cpp + src/device.cpp ) add_subdirectory(midifile) -add_executable(midi-parser ${SRCS}) +add_executable(parser src/parser.cpp ${SRCS}) +add_executable(streamer src/streamer.cpp ${SRCS}) +add_executable(header src/header.cpp ${SRCS}) -target_link_libraries(midi-parser PRIVATE midifile PUBLIC stdc++) +target_link_libraries(parser PRIVATE midifile PUBLIC stdc++) +target_link_libraries(streamer PRIVATE midifile PUBLIC stdc++) +target_link_libraries(header PRIVATE midifile PUBLIC stdc++) diff --git a/src/binary.cpp b/src/binary.cpp new file mode 100644 index 0000000..978d620 --- /dev/null +++ b/src/binary.cpp @@ -0,0 +1,60 @@ + +#include "binary.hpp" +#include "packet.hpp" +#include "scheduler.hpp" +#include "../midifile/include/MidiFile.h" +#include +#include + +unsigned binary::process(std::ostream& dst, const char* path) { + smf::MidiFile midifile; + midifile.read(path); + midifile.doTimeAnalysis(); + midifile.joinTracks(); + + Scheduler scheduler(midifile.getTimeInSeconds(1)); + auto& track = midifile[0]; + unsigned ticks_last = 0; + unsigned notes_added = 0; + + for(int i = 0; i < track.size(); i++) { + smf::MidiEvent& note = track[i]; + + if(!note.isNote()) { + continue; + } + + smf::MidiEvent& note_end = *note.getLinkedEvent(); + scheduler.add_note(note.getKeyNumber(), note.tick, note.isNoteOn()); + notes_added++; + } + + scheduler.finalise(dst); + return notes_added; +} + +bool binary::process_all(std::string& dst, int argc, char **argv) { + if(argc > 2) { + std::stringstream ss; + for(int i = 2; i < argc; i++) { + binary::process(ss, argv[i]); + } + binary::finalize(ss); + dst = std::move(ss.str()); + return true; + } + + else if(argc == 2) { + dst = std::string(std::istreambuf_iterator(std::cin), std::istreambuf_iterator()); + return true; + } + + else { + return false; + } +} + +void binary::finalize(std::ostream &dst) { + packet::Generic(packet::Type::stop).finalise(dst); +} + diff --git a/src/binary.hpp b/src/binary.hpp new file mode 100644 index 0000000..8d8ee50 --- /dev/null +++ b/src/binary.hpp @@ -0,0 +1,12 @@ + +#pragma once + +#include +#include + +namespace binary { + unsigned process(std::ostream& dst, const char* path); + bool process_all(std::string& dst, int argc, char** argv); + void finalize(std::ostream& dst); +} + diff --git a/src/device.cpp b/src/device.cpp index 9170ba4..e7c3daa 100644 --- a/src/device.cpp +++ b/src/device.cpp @@ -1,12 +1,21 @@ #include "device.hpp" +#include #include #include #include Device::Device(const char* path) { fd = ::open(path, O_RDWR); + + struct termios tio; + tcgetattr(fd, &tio); + + cfsetspeed(&tio, B57600); + cfmakeraw(&tio); + + assert(tcsetattr(fd, TCSANOW, &tio) == 0); } Device::~Device() { @@ -31,6 +40,16 @@ void Device::read(char* data, size_t len) { ::read(fd, data, len); } +void Device::put(char ch) { + write(&ch, 1); +} + +char Device::get() { + char ch; + read(&ch, 1); + return ch; +} + void Device::read_until(char target) { char ch; read(&ch, 1); diff --git a/src/device.hpp b/src/device.hpp index eadfae4..a5ab56b 100644 --- a/src/device.hpp +++ b/src/device.hpp @@ -5,6 +5,16 @@ #include struct Device { + + enum Mode { + e_none, + e_ready, + e_local, + e_serial, + e_read, + e_jump, + }; + Device(const char* path); ~Device(); operator int(); @@ -12,6 +22,8 @@ struct Device { void write(const char* data, size_t len); void read(char* data, size_t len); void read_until(char target = '\n'); + void put(char ch); + char get(); template void printf(const char* format, Args... args) { diff --git a/src/header.cpp b/src/header.cpp index eeb7655..b489281 100644 --- a/src/header.cpp +++ b/src/header.cpp @@ -1,15 +1,17 @@ -#include "header.hpp" +#include "binary.hpp" #include +#include +#include const int COLS = 32; -void header::generate(std::ostream& dst, const std::vector& src, const char* var_name) { +static void generate(std::ostream& dst, const std::string& 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) { + for(int i = 0; i < src.length(); i += COLS) { dst << '\t'; @@ -23,3 +25,15 @@ void header::generate(std::ostream& dst, const std::vector& src, const cha dst << "};\n" << std::dec; } +int main(int argc, char** argv) { + + std::string data; + + if(!binary::process_all(data, argc, argv)) { + std::cerr << "Usage: " << argv[0] << " varname [midifile ...]?\n\n"; + return 1; + } + + generate(std::cout, data, argv[1]); +} + diff --git a/src/header.hpp b/src/header.hpp deleted file mode 100644 index 3a696c4..0000000 --- a/src/header.hpp +++ /dev/null @@ -1,12 +0,0 @@ - -#pragma once - -#include -#include - -namespace header { - -void generate(std::ostream& dst, const std::vector& src, const char* var_name); - -}; - diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 9c49b17..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,59 +0,0 @@ - -#include "../midifile/include/MidiFile.h" -#include -#include -#include -#include -#include -#include -#include -#include "header.hpp" -#include "scheduler.hpp" - -int main(int argc, char** argv) { - - smf::MidiFile midifile; - midifile.read(argv[2]); - - midifile.doTimeAnalysis(); - midifile.joinTracks(); - - Scheduler scheduler(midifile.getTimeInSeconds(1)); - auto& track = midifile[0]; - unsigned ticks_last = 0; - unsigned notes_added = 0; - - std::cout << "Seconds per tick: " << midifile.getTimeInSeconds(1) << std::endl; - - for(int i = 0; i < track.size(); i++) { - smf::MidiEvent& note = track[i]; - - if(!note.isNote()) { - continue; - } - - if(note.tick > ticks_last) { - scheduler.display(std::cout); - } - - smf::MidiEvent& note_end = *note.getLinkedEvent(); - if(!scheduler.add_note(note.getKeyNumber(), note.tick, note.isNoteOn())) { -// std::cout << "Warning (" << i << "): Failed\n"; - } - notes_added++; - } - - scheduler.display(std::cout); - - std::vector 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; -} - diff --git a/src/packet.cpp b/src/packet.cpp index ecebcfe..88ff896 100644 --- a/src/packet.cpp +++ b/src/packet.cpp @@ -1,23 +1,22 @@ #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) { +static void output(std::ostream& dst, uint64_t v, int size) { for(int i = size * 8 - 8; i >= 0; i -= 8) { - dst.push_back(v >> i); + dst.put(v >> i); } } -void packet::Clear::finalise(std::vector& dst) const { - dst.push_back((Type::clear << 5) | (index & bm(5))); +void packet::Clear::finalise(std::ostream& dst) const { + dst.put((Type::clear << 5) | (index & bm(5))); } -void packet::ClearTS::finalise(std::vector& dst) const { +void packet::ClearTS::finalise(std::ostream& dst) const { uint64_t v = Type::clear_ts & bm(3); v = (v << 5) | (index & bm(5)); v = (v << 20) | (ticks & bm(20)); @@ -25,7 +24,7 @@ void packet::ClearTS::finalise(std::vector& dst) const { output(dst, v, 4); } -void packet::Set::finalise(std::vector& dst) const { +void packet::Set::finalise(std::ostream& dst) const { uint64_t v = Type::set & bm(3); v = (v << 5) | (index & bm(5)); v = (v << 12) | (frequency & bm(12)); @@ -33,7 +32,7 @@ void packet::Set::finalise(std::vector& dst) const { output(dst, v, 3); } -void packet::SetTS::finalise(std::vector& dst) const { +void packet::SetTS::finalise(std::ostream& dst) const { uint64_t v = Type::set_ts & bm(3); v = (v << 5) | (index & bm(5)); v = (v << 20) | (ticks & bm(20)); @@ -41,22 +40,19 @@ void packet::SetTS::finalise(std::vector& dst) const { output(dst, v, 5); } -void packet::Config::finalise(std::vector& dst) const { +void packet::Config::finalise(std::ostream& 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]); - } + v = (v << 8) | (amplitude & bm(8)); + output(dst, v, 5); } -void packet::Stop::finalise(std::vector& dst) const { - dst.push_back(Type::stop << 5); +void packet::Stop::finalise(std::ostream& dst) const { + dst.put(Type::stop << 5); } -void packet::Generic::finalise(std::vector& dst) const { +void packet::Generic::finalise(std::ostream& dst) const { switch(type) { case Type::clear: clear.finalise(dst); diff --git a/src/packet.hpp b/src/packet.hpp index 2df1799..fad1242 100644 --- a/src/packet.hpp +++ b/src/packet.hpp @@ -2,7 +2,7 @@ #pragma once #include -#include +#include namespace packet { @@ -16,14 +16,14 @@ namespace packet { }; struct Stop { - void finalise(std::vector& dst) const; + void finalise(std::ostream& dst) const; }; struct Set { unsigned index; unsigned frequency; - void finalise(std::vector& dst) const; + void finalise(std::ostream& dst) const; }; struct SetTS { @@ -31,27 +31,27 @@ namespace packet { unsigned ticks; unsigned frequency; - void finalise(std::vector& dst) const; + void finalise(std::ostream& dst) const; }; struct Clear { unsigned index; - void finalise(std::vector& dst) const; + void finalise(std::ostream& dst) const; }; struct ClearTS { unsigned index; unsigned ticks; - void finalise(std::vector& dst) const; + void finalise(std::ostream& dst) const; }; struct Config { unsigned us_per_tick; - float amplitude; + unsigned amplitude; - void finalise(std::vector& dst) const; + void finalise(std::ostream& dst) const; }; constexpr std::size_t get_size(Type type) { @@ -67,7 +67,7 @@ namespace packet { case Type::stop: return 1; case Type::config: - return 8; + return 5; } return 1; } @@ -82,7 +82,7 @@ namespace packet { ClearTS clear_ts; Config config; }; - void finalise(std::vector& dst) const; + void finalise(std::ostream& dst) const; }; }; diff --git a/src/parser.cpp b/src/parser.cpp new file mode 100644 index 0000000..20a5648 --- /dev/null +++ b/src/parser.cpp @@ -0,0 +1,23 @@ + +#include +#include +#include +#include +#include "binary.hpp" + +int main(int argc, char** argv) { + + if(argc == 1) { + std::cerr << "Usage: " << argv[0] << " midifile ...\n\n"; + return 1; + } + + for(int i = 1; i < argc; i++) { + binary::process(std::cout, argv[i]); + } + + binary::finalize(std::cout); + + return 0; +} + diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 9dbec1f..c5936e8 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -3,12 +3,12 @@ #include "key.hpp" #include "packet.hpp" #include +#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) { @@ -69,19 +69,18 @@ bool Scheduler::add_note(unsigned id, unsigned ticks, bool state) { return true; } -void Scheduler::finalise(std::vector& dst) const { +void Scheduler::finalise(std::ostream& dst) const { - float amplitude = std::max(1.f / channels_in_use_max, 1.f/8.f); + unsigned amplitude = std::round(std::max(1.f / channels_in_use_max, 1.f/8.f) * 255); 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={}}; + + std::cerr << "amplitude = " << amplitude << std::endl; packet_start.finalise(dst); for(const packet::Generic& packet : packets) { packet.finalise(dst); } - - packet_end.finalise(dst); } void Scheduler::display(std::ostream& os) const { @@ -89,13 +88,13 @@ void Scheduler::display(std::ostream& os) const { os << " + "; for(int i = 0; i < keys.size(); i++) { - std::cout << (keys[i].active ? '|' : ' '); + os << (keys[i].active ? '|' : ' '); } os << " + "; for(int i = 0; i < channels.size(); i++) { - std::cout << (channels[i].active ? '.' : ' '); + os << (channels[i].active ? '.' : ' '); } os << " + \n"; diff --git a/src/scheduler.hpp b/src/scheduler.hpp index d171392..3780c3e 100644 --- a/src/scheduler.hpp +++ b/src/scheduler.hpp @@ -21,7 +21,7 @@ struct Scheduler { Scheduler(double s_per_tick); bool add_note(unsigned key, unsigned ticks, bool active); - void finalise(std::vector& dst) const; + void finalise(std::ostream& os) const; void display(std::ostream& os) const; private: diff --git a/src/streamer.cpp b/src/streamer.cpp new file mode 100644 index 0000000..36be3d9 --- /dev/null +++ b/src/streamer.cpp @@ -0,0 +1,57 @@ + +#include "binary.hpp" +#include "device.hpp" +#include +#include +#include + +int main(int argc, char** argv) { + std::string data; + + if(!binary::process_all(data, argc, argv)) { + std::cerr << "Usage: " << argv[0] << " device [midifile ...]?\n\n"; + return 1; + } + + unsigned at = 0; + Device device(argv[1]); + device.put(Device::Mode::e_serial); + + for(;;) { + Device::Mode mode = (Device::Mode)device.get(); + switch(mode) { + default: { + std::cout << "device: unknown (" << mode << ")" << std::endl; + break; + } + case Device::Mode::e_ready: { + device.put(Device::Mode::e_serial); + std::cout << "device: ready" << std::endl; + break; + } + case Device::Mode::e_jump: { + at = (device.get() & 0xff); + at = (at << 8) | (device.get() & 0xff); + at = (at << 8) | (device.get() & 0xff); + at = (at << 8) | (device.get() & 0xff); + std::cout << "device: jump (" << at << ")" << std::endl; + break; + } + case Device::Mode::e_read: { + unsigned len = (device.get() & 0xff); + if(len + at > data.length()) { + unsigned l = data.length() - at; + device.write(data.c_str() + at, l); + for(int i = 0; i < len - l; i++) { + device.put(0); + } + at += l; + } else { + device.write(data.c_str() + at, len); + at += len; + } + break; + }} + } +} +