Skip to content

Commit

Permalink
Added support for Chords
Browse files Browse the repository at this point in the history
  • Loading branch information
RandyParedis committed Nov 24, 2018
1 parent 9e3949a commit 457b05a
Show file tree
Hide file tree
Showing 13 changed files with 339 additions and 175 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,4 @@ compile `cmake` of this project with the flag `-DTRNG_LOC` set to your home dire
| 18-11-2018 | Added **1/f Noise** and **Centralized** as a pitch algorithm.
| 22-11-2018 | Fixed MidiPlayer playback with ties, added `bpm` and `time` signature to styles.
| 22-11-2018 | Added **Brownian Motion** as a rhythm algorithm.
| 24-11-2018 | Added support for chords, **BUT** `MIDIPlayer` currently only plays bottom note of each chord and no multiple-note chords are being generated by the algorithms (yet).
2 changes: 1 addition & 1 deletion docs/Future.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This file describes all planned features to be added in the future
- _(Dynamic algorithms)_
- _(Custom algorithm parsing)_
- MidiPlayer
- Optimize playback (redo algorithm).
- _(Force playing at all times)_
- Config
- Make it so the execution does not break on invalid input (preprocessing)
Expand All @@ -24,4 +25,3 @@ This file describes all planned features to be added in the future
- General Todo
- Simple Installation
- Automatic beaming for notes
- Add support for chords
4 changes: 2 additions & 2 deletions main/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
"options": {
"rhythm": {
"smallest": "16th",
"largest": "quarter"
"largest": "half"
}
},
"rest-ratio": 0.05
},
"export": {
"filename": "good-rhythm.xml",
"filename": "music.xml",
"title": "My Score",
"composer": "autoplay v@VERSION@",
"rights": "Copyright \u00A9 2018 autoplay v@VERSION@, created by Randy Paredis"
Expand Down
26 changes: 14 additions & 12 deletions main/music/MIDIPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ namespace autoplay {
midiout->sendMessage(&msg);

// Collect & Play Measures
// TODO: split up in collect and playback
// TODO: add support for playing chords, instead of the bottom note.
logger->debug("Collecting and Playing ") << duration << " Measure(s)";
zz::log::ProgBar pb{duration, "Playing"};
for(unsigned measure_number = 0; measure_number < duration; ++measure_number) {
Expand All @@ -146,44 +148,44 @@ namespace autoplay {
bpm = curr_measure->getBPM();
}

for(const auto& note : curr_measure->getNotes()) {
for(const auto& chord : curr_measure->getNotes()) {
uint8_t msgch = channel;
if(score.getParts().at(channel)->getInstruments().size() > 1 ||
score.getParts().at(channel)->getInstruments().at(0)->isPercussion()) {
msgch = 9;
}
if(note.isPause()) {
for(uint8_t len = 0; len < note.getDuration(); ++len) {
if(chord.isPause()) {
for(uint8_t len = 0; len < chord.getDuration(); ++len) {
msg = {};
nl.emplace_back(msg);
}
continue;
}
std::vector<unsigned char> _msg = {};
if(!note.getTieEnd()) {
_msg = note.getOnMessage(msgch);
if(!chord.getTieEnd()) {
_msg = chord.bottom()->getOnMessage(msgch);
if(score.getParts().at(channel)->getInstruments().size() > 1 ||
score.getParts().at(channel)->getInstruments().at(0)->isPercussion()) {
if(note.getInstrument() != nullptr) {
_msg.at(1) = note.getInstrument()->getUnpitched();
if(chord.bottom()->getInstrument() != nullptr) {
_msg.at(1) = chord.bottom()->getInstrument()->getUnpitched();
}
}
}
nl.emplace_back(_msg);

for(uint8_t len = 0; len < note.getDuration() - 2; ++len) { // duration - strike - release
for(uint8_t len = 0; len < chord.getDuration() - 2; ++len) { // duration - strike - release
msg = {};
nl.emplace_back(msg);
}
if(note.getTieStart()) {
if(chord.getTieStart()) {
msg = {};
nl.emplace_back(msg);
} else {
_msg = note.getOffMessage(msgch);
_msg = chord.bottom()->getOffMessage(msgch);
if(score.getParts().at(channel)->getInstruments().size() > 1 ||
score.getParts().at(channel)->getInstruments().at(0)->isPercussion()) {
if(note.getInstrument() != nullptr) {
_msg.at(1) = note.getInstrument()->getUnpitched();
if(chord.bottom()->getInstrument() != nullptr) {
_msg.at(1) = chord.bottom()->getInstrument()->getUnpitched();
}
}
nl.emplace_back(_msg);
Expand Down
39 changes: 25 additions & 14 deletions main/music/Measure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ namespace autoplay {

unsigned int Measure::length() const {
unsigned int length = 0;
for(const Note& n : m_notes) {
length += n.getDuration();
for(const Chord& c : m_notes) {
if(!c.empty()) {
length += c.getDuration();
}
}
return length;
}
Expand All @@ -50,32 +52,33 @@ namespace autoplay {
me->setBPM(m_bpm);
MeasureList res = {me};
if(isOverflowing()) {
for(const Note& n : m_notes) {
for(const Chord& c : m_notes) {
std::shared_ptr<Measure> m = res.back();
unsigned int m_len = m->length();
if(m_len + n.getDuration() <= m->max_length()) {
m->m_notes.emplace_back(n);
} else if(m_len == m->max_length()) {
if(m_len + c.getDuration() <= m->max_length()) {
m->m_notes.emplace_back(c);
} else if(m_len == m->max_length() && c.getDuration() < m->max_length()) {
res.emplace_back(std::make_shared<Measure>(m_clef, m_time, m_divisions, m_fifths));
res.back()->setBPM(m_bpm);
res.back()->m_notes.emplace_back(n);
res.back()->m_notes.emplace_back(c);
} else {
// Create new notes
std::vector<Note> v = {};
std::vector<Chord> v = {};

unsigned int da = m->max_length() - m_len;
Note a{n};

Chord a{c};
a.setDuration(da);
v.emplace_back(a);

unsigned int db = n.getDuration() - da;
unsigned int db = c.getDuration() - da;
while(db > m->max_length()) {
Note b{n};
Chord b{c};
b.setDuration(m->max_length());
v.emplace_back(b);
db -= m->max_length();
}
Note b{n};
Chord b{c};
b.setDuration(db);
v.emplace_back(b);

Expand Down Expand Up @@ -103,17 +106,25 @@ namespace autoplay {
}

MeasureList Measure::operator+(const Measure& rhs) {
for(const Note& n : rhs.m_notes) {
*this += n;
for(const Chord& c : rhs.m_notes) {
*this += c;
}
return this->measurize();
}

Measure* Measure::operator+(const Note& rhs) {
Chord chord{rhs};
this->m_notes.emplace_back(chord);
return this;
}

Measure* Measure::operator+(const Chord& rhs) {
this->m_notes.emplace_back(rhs);
return this;
}

void Measure::operator+=(const Note& rhs) { *this + rhs; }

void Measure::operator+=(const Chord& rhs) { *this + rhs; }
}
}
33 changes: 26 additions & 7 deletions main/music/Measure.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,29 +166,48 @@ namespace autoplay {
*/
void operator+=(const Note& rhs);

/**
* Add a Chord to this Measure
* @param rhs The Chord to add
* @return This Measure, after the note was added
*/
Measure* operator+(const Chord& rhs);

/**
* Add a Chord respectively to this Measure
* @param rhs The Chord to add
*/
void operator+=(const Chord& rhs);

/**
* Append a Note to the Measure. Alias of operator+
* @param n The Note to append.
*/
inline void append(const Note& n) { *this += n; }

/**
* Get the last Note of the Measure by reference
* Append a Chord to the Measure. Alias of operator+
* @param n The Chord to append.
*/
inline void append(const Chord& n) { *this += n; }

/**
* Get the last Chord of the Measure by reference
* @return The back of the m_notes vector
*/
inline Note& back() { return m_notes.back(); }
inline Chord& back() { return m_notes.back(); }

/**
* Fetches all Notes from this Measure
* Fetches all Chords from this Measure
* @return A vector, containing all Notes.
*/
inline std::vector<Note> getNotes() const { return m_notes; }
inline std::vector<Chord> getNotes() const { return m_notes; }

/**
* Fetches all Notes from this Measure by reference
* Fetches all Chords from this Measure by reference
* @return A vector, containing all Notes.
*/
inline std::vector<Note>& getNotes() { return m_notes; }
inline std::vector<Chord>& getNotes() { return m_notes; }

/**
* Sets the BPM. It uses the time as reference;
Expand All @@ -208,7 +227,7 @@ namespace autoplay {
Clef m_clef; ///< The clef of the current measure
std::pair<uint8_t, uint8_t> m_time; ///< The time of the measure, in the form of <beats, beat-type>

std::vector<Note> m_notes; ///< The Notes of this Measure
std::vector<Chord> m_notes; ///< The Notes of this Measure

int m_divisions; ///< The divisions value (amount of duration per quarter Note) for this Measure
int m_bpm; ///< The amount of beats per minute (expressed wrt the time signature)
Expand Down
89 changes: 51 additions & 38 deletions main/music/Note.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,15 +293,46 @@ namespace autoplay {
return (fill && !boost::algorithm::ends_with(m_head, "-empty"));
}

std::vector<Note> Note::splitByDivisions(const int& divisions, bool generatedots) const {
std::vector<Note> ref;
std::string Note::getType(const int& divisions) const {
float dur = (std::pow(2.0f, std::floor(std::log2f(m_duration))) / (float)divisions) / 4.0f;
auto nearby = [](float val, float ref) -> bool { return val < ref + 0.0000001 && val > ref - 0.0000001; };
if(nearby(dur, 1.0f / 256.0f)) {
return "256th";
} else if(nearby(dur, 1.0f / 128.0f)) {
return "128th";
} else if(nearby(dur, 1.0f / 64.0f)) {
return "64th";
} else if(nearby(dur, 1.0f / 32.0f)) {
return "32nd";
} else if(nearby(dur, 1.0f / 16.0f)) {
return "16th";
} else if(nearby(dur, 1.0f / 8.0f)) {
return "eighth";
} else if(nearby(dur, 1.0f / 4.0f)) {
return "quarter";
} else if(nearby(dur, 1.0f / 2.0f)) {
return "half";
} else if(nearby(dur, 1.0f)) {
return "whole";
} else if(nearby(dur, 2.0f)) {
return "breve";
} else if(nearby(dur, 4.0f)) {
return "long";
}
throw std::runtime_error("Division value (" + std::to_string((int)divisions) +
") gave an unknown duration of " + std::to_string(dur) + ", which came from " +
std::to_string(m_duration) + ".");
}

std::vector<Chord> Chord::splitByDivisions(const int& divisions, bool generatedots) const {
std::vector<Chord> ref;

if(m_duration == 0) {
throw std::runtime_error("Duration of Note is 0!");
if(getDuration() == 0) {
throw std::runtime_error("Duration of Chord is 0!");
}

float rem = 0.0f;
float T = (float)m_duration / (4.0f * divisions);
float T = (float)getDuration() / (4.0f * divisions);
int n = 1;

auto nearby = [](float val, float ref) -> bool { return val < ref + 0.00000001 && val > ref - 0.00000001; };
Expand All @@ -312,11 +343,11 @@ namespace autoplay {
float _n = std::log2(T) + 8;
n = (int)std::floor(_n);
rem = (float)std::pow(2.0f, n - 8);
Note note{*this};
note.setDuration((unsigned int)(rem * divisions * 4));
note.setTieStart();
note.setTieEnd();
ref.emplace_back(note);
Chord chord{*this};
chord.setDuration((unsigned int)(rem * divisions * 4));
chord.setTieStart();
chord.setTieEnd();
ref.emplace_back(chord);

if(nearby(_n - n, 0.0f)) {
break;
Expand Down Expand Up @@ -350,35 +381,17 @@ namespace autoplay {
return ref;
}

std::string Note::getType(const int& divisions) const {
float dur = (std::pow(2.0f, std::floor(std::log2f(m_duration))) / (float)divisions) / 4.0f;
auto nearby = [](float val, float ref) -> bool { return val < ref + 0.0000001 && val > ref - 0.0000001; };
if(nearby(dur, 1.0f / 256.0f)) {
return "256th";
} else if(nearby(dur, 1.0f / 128.0f)) {
return "128th";
} else if(nearby(dur, 1.0f / 64.0f)) {
return "64th";
} else if(nearby(dur, 1.0f / 32.0f)) {
return "32nd";
} else if(nearby(dur, 1.0f / 16.0f)) {
return "16th";
} else if(nearby(dur, 1.0f / 8.0f)) {
return "eighth";
} else if(nearby(dur, 1.0f / 4.0f)) {
return "quarter";
} else if(nearby(dur, 1.0f / 2.0f)) {
return "half";
} else if(nearby(dur, 1.0f)) {
return "whole";
} else if(nearby(dur, 2.0f)) {
return "breve";
} else if(nearby(dur, 4.0f)) {
return "long";
std::shared_ptr<Note> Chord::bottom() const {
if(empty()) {
return nullptr;
}
throw std::runtime_error("Division value (" + std::to_string((int)divisions) +
") gave an unknown duration of " + std::to_string(dur) + ", which came from " +
std::to_string(m_duration) + ".");
unsigned int idx = 0;
for(unsigned int i = 0; i < m_notes.size(); ++i) {
if(m_notes.at(i)->getPitch() < m_notes.at(idx)->getPitch()) {
idx = i;
}
}
return m_notes.at(idx);
}
}
}
Loading

0 comments on commit 457b05a

Please sign in to comment.