2013-08-01 23:52:09 +02:00
|
|
|
#include "importmidi_swing.h"
|
|
|
|
#include "libmscore/score.h"
|
|
|
|
#include "libmscore/chordrest.h"
|
|
|
|
#include "libmscore/stafftext.h"
|
|
|
|
#include "libmscore/element.h"
|
|
|
|
#include "libmscore/segment.h"
|
|
|
|
#include "libmscore/measure.h"
|
|
|
|
#include "libmscore/staff.h"
|
|
|
|
#include "libmscore/tuplet.h"
|
2013-08-02 13:43:43 +02:00
|
|
|
#include "libmscore/fraction.h"
|
2013-08-01 23:52:09 +02:00
|
|
|
|
|
|
|
|
|
|
|
namespace Ms {
|
|
|
|
namespace Swing {
|
|
|
|
|
2013-08-02 13:43:43 +02:00
|
|
|
class SwingDetector
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SwingDetector(MidiOperation::Swing st);
|
|
|
|
|
|
|
|
void add(ChordRest *cr);
|
|
|
|
bool wasSwingApplied() const { return swingApplied; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<ChordRest *> elements;
|
|
|
|
Fraction sumLen;
|
|
|
|
const Fraction FULL_LEN = Fraction(1, 4);
|
|
|
|
MidiOperation::Swing swingType;
|
|
|
|
bool swingApplied = false;
|
|
|
|
|
|
|
|
void reset();
|
|
|
|
void append(ChordRest *cr);
|
|
|
|
void checkNormalSwing();
|
|
|
|
void checkShuffle();
|
|
|
|
void applySwing();
|
|
|
|
bool areAllTuplets() const;
|
|
|
|
bool areAllNonTuplets() const;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-08-01 23:52:09 +02:00
|
|
|
SwingDetector::SwingDetector(MidiOperation::Swing st)
|
|
|
|
: swingType(st)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwingDetector::add(ChordRest *cr)
|
|
|
|
{
|
|
|
|
if (elements.empty()) {
|
|
|
|
if (cr->globalDuration() >= FULL_LEN)
|
|
|
|
return;
|
|
|
|
int tickInBar = cr->tick() - cr->measure()->tick();
|
|
|
|
if (tickInBar % MScore::division == 0)
|
|
|
|
append(cr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (sumLen + cr->globalDuration() > FULL_LEN) {
|
|
|
|
reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
append(cr);
|
|
|
|
if (sumLen == FULL_LEN) {
|
|
|
|
// check for swing patterns
|
|
|
|
switch (swingType) {
|
|
|
|
case MidiOperation::Swing::SWING:
|
|
|
|
checkNormalSwing();
|
|
|
|
break;
|
|
|
|
case MidiOperation::Swing::SHUFFLE:
|
|
|
|
checkShuffle();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwingDetector::reset()
|
|
|
|
{
|
|
|
|
elements.clear();
|
|
|
|
sumLen = Fraction(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwingDetector::append(ChordRest *cr)
|
|
|
|
{
|
|
|
|
if (cr->type() == Element::CHORD || cr->type() == Element::REST) {
|
|
|
|
elements.push_back(cr);
|
|
|
|
sumLen += cr->globalDuration();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwingDetector::checkNormalSwing()
|
|
|
|
{
|
|
|
|
if (elements.size() == 2
|
|
|
|
&& areAllTuplets()
|
|
|
|
&& (elements[0]->type() == Element::CHORD
|
|
|
|
|| elements[1]->type() == Element::CHORD)
|
|
|
|
&& elements[0]->duration().reduced() == Fraction(1, 4)
|
|
|
|
&& elements[1]->duration().reduced() == Fraction(1, 8))
|
|
|
|
{
|
|
|
|
// swing with two 8th notes
|
|
|
|
// or 8th note + 8th rest
|
|
|
|
// or 8th rest + 8th note
|
|
|
|
applySwing();
|
|
|
|
}
|
|
|
|
else if (elements.size() == 3
|
|
|
|
&& elements[0]->type() == Element::CHORD
|
|
|
|
&& elements[1]->type() == Element::REST
|
|
|
|
&& elements[2]->type() == Element::CHORD
|
|
|
|
&& elements[0]->duration().reduced() == Fraction(1, 8)
|
|
|
|
&& elements[1]->duration().reduced() == Fraction(1, 8)
|
|
|
|
&& elements[2]->duration().reduced() == Fraction(1, 8))
|
|
|
|
{
|
|
|
|
// swing with two 8th notes
|
|
|
|
applySwing();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwingDetector::checkShuffle()
|
|
|
|
{
|
|
|
|
if (elements.size() == 2
|
|
|
|
&& areAllNonTuplets()
|
|
|
|
&& elements[0]->type() == Element::CHORD
|
|
|
|
&& (elements[1]->type() == Element::CHORD
|
|
|
|
|| elements[1]->type() == Element::REST)
|
|
|
|
&& elements[0]->duration().reduced() == Fraction(3, 16) // dotted 8th
|
|
|
|
&& elements[1]->duration().reduced() == Fraction(1, 16))
|
|
|
|
{
|
|
|
|
// swing with two 8th notes
|
|
|
|
// or 8th note + 8th rest
|
|
|
|
applySwing();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwingDetector::applySwing()
|
|
|
|
{
|
2013-08-04 11:55:32 +02:00
|
|
|
if (elements.size() != 2 && elements.size() != 3)
|
|
|
|
return;
|
|
|
|
|
2013-08-01 23:52:09 +02:00
|
|
|
Tuplet *tuplet = nullptr;
|
|
|
|
for (ChordRest *el: elements) {
|
|
|
|
el->setDurationType(TDuration::V_EIGHT);
|
2013-08-04 11:55:32 +02:00
|
|
|
el->setDuration(Fraction(1, 8));
|
2013-08-01 23:52:09 +02:00
|
|
|
el->setDots(0);
|
|
|
|
if (el->tuplet()) {
|
|
|
|
if (!tuplet)
|
|
|
|
tuplet = el->tuplet();
|
|
|
|
el->setTuplet(nullptr);
|
|
|
|
}
|
|
|
|
}
|
2013-08-04 11:55:32 +02:00
|
|
|
if (elements.size() == 3) {
|
|
|
|
// remove central rest
|
|
|
|
ChordRest *cr = elements[1];
|
|
|
|
cr->score()->removeElement(cr);
|
2013-08-08 13:55:35 +02:00
|
|
|
delete cr;
|
|
|
|
cr = nullptr;
|
2013-08-04 11:55:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ChordRest *first = elements.front();
|
|
|
|
int startTick = first->segment()->tick();
|
|
|
|
ChordRest *last = elements.back();
|
|
|
|
last->segment()->remove(last);
|
|
|
|
Segment* s = last->measure()->getSegment(last, startTick + MScore::division / 2);
|
|
|
|
s->add(last);
|
|
|
|
|
|
|
|
if (tuplet) {
|
|
|
|
// delete tuplet
|
2013-08-08 13:55:35 +02:00
|
|
|
delete tuplet;
|
|
|
|
tuplet = nullptr;
|
2013-08-04 11:55:32 +02:00
|
|
|
}
|
2013-08-01 23:52:09 +02:00
|
|
|
if (!swingApplied)
|
|
|
|
swingApplied = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SwingDetector::areAllTuplets() const
|
|
|
|
{
|
|
|
|
for (const auto &el: elements) {
|
|
|
|
if (!el->tuplet())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SwingDetector::areAllNonTuplets() const
|
|
|
|
{
|
|
|
|
for (const auto &el: elements) {
|
|
|
|
if (el->tuplet())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
|
|
|
2013-08-04 13:58:39 +02:00
|
|
|
QString swingCaption(MidiOperation::Swing swingType)
|
|
|
|
{
|
|
|
|
QString caption;
|
|
|
|
switch (swingType) {
|
|
|
|
case MidiOperation::Swing::SWING:
|
|
|
|
caption = "Swing";
|
|
|
|
break;
|
|
|
|
case MidiOperation::Swing::SHUFFLE:
|
|
|
|
caption = "Shuffle";
|
|
|
|
break;
|
|
|
|
case MidiOperation::Swing::NONE:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return caption;
|
|
|
|
}
|
|
|
|
|
2013-08-01 23:52:09 +02:00
|
|
|
void detectSwing(Staff *staff, MidiOperation::Swing swingType)
|
|
|
|
{
|
|
|
|
Score *score = staff->score();
|
|
|
|
int strack = staff->idx() * VOICES;
|
|
|
|
SwingDetector swingDetector(swingType);
|
|
|
|
|
|
|
|
for (Segment *seg = score->firstSegment(Segment::SegChordRest); seg;
|
|
|
|
seg = seg->next1(Segment::SegChordRest)) {
|
|
|
|
for (int voice = 0; voice < VOICES; ++voice) {
|
|
|
|
ChordRest *cr = static_cast<ChordRest *>(seg->element(strack + voice));
|
|
|
|
if (!cr)
|
|
|
|
continue;
|
|
|
|
swingDetector.add(cr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (swingDetector.wasSwingApplied()) {
|
|
|
|
// add swing label to the score
|
|
|
|
StaffText* st = new StaffText(score);
|
|
|
|
st->setTextStyleType(TEXT_STYLE_STAFF);
|
2013-08-04 13:58:39 +02:00
|
|
|
st->setText(swingCaption(swingType));
|
2013-08-01 23:52:09 +02:00
|
|
|
Segment *seg = score->firstSegment(Segment::SegChordRest);
|
|
|
|
st->setParent(seg);
|
|
|
|
st->setTrack(strack); // voice == 0
|
|
|
|
score->addElement(st);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Swing
|
|
|
|
} // namespace Ms
|