add track config menu
This commit is contained in:
parent
f763871106
commit
8c8f1f7a3d
|
@ -12,14 +12,13 @@ set(SRCS
|
|||
src/scheduler.cpp
|
||||
src/key.cpp
|
||||
src/device.cpp
|
||||
src/song_info.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(midifile)
|
||||
add_executable(parser src/parser.cpp ${SRCS})
|
||||
add_executable(streamer src/streamer.cpp ${SRCS})
|
||||
add_executable(header src/header.cpp ${SRCS})
|
||||
|
||||
target_link_libraries(parser PRIVATE midifile PUBLIC stdc++)
|
||||
target_link_libraries(streamer PRIVATE midifile PUBLIC stdc++)
|
||||
target_link_libraries(header PRIVATE midifile PUBLIC stdc++)
|
||||
target_link_libraries(parser PRIVATE midifile PUBLIC stdc++ ncurses)
|
||||
target_link_libraries(streamer PRIVATE midifile PUBLIC stdc++ ncurses)
|
||||
|
||||
|
|
|
@ -3,56 +3,62 @@
|
|||
#include "packet.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "../midifile/include/MidiFile.h"
|
||||
#include "song_info.hpp"
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
unsigned binary::process(std::ostream& dst, const char* path) {
|
||||
smf::MidiFile midifile;
|
||||
midifile.read(path);
|
||||
midifile.doTimeAnalysis();
|
||||
std::vector<packet::ToneType> track_modes(midifile.getNumTracks(), packet::ToneType::tt_sine);
|
||||
|
||||
song_info::Info info {.path=path};
|
||||
info.tracks.resize(midifile.getTrackCount());
|
||||
|
||||
midifile.joinTracks();
|
||||
auto& track = midifile[0];
|
||||
int simultaneous_notes = 0;
|
||||
int max_simultaneous_notes = 0;
|
||||
|
||||
for(int i = 0; i < track.size(); i++) {
|
||||
smf::MidiEvent& note = track[i];
|
||||
song_info::Track& track = info.tracks[note.track];
|
||||
|
||||
if(note.isTrackName()) {
|
||||
track.name = note.getMetaContent();
|
||||
}
|
||||
|
||||
if(note.isNote()) {
|
||||
if(note.isNoteOn()) {
|
||||
simultaneous_notes += 1;
|
||||
} else {
|
||||
simultaneous_notes -= 1;
|
||||
}
|
||||
max_simultaneous_notes = std::max(max_simultaneous_notes, simultaneous_notes);
|
||||
}
|
||||
}
|
||||
|
||||
info.volume = 256 / std::max(max_simultaneous_notes, 1);
|
||||
song_info::collect(info);
|
||||
|
||||
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];
|
||||
song_info::Track& track = info.tracks[note.track];
|
||||
|
||||
if(note.isTrackName()) {
|
||||
std::string name = note.getMetaContent();
|
||||
|
||||
for(int i = 0; i < name.length(); i++) {
|
||||
char c = name[i];
|
||||
if(c >= 'A' && c <= 'Z') {
|
||||
name[i] = c + 32;
|
||||
}
|
||||
}
|
||||
|
||||
if(name.find("saw") != std::string::npos) {
|
||||
track_modes[note.track] = packet::ToneType::tt_saw;
|
||||
}
|
||||
|
||||
else if(name.find("square") != std::string::npos || name.find("distortion") != std::string::npos || name.find("overdrive") != std::string::npos) {
|
||||
track_modes[note.track] = packet::ToneType::tt_square;
|
||||
}
|
||||
|
||||
else if(name.find("triangle") != std::string::npos) {
|
||||
track_modes[note.track] = packet::ToneType::tt_triangle;
|
||||
}
|
||||
}
|
||||
|
||||
if(!note.isNote()) {
|
||||
if(!track.enabled || !note.isNote()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
smf::MidiEvent& note_end = *note.getLinkedEvent();
|
||||
scheduler.add_note(note.getKeyNumber(), note.tick, note.isNoteOn(), track_modes[note.track]);
|
||||
scheduler.add_note(note.getKeyNumber(), note.tick, note.isNoteOn(), track.type, track.gain);
|
||||
notes_added++;
|
||||
}
|
||||
|
||||
|
@ -61,9 +67,23 @@ unsigned binary::process(std::ostream& dst, const char* path) {
|
|||
}
|
||||
|
||||
bool binary::process_all(std::string& dst, int start, int argc, char **argv) {
|
||||
if(argc > start) {
|
||||
if(argc < start + 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
for(int i = start; i < argc; i++) {
|
||||
|
||||
if(std::strcmp(argv[start], "-b") == 0) {
|
||||
if(argc != start + 2) {
|
||||
return false;
|
||||
}
|
||||
std::ifstream file(argv[start + 1], std::ios::binary);
|
||||
dst = std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
return true;
|
||||
}
|
||||
|
||||
if(std::strcmp(argv[start], "-i") == 0) {
|
||||
for(int i = start+1; i < argc; i++) {
|
||||
binary::process(ss, argv[i]);
|
||||
}
|
||||
binary::finalize(ss);
|
||||
|
@ -71,14 +91,7 @@ bool binary::process_all(std::string& dst, int start, int argc, char **argv) {
|
|||
return true;
|
||||
}
|
||||
|
||||
else if(argc == start) {
|
||||
dst = std::string(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>());
|
||||
return true;
|
||||
}
|
||||
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void binary::finalize(std::ostream &dst) {
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
|
||||
#include "binary.hpp"
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
const int COLS = 32;
|
||||
|
||||
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.length(); 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;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
std::string data;
|
||||
|
||||
if(!binary::process_all(data, 2, argc, argv)) {
|
||||
std::cerr << "Usage: " << argv[0] << " varname [midifile ...]?\n\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
generate(std::cout, data, argv[1]);
|
||||
}
|
||||
|
|
@ -12,6 +12,19 @@ static void output(std::ostream& dst, uint64_t v, int size) {
|
|||
}
|
||||
}
|
||||
|
||||
const char* packet::tone_type_get_str(ToneType tt) {
|
||||
switch(tt) {
|
||||
case tt_sine:
|
||||
return "sine";
|
||||
case tt_saw:
|
||||
return "sawtooth";
|
||||
case tt_square:
|
||||
return "square";
|
||||
case tt_triangle:
|
||||
return "triangle";
|
||||
}
|
||||
}
|
||||
|
||||
void packet::Clear::finalise(std::ostream& dst) const {
|
||||
dst.put((Type::clear << 5) | (index & bm(5)));
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ namespace packet {
|
|||
tt_triangle = 3,
|
||||
};
|
||||
|
||||
const char* tone_type_get_str(ToneType tt);
|
||||
|
||||
struct Stop {
|
||||
void finalise(std::ostream& dst) const;
|
||||
};
|
||||
|
|
|
@ -2,21 +2,24 @@
|
|||
#include <cmath>
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "binary.hpp"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
if(argc == 1) {
|
||||
std::cerr << "Usage: " << argv[0] << " midifile ...\n\n";
|
||||
if(argc < 3) {
|
||||
std::cerr << "Usage: " << argv[0] << " output [midifile ...]\n\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(int i = 1; i < argc; i++) {
|
||||
binary::process(std::cout, argv[i]);
|
||||
std::ofstream output(argv[1], std::ios::binary);
|
||||
|
||||
for(int i = 2; i < argc; i++) {
|
||||
binary::process(output, argv[i]);
|
||||
}
|
||||
|
||||
binary::finalize(std::cout);
|
||||
binary::finalize(output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ Scheduler::Scheduler(double s_per_tick)
|
|||
: us_per_tick(s_per_tick * 1e6) {
|
||||
}
|
||||
|
||||
bool Scheduler::add_note(unsigned key_id, unsigned ticks, bool state, packet::ToneType mode) {
|
||||
bool Scheduler::add_note(unsigned key_id, unsigned ticks, bool state, packet::ToneType mode, unsigned gain) {
|
||||
|
||||
unsigned id = key_id * (mode + 1);
|
||||
|
||||
|
@ -29,9 +29,20 @@ bool Scheduler::add_note(unsigned key_id, unsigned ticks, bool state, packet::To
|
|||
channels_in_use--;
|
||||
|
||||
if(ticks_at >= ticks) {
|
||||
packets.push_back({.type=packet::Type::clear, .clear={.index=channel}});
|
||||
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}});
|
||||
packets.push_back({
|
||||
.type=packet::Type::clear_ts,
|
||||
.clear_ts={
|
||||
.index=channel,
|
||||
.ticks=ticks - ticks_at,
|
||||
},
|
||||
});
|
||||
ticks_at = ticks;
|
||||
}
|
||||
return true;
|
||||
|
@ -61,7 +72,7 @@ bool Scheduler::add_note(unsigned key_id, unsigned ticks, bool state, packet::To
|
|||
.index=channel_id,
|
||||
.frequency=key::get_freq(key_id),
|
||||
.mode=mode,
|
||||
.gain=0,
|
||||
.gain=gain,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
|
@ -72,7 +83,7 @@ bool Scheduler::add_note(unsigned key_id, unsigned ticks, bool state, packet::To
|
|||
.ticks=ticks - ticks_at,
|
||||
.frequency=key::get_freq(key_id),
|
||||
.mode=mode,
|
||||
.gain=0,
|
||||
.gain=gain,
|
||||
},
|
||||
});
|
||||
ticks_at = ticks;
|
||||
|
|
|
@ -20,7 +20,7 @@ struct Scheduler {
|
|||
|
||||
Scheduler(double s_per_tick);
|
||||
|
||||
bool add_note(unsigned key, unsigned ticks, bool active, packet::ToneType mode);
|
||||
bool add_note(unsigned key, unsigned ticks, bool active, packet::ToneType mode, unsigned gain);
|
||||
void finalise(std::ostream& os) const;
|
||||
void display(std::ostream& os) const;
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
|
||||
#include "song_info.hpp"
|
||||
#include "packet.hpp"
|
||||
#include "util.hpp"
|
||||
#include <cmath>
|
||||
#include <ncurses.h>
|
||||
|
||||
struct ScreenGuard {
|
||||
ScreenGuard() {
|
||||
initscr();
|
||||
keypad(stdscr, true);
|
||||
nonl();
|
||||
cbreak();
|
||||
noecho();
|
||||
}
|
||||
|
||||
~ScreenGuard() {
|
||||
endwin();
|
||||
}
|
||||
};
|
||||
|
||||
void song_info::collect(Info& info) {
|
||||
ScreenGuard guard;
|
||||
bool running = true;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
while(running) {
|
||||
|
||||
// render info screen
|
||||
clear();
|
||||
mvaddstr(1, 0, "Configuring ");
|
||||
addstr(info.path.c_str());
|
||||
|
||||
mvaddstr(3, 0, "Volume");
|
||||
mvaddstr(3, 32, util::sprintf("[ %d / 256 ]", info.volume).c_str());
|
||||
|
||||
mvaddstr(6, 0, "Track Name");
|
||||
mvaddstr(6, 32, "Enable");
|
||||
mvaddstr(6, 48, "Gain");
|
||||
mvaddstr(6, 64, "Instrument");
|
||||
|
||||
for(unsigned i = 0; i < info.tracks.size(); i++) {
|
||||
Track& t = info.tracks[i];
|
||||
mvaddstr(8+i, 0, t.name.c_str());
|
||||
mvaddstr(8+i, 32, util::sprintf("[ %s ]", t.enabled ? "*" : " ").c_str());
|
||||
if(t.enabled) {
|
||||
mvaddstr(8+i, 48, util::sprintf("[ %d% ]", (t.gain + 1) * 100).c_str());
|
||||
mvaddstr(8+i, 64, util::sprintf("[ %s ]", packet::tone_type_get_str(t.type)).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if(y == 0) {
|
||||
move(3, 32);
|
||||
} else {
|
||||
move(8+y-1, 34+x*16);
|
||||
}
|
||||
|
||||
int c = getch();
|
||||
int dir = 1;
|
||||
|
||||
switch(c) {
|
||||
case KEY_UP:
|
||||
y = std::max(0, y - 1);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
y = std::min((int)info.tracks.size(), y + 1);
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
x = ((x - 1) % 3 + 3) % 3;
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
x = (x + 1) % 3;
|
||||
break;
|
||||
case 27:
|
||||
running = false;
|
||||
break;
|
||||
case 'z': case 'Z':
|
||||
dir = -1;
|
||||
case 'x': case 'X': case 13: case ' ': case KEY_ENTER: {
|
||||
if(y == 0) {
|
||||
info.volume += dir;
|
||||
info.volume &= 255;
|
||||
break;
|
||||
}
|
||||
Track& t = info.tracks[y - 1];
|
||||
switch(x) {
|
||||
case 0:
|
||||
t.enabled ^= 1;
|
||||
break;
|
||||
case 1:
|
||||
t.gain += dir;
|
||||
t.gain &= 3;
|
||||
break;
|
||||
case 2:
|
||||
t.type = (packet::ToneType)((t.type + dir) & 3);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "packet.hpp"
|
||||
|
||||
namespace song_info {
|
||||
|
||||
struct Track {
|
||||
std::string name = "";
|
||||
packet::ToneType type = packet::ToneType::tt_sine;
|
||||
unsigned gain = 0;
|
||||
bool enabled = 1;
|
||||
};
|
||||
struct Info {
|
||||
std::vector<Track> tracks;
|
||||
unsigned volume;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
void collect(Info& info);
|
||||
};
|
||||
|
|
@ -77,7 +77,9 @@ int main(int argc, char** argv) {
|
|||
|
||||
if(!binary::process_all(data, 3, argc, argv)) {
|
||||
error_help:
|
||||
std::cerr << "Usage: " << argv[0] << " -[s|f] device [midifile ...]?\n\n";
|
||||
std::cerr << "Usage: " << argv[0] << " -[s|f] device ...\n";
|
||||
std::cerr << " -i [midifile ...]\n";
|
||||
std::cerr << " -b binary\n\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
namespace util {
|
||||
|
||||
template <typename... Types>
|
||||
constexpr std::string sprintf(const char* fmt, Types... args) {
|
||||
std::size_t len = std::snprintf(nullptr, 0, fmt, args...);
|
||||
std::string str;
|
||||
str.resize(len + 1);
|
||||
len = std::snprintf(str.data(), len + 1, fmt, args...);
|
||||
str[len] = '\0';
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue