Compare commits
2 Commits
251ea220e3
...
31ab2fb132
Author | SHA1 | Date |
---|---|---|
Jay Robson | 31ab2fb132 | |
Jay Robson | 9e606ae532 |
|
@ -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++)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -1,12 +1,21 @@
|
|||
|
||||
#include "device.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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);
|
||||
|
|
|
@ -5,6 +5,16 @@
|
|||
#include <cstdio>
|
||||
|
||||
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<typename... Args>
|
||||
void printf(const char* format, Args... args) {
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
|
||||
#include "header.hpp"
|
||||
#include "binary.hpp"
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
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 << "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<char>& 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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
59
src/main.cpp
59
src/main.cpp
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
|
||||
#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) {
|
||||
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<char>& 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<char>& 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<char>& dst) const {
|
|||
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);
|
||||
v = (v << 5) | (index & bm(5));
|
||||
v = (v << 12) | (frequency & bm(12));
|
||||
|
@ -33,7 +32,7 @@ void packet::Set::finalise(std::vector<char>& dst) const {
|
|||
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);
|
||||
v = (v << 5) | (index & bm(5));
|
||||
v = (v << 20) | (ticks & bm(20));
|
||||
|
@ -41,22 +40,19 @@ void packet::SetTS::finalise(std::vector<char>& dst) const {
|
|||
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);
|
||||
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<char>& 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<char>& dst) const {
|
||||
void packet::Generic::finalise(std::ostream& dst) const {
|
||||
switch(type) {
|
||||
case Type::clear:
|
||||
clear.finalise(dst);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
|
||||
namespace packet {
|
||||
|
||||
|
@ -16,14 +16,14 @@ namespace packet {
|
|||
};
|
||||
|
||||
struct Stop {
|
||||
void finalise(std::vector<char>& dst) const;
|
||||
void finalise(std::ostream& dst) const;
|
||||
};
|
||||
|
||||
struct Set {
|
||||
unsigned index;
|
||||
unsigned frequency;
|
||||
|
||||
void finalise(std::vector<char>& dst) const;
|
||||
void finalise(std::ostream& dst) const;
|
||||
};
|
||||
|
||||
struct SetTS {
|
||||
|
@ -31,27 +31,27 @@ namespace packet {
|
|||
unsigned ticks;
|
||||
unsigned frequency;
|
||||
|
||||
void finalise(std::vector<char>& dst) const;
|
||||
void finalise(std::ostream& dst) const;
|
||||
};
|
||||
|
||||
struct Clear {
|
||||
unsigned index;
|
||||
|
||||
void finalise(std::vector<char>& dst) const;
|
||||
void finalise(std::ostream& dst) const;
|
||||
};
|
||||
|
||||
struct ClearTS {
|
||||
unsigned index;
|
||||
unsigned ticks;
|
||||
|
||||
void finalise(std::vector<char>& dst) const;
|
||||
void finalise(std::ostream& dst) const;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
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) {
|
||||
|
@ -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<char>& dst) const;
|
||||
void finalise(std::ostream& dst) const;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -3,12 +3,12 @@
|
|||
#include "key.hpp"
|
||||
#include "packet.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#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) {
|
||||
|
@ -69,19 +69,15 @@ bool Scheduler::add_note(unsigned id, unsigned ticks, bool state) {
|
|||
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_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 {
|
||||
|
@ -89,13 +85,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";
|
||||
|
|
|
@ -21,7 +21,7 @@ struct Scheduler {
|
|||
Scheduler(double s_per_tick);
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
|
|
|
@ -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;
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue