Compare commits

...

2 Commits

14 changed files with 237 additions and 115 deletions

View File

@ -7,15 +7,19 @@ set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_FLAGS "-g") set(CMAKE_CXX_FLAGS "-g")
set(SRCS set(SRCS
src/main.cpp
src/header.cpp
src/packet.cpp src/packet.cpp
src/binary.cpp
src/scheduler.cpp src/scheduler.cpp
src/key.cpp src/key.cpp
src/device.cpp
) )
add_subdirectory(midifile) 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++)

60
src/binary.cpp Normal file
View File

@ -0,0 +1,60 @@
#include "binary.hpp"
#include "packet.hpp"
#include "scheduler.hpp"
#include "../midifile/include/MidiFile.h"
#include <ostream>
#include <sstream>
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<char>(std::cin), std::istreambuf_iterator<char>());
return true;
}
else {
return false;
}
}
void binary::finalize(std::ostream &dst) {
packet::Generic(packet::Type::stop).finalise(dst);
}

12
src/binary.hpp Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <ostream>
#include <string>
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);
}

View File

@ -1,12 +1,21 @@
#include "device.hpp" #include "device.hpp"
#include <cassert>
#include <fcntl.h> #include <fcntl.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
Device::Device(const char* path) { Device::Device(const char* path) {
fd = ::open(path, O_RDWR); fd = ::open(path, O_RDWR);
struct termios tio;
tcgetattr(fd, &tio);
cfsetspeed(&tio, B57600);
cfmakeraw(&tio);
assert(tcsetattr(fd, TCSANOW, &tio) == 0);
} }
Device::~Device() { Device::~Device() {
@ -31,6 +40,16 @@ void Device::read(char* data, size_t len) {
::read(fd, data, 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) { void Device::read_until(char target) {
char ch; char ch;
read(&ch, 1); read(&ch, 1);

View File

@ -5,6 +5,16 @@
#include <cstdio> #include <cstdio>
struct Device { struct Device {
enum Mode {
e_none,
e_ready,
e_local,
e_serial,
e_read,
e_jump,
};
Device(const char* path); Device(const char* path);
~Device(); ~Device();
operator int(); operator int();
@ -12,6 +22,8 @@ struct Device {
void write(const char* data, size_t len); void write(const char* data, size_t len);
void read(char* data, size_t len); void read(char* data, size_t len);
void read_until(char target = '\n'); void read_until(char target = '\n');
void put(char ch);
char get();
template<typename... Args> template<typename... Args>
void printf(const char* format, Args... args) { void printf(const char* format, Args... args) {

View File

@ -1,15 +1,17 @@
#include "header.hpp" #include "binary.hpp"
#include <ios> #include <ios>
#include <iostream>
#include <string>
const int COLS = 32; const int COLS = 32;
void header::generate(std::ostream& dst, const std::vector<char>& src, const char* var_name) { static void generate(std::ostream& dst, const std::string& src, const char* var_name) {
dst << "#include <avr/pgmspace.h>\n"; dst << "#include <avr/pgmspace.h>\n";
dst << "inline const PROGMEM unsigned char " << var_name << "[] = {\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'; dst << '\t';
@ -23,3 +25,15 @@ void header::generate(std::ostream& dst, const std::vector<char>& src, const cha
dst << "};\n" << std::dec; 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]);
}

View File

@ -1,12 +0,0 @@
#pragma once
#include <ostream>
#include <vector>
namespace header {
void generate(std::ostream& dst, const std::vector<char>& src, const char* var_name);
};

View File

@ -1,59 +0,0 @@
#include "../midifile/include/MidiFile.h"
#include <cmath>
#include <csignal>
#include <cstring>
#include <fstream>
#include <ostream>
#include <iostream>
#include <vector>
#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<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;
}

View File

@ -1,23 +1,22 @@
#include "packet.hpp" #include "packet.hpp"
#include <cstdint> #include <cstdint>
#include <vector>
constexpr auto bm(auto n) { constexpr auto bm(auto n) {
return (1L << n) - 1; return (1L << n) - 1;
} }
static void output(std::vector<char>& 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) { 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<char>& dst) const { void packet::Clear::finalise(std::ostream& dst) const {
dst.push_back((Type::clear << 5) | (index & bm(5))); dst.put((Type::clear << 5) | (index & bm(5)));
} }
void packet::ClearTS::finalise(std::vector<char>& dst) const { void packet::ClearTS::finalise(std::ostream& dst) const {
uint64_t v = Type::clear_ts & bm(3); uint64_t v = Type::clear_ts & bm(3);
v = (v << 5) | (index & bm(5)); v = (v << 5) | (index & bm(5));
v = (v << 20) | (ticks & bm(20)); v = (v << 20) | (ticks & bm(20));
@ -25,7 +24,7 @@ void packet::ClearTS::finalise(std::vector<char>& dst) const {
output(dst, v, 4); output(dst, v, 4);
} }
void packet::Set::finalise(std::vector<char>& dst) const { void packet::Set::finalise(std::ostream& dst) const {
uint64_t v = Type::set & bm(3); uint64_t v = Type::set & bm(3);
v = (v << 5) | (index & bm(5)); v = (v << 5) | (index & bm(5));
v = (v << 12) | (frequency & bm(12)); v = (v << 12) | (frequency & bm(12));
@ -33,7 +32,7 @@ void packet::Set::finalise(std::vector<char>& dst) const {
output(dst, v, 3); output(dst, v, 3);
} }
void packet::SetTS::finalise(std::vector<char>& dst) const { void packet::SetTS::finalise(std::ostream& dst) const {
uint64_t v = Type::set_ts & bm(3); uint64_t v = Type::set_ts & bm(3);
v = (v << 5) | (index & bm(5)); v = (v << 5) | (index & bm(5));
v = (v << 20) | (ticks & bm(20)); v = (v << 20) | (ticks & bm(20));
@ -41,22 +40,19 @@ void packet::SetTS::finalise(std::vector<char>& dst) const {
output(dst, v, 5); output(dst, v, 5);
} }
void packet::Config::finalise(std::vector<char>& dst) const { void packet::Config::finalise(std::ostream& dst) const {
uint64_t v = Type::config & bm(3); uint64_t v = Type::config & bm(3);
uint8_t* a_data = (uint8_t*)&amplitude; uint8_t* a_data = (uint8_t*)&amplitude;
v = (v << 29) | (us_per_tick & bm(29)); v = (v << 29) | (us_per_tick & bm(29));
output(dst, v, 4); v = (v << 8) | (amplitude & bm(8));
output(dst, v, 5);
for(int i = 0; i < 4; i++) {
dst.push_back(a_data[i]);
}
} }
void packet::Stop::finalise(std::vector<char>& dst) const { void packet::Stop::finalise(std::ostream& dst) const {
dst.push_back(Type::stop << 5); dst.put(Type::stop << 5);
} }
void packet::Generic::finalise(std::vector<char>& dst) const { void packet::Generic::finalise(std::ostream& dst) const {
switch(type) { switch(type) {
case Type::clear: case Type::clear:
clear.finalise(dst); clear.finalise(dst);

View File

@ -2,7 +2,7 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <vector> #include <ostream>
namespace packet { namespace packet {
@ -16,14 +16,14 @@ namespace packet {
}; };
struct Stop { struct Stop {
void finalise(std::vector<char>& dst) const; void finalise(std::ostream& dst) const;
}; };
struct Set { struct Set {
unsigned index; unsigned index;
unsigned frequency; unsigned frequency;
void finalise(std::vector<char>& dst) const; void finalise(std::ostream& dst) const;
}; };
struct SetTS { struct SetTS {
@ -31,27 +31,27 @@ namespace packet {
unsigned ticks; unsigned ticks;
unsigned frequency; unsigned frequency;
void finalise(std::vector<char>& dst) const; void finalise(std::ostream& dst) const;
}; };
struct Clear { struct Clear {
unsigned index; unsigned index;
void finalise(std::vector<char>& dst) const; void finalise(std::ostream& dst) const;
}; };
struct ClearTS { struct ClearTS {
unsigned index; unsigned index;
unsigned ticks; unsigned ticks;
void finalise(std::vector<char>& dst) const; void finalise(std::ostream& dst) const;
}; };
struct Config { struct Config {
unsigned us_per_tick; unsigned us_per_tick;
float amplitude; unsigned amplitude;
void finalise(std::vector<char>& dst) const; void finalise(std::ostream& dst) const;
}; };
constexpr std::size_t get_size(Type type) { constexpr std::size_t get_size(Type type) {
@ -67,7 +67,7 @@ namespace packet {
case Type::stop: case Type::stop:
return 1; return 1;
case Type::config: case Type::config:
return 8; return 5;
} }
return 1; return 1;
} }
@ -82,7 +82,7 @@ namespace packet {
ClearTS clear_ts; ClearTS clear_ts;
Config config; Config config;
}; };
void finalise(std::vector<char>& dst) const; void finalise(std::ostream& dst) const;
}; };
}; };

23
src/parser.cpp Normal file
View File

@ -0,0 +1,23 @@
#include <cmath>
#include <csignal>
#include <cstring>
#include <iostream>
#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;
}

View File

@ -3,12 +3,12 @@
#include "key.hpp" #include "key.hpp"
#include "packet.hpp" #include "packet.hpp"
#include <algorithm> #include <algorithm>
#include <cmath>
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <ostream> #include <ostream>
Scheduler::Scheduler(double s_per_tick) : us_per_tick(s_per_tick * 1e6) { 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) { bool Scheduler::add_note(unsigned id, unsigned ticks, bool state) {
@ -69,19 +69,15 @@ bool Scheduler::add_note(unsigned id, unsigned ticks, bool state) {
return true; return true;
} }
void Scheduler::finalise(std::vector<char>& 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_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); packet_start.finalise(dst);
for(const packet::Generic& packet : packets) { for(const packet::Generic& packet : packets) {
packet.finalise(dst); packet.finalise(dst);
} }
packet_end.finalise(dst);
} }
void Scheduler::display(std::ostream& os) const { void Scheduler::display(std::ostream& os) const {
@ -89,13 +85,13 @@ void Scheduler::display(std::ostream& os) const {
os << " + "; os << " + ";
for(int i = 0; i < keys.size(); i++) { for(int i = 0; i < keys.size(); i++) {
std::cout << (keys[i].active ? '|' : ' '); os << (keys[i].active ? '|' : ' ');
} }
os << " + "; os << " + ";
for(int i = 0; i < channels.size(); i++) { for(int i = 0; i < channels.size(); i++) {
std::cout << (channels[i].active ? '.' : ' '); os << (channels[i].active ? '.' : ' ');
} }
os << " + \n"; os << " + \n";

View File

@ -21,7 +21,7 @@ struct Scheduler {
Scheduler(double s_per_tick); Scheduler(double s_per_tick);
bool add_note(unsigned key, unsigned ticks, bool active); bool add_note(unsigned key, unsigned ticks, bool active);
void finalise(std::vector<char>& dst) const; void finalise(std::ostream& os) const;
void display(std::ostream& os) const; void display(std::ostream& os) const;
private: private:

57
src/streamer.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "binary.hpp"
#include "device.hpp"
#include <iostream>
#include <ostream>
#include <string>
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;
}}
}
}