Skip to content
Snippets Groups Projects
Commit 2f981795 authored by SeeLook's avatar SeeLook
Browse files

Implemented adding notes at the score end with splitting long notes into measures.

Initial implementation of bar line.
parent be400744
Branches
Tags
No related merge requests found
......@@ -21,15 +21,24 @@
#include "tstaffobject.h"
#include "tnoteobject.h"
#include "tnotepair.h"
#include "music/tmeter.h"
#include "music/tnote.h"
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtCore/qdebug.h>
TmeasureObject::TmeasureObject(TscoreObject* parent) :
TmeasureObject::TmeasureObject(int nr, TscoreObject* parent) :
QObject(parent),
m_number(nr),
m_score(parent),
m_staff(nullptr),
m_number(-1) // undefined
m_firstInGr(new qint8[1]),
m_barLine(nullptr)
{
m_duration = m_score->meter()->duration();
m_free = m_duration;
}
......@@ -45,14 +54,36 @@ void TmeasureObject::setStaff(TstaffObject* st) {
}
void TmeasureObject::appendNewNotes(int segmentId, int count) {
// so far we are sure there is enough space for whole note list in this measure
// qDebug() << debug() << "append" << count << "note(s)";
for (int n = segmentId; n < segmentId + count; ++n)
m_notes.append(m_score->noteSegment(n));
updateRhythmicGroups();
// resolve beaming
for (int n = segmentId; n < segmentId + count; ++n) {
auto np = m_score->noteSegment(n);
auto noteObject = new TnoteObject(m_staff);
noteObject->setMeasure(this);
noteObject->setNote(*np->note());
np->setNoteObject(noteObject);
}
m_staff->appendNewNotes(segmentId, count);
if (m_free == 0)
checkBarLine();
}
void TmeasureObject::insertNote(int id, TnotePair* np) {
qDebug() << "[TmeasureObject] inserting note at" << id;
// qDebug() << debug() << "inserting note at" << id;
m_notes.append(np);
updateRhythmicGroups();
if (np->object() == nullptr) {
auto noteObject = new TnoteObject(m_staff);
noteObject->setMeasure(this);
noteObject->setNote(*np->note());
np->setNoteObject(noteObject);
m_notes.append(np);
m_staff->addNote(np);
}
}
......@@ -68,4 +99,56 @@ int TmeasureObject::lastNoteId() const {
}
char TmeasureObject::debug() {
QTextStream o(stdout);
o << " \033[01;33m[" << QString("%1/%2").arg(number() + 1).arg(m_staff ? m_staff->number() + 1 : -1) << " MEASURE]\033[01;00m";
return 32; // fake
}
//#################################################################################################
//################### PROTECTED ############################################
//#################################################################################################
void TmeasureObject::updateRhythmicGroups() {
if (duration() == 0)
return;
int notePos = 0, grNr = 0, currGr = 0;
delete[] m_firstInGr;
m_firstInGr = new qint8[m_score->groupCount()];
m_firstInGr[0] = 0; // first note in measure also begins first rhythmic group
for (int i = 0; i < m_notes.size(); ++i) {
if (currGr != grNr) {
m_firstInGr[grNr] = i;
currGr = grNr;
}
m_notes[i]->setRhythmGroup(grNr);
notePos += m_notes[i]->note()->duration();
while (grNr < m_score->groupCount() && notePos >= m_score->groupPos(grNr))
grNr++;
}
if (currGr < m_score->groupCount() - 1) { // fill the rest of the array
for (int gr = currGr + 1; gr < m_score->groupCount(); ++gr)
m_firstInGr[gr] = -1; // with '-1' - means no notes yet
}
m_free = m_duration - notePos;
// qDebug() << debug() << "Updating rhythmic groups" << m_free;
}
void TmeasureObject::checkBarLine() {
if (!m_barLine) {
QQmlEngine engine;
QQmlComponent comp(&engine, this);
comp.setData("import QtQuick 2.7; Rectangle { width: 0.3; height: 8 }", QUrl());
m_barLine = qobject_cast<QQuickItem*>(comp.create());
}
auto lastNote = last()->object();
m_barLine->setParentItem(m_staff->staffItem());
m_barLine->setProperty("color", lastNote->color());
m_barLine->setX(lastNote->rightX() - 0.6);
m_barLine->setY(m_staff->upperLine());
// qDebug() << debug() << "check barline";
}
......@@ -24,6 +24,7 @@
#include <QtCore/qobject.h>
class QQuickItem;
class TscoreObject;
class TstaffObject;
class TnoteObject;
......@@ -39,11 +40,27 @@ class NOOTKACORE_EXPORT TmeasureObject : public QObject
public:
explicit TmeasureObject(TscoreObject* parent = nullptr);
explicit TmeasureObject(int nr = -1, TscoreObject* parent = nullptr);
int number() const { return m_number; }
void setNumber(int nr);
/**
* Actual duration of all notes in the measure
*/
int duration() const { return m_duration; }
/**
* Free 'rhythm space' remained in the measure
*/
int free() const { return m_free; }
/**
* Adds list of @p count notes at the measure end starting from @p segmentId note number in the score
* Measure has to have already space for the whole list!
*/
void appendNewNotes(int segmentId, int count);
void insertNote(int id, TnotePair* np);
TscoreObject* score() { return m_score; }
......@@ -64,12 +81,28 @@ public:
*/
int lastNoteId() const;
/**
* Prints debug message with [number MEASURE]
*/
char debug();
protected:
/**
* Sets appropriate @p setRhythmGroup of every note in the measure.
*/
void updateRhythmicGroups();
void checkBarLine();
private:
int m_number;
int m_duration;
int m_id;
TscoreObject *m_score;
TstaffObject *m_staff;
int m_number;
int m_free;
QList<TnotePair*> m_notes;
qint8 *m_firstInGr; /**< qint8 is sufficient - measure never has more than 127 notes */
QQuickItem *m_barLine;
};
#endif // TMEASUREOBJECT_H
......@@ -24,6 +24,7 @@
#include <QtQml/qqmlengine.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpalette.h>
#include <QtCore/qdebug.h>
......@@ -146,13 +147,17 @@ void TnoteObject::setColor(const QColor& c) {
* - width
*/
void TnoteObject::setNote(const Tnote& n) {
bool updateHead = n.rhythm() != m_note->rhythm() || n.isRest() != m_note->isRest();
bool updateHead = n.rhythm() != m_note->rhythm() || n.isRest() != m_note->isRest() || n.hasDot() != m_note->hasDot();
bool updateStem = updateHead || (n.rtm.beam() != Trhythm::e_noBeam) != (m_note->rtm.beam() != Trhythm::e_noBeam);
*m_note = n;
if (updateHead)
m_head->setProperty("text", getHeadText());
if (updateHead) {
QString headText = getHeadText();
if (m_note->hasDot())
headText.append(QStringLiteral("\ue1e7"));
m_head->setProperty("text", headText);
}
if (m_note->isRest())
m_notePosY = staff()->upperLine() + (m_note->rhythm() == Trhythm::Whole ? 2.0 : 4.0);
......@@ -201,7 +206,7 @@ void TnoteObject::setNote(const Tnote& n) {
m_bg->setY(m_stem->y() - (m_note->rtm.stemDown() ? 3.25 : 1.25));
m_bg->setWidth(width());
qDebug() << "[TnoteObject] set note" << m_note->toText() << m_note->rtm.string() << "note pos" << m_notePosY << "width:" << width();
qDebug() << debug() << "set note" << m_note->toText() << m_note->rtm.string() << "note pos" << m_notePosY << "width:" << width();
}
......@@ -224,6 +229,13 @@ qreal TnoteObject::rhythmFactor() {
}
char TnoteObject::debug() {
QTextStream o(stdout);
o << " \033[01;29m[NOTE" << index() << "]\033[01;00m";
return 32; // fake
}
//#################################################################################################
//################### PROTECTED ############################################
//#################################################################################################
......
......@@ -29,6 +29,9 @@ class TmeasureObject;
class Tnote;
/**
* @class TnoteObject is @class QQuickItem derivative representing single note on the score
*/
class NOOTKACORE_EXPORT TnoteObject : public QQuickItem
{
......@@ -78,6 +81,11 @@ public:
*/
qreal rhythmFactor();
/**
* Prints to std out debug info about this note: [NOTE number] in color
*/
char debug();
signals:
void notePosYchanged();
......
......@@ -20,6 +20,9 @@
#define TNOTEPAIR_H
#include <QtCore/qobject.h>
class Tnote;
class TnoteObject;
......@@ -37,9 +40,17 @@ public:
void setNoteObject(TnoteObject* ob);
/**
* Number of rhythmical group in the measure, -1 (undefined) by default
*/
qint8 rhythmGroup() { return m_group; }
void setRhythmGroup(qint8 g) { m_group = g; }
private:
Tnote *m_note;
TnoteObject *m_noteObj;
qint8 m_group = -1;
};
#endif // TNOTEPAIR_H
......@@ -22,8 +22,9 @@
#include "tnotepair.h"
#include "music/tmeter.h"
#include "music/tnote.h"
#include <QtCore/qdebug.h>
#include <QtCore/qdebug.h>
#include "checktime.h"
TscoreObject::TscoreObject(QObject* parent) :
......@@ -33,7 +34,7 @@ TscoreObject::TscoreObject(QObject* parent) :
{
m_meter = new Tmeter(Tmeter::Meter_4_4);
setMeter(4); // Tmeter::Meter_4_4
m_measures << new TmeasureObject(this);
m_measures << new TmeasureObject(0, this);
}
......@@ -50,12 +51,68 @@ void TscoreObject::setParent(QObject* p) {
}
/** @p static
* This method creates @p outList of notes that have pitch of @p n note
* but splits @p dur duration into possible rhythmic values.
*/
void solveList(const Tnote& n, int dur, QList<Tnote>& outList) {
// TODO: add ties
Trhythm rtm(dur); // try to obtain rhythm value
if (!rtm.isValid()) { // if it is not possible to express the duration in single rhythm - resolve it in multiple values
TrhythmList solvList;
Trhythm::resolve(dur, solvList);
for (int r = 0; r < solvList.count(); ++r)
outList << Tnote(n, Trhythm(solvList[r].rhythm(), n.isRest(), solvList[r].hasDot(), solvList[r].isTriplet()));
} else { // just single note in the list
rtm.setRest(n.isRest());
outList << Tnote(n, rtm);
}
}
void TscoreObject::addNote(const Tnote& n) {
CHECKTIME (
auto lastMeasure = m_measures.last();
if (lastMeasure->free() == 0) {
lastMeasure = new TmeasureObject(m_measures.count(), this);
lastMeasure->setStaff(lastStaff());
m_measures << lastMeasure;
}
int noteDur = n.duration();
if (noteDur > lastMeasure->free()) { // split note that is adding
int leftDuration = noteDur - lastMeasure->free();
QList<Tnote> notesToCurrent;
QList<Tnote> notesToNext;
int lastNoteId = m_segments.count();
solveList(n, lastMeasure->free(), notesToCurrent); // solve free duration in current measure
if (notesToCurrent.isEmpty())
qDebug() << "[TscoreObject] can't resolve duration of" << lastMeasure->free();
else {
appendNoteList(notesToCurrent);
lastMeasure->appendNewNotes(lastNoteId, notesToCurrent.count());
}
solveList(n, leftDuration, notesToNext); // solve remaining duration for the next measure
lastNoteId = m_segments.count(); // update id of the last note segment
if (notesToNext.isEmpty())
qDebug() << "[TscoreObject] can't resolve duration" << leftDuration;
else {
appendNoteList(notesToNext);
auto newLastMeasure = new TmeasureObject(m_measures.count(), this); // add a new measure
newLastMeasure->setStaff(lastStaff());
m_measures << newLastMeasure;
newLastMeasure->appendNewNotes(lastNoteId, notesToNext.count());
}
} else { // just add new note to the measure
m_notes << n;
int lastNoteId = m_segments.count();
auto newSeg = new TnotePair(&m_notes.last());
m_segments << newSeg;
auto lastMeasure = m_measures.last();
lastMeasure->insertNote(lastMeasure->lastNoteId() - lastMeasure->firstNoteId(), newSeg);
lastMeasure->appendNewNotes(lastNoteId, 1);
}
)
}
......@@ -120,3 +177,16 @@ void TscoreObject::addStaff(TstaffObject* st) {
if (st->number() == 0) // initialize first measure staff
m_measures.first()->setStaff(st);
}
//#################################################################################################
//################### PRIVATE ############################################
//#################################################################################################
void TscoreObject::appendNoteList(QList<Tnote>& l) {
for (Tnote n : l) {
m_notes << n;
m_segments << new TnotePair(&m_notes.last());
}
}
......@@ -77,6 +77,14 @@ public:
void setMeter(int m);
int meterToInt();
TnotePair* noteSegment(int id) { return m_segments[id]; }
TnotePair* firstSegment() { return m_segments.first(); }
TnotePair* lastSegment() { return m_segments.last(); }
TstaffObject* staff(int id) { return m_staves[id]; }
TstaffObject* firstStaff() { return m_staves.first(); }
TstaffObject* lastStaff() { return m_staves.last(); }
/**
* Returns duration of given @param grNr group starting from measure beginning
* Describes grouping (beaming - beam connections) of notes in a single measure for current meter.
......@@ -101,6 +109,13 @@ protected:
TclefOffset clefOffset() const { return m_clefOffset; }
private:
/**
* Appends notes to @p m_notes list, creates corresponding @p TnotePair
* and adds them to @p m_segments list
*/
void appendNoteList(QList<Tnote>& l);
private:
Tmeter *m_meter;
bool m_keySignEnabled;
......
......@@ -36,14 +36,14 @@ TstaffObject::TstaffObject(QObject* parent) :
TstaffObject::~TstaffObject() {
qDebug() << "[TstaffObject] is going delete";
qDebug() << debug() << "is going delete";
}
void TstaffObject::setScore(TscoreObject* s) {
m_score = s;
setParent(s);
qDebug() << "TstaffObject got a score parent" << s;
qDebug() << debug() << "got a score parent" << s;
m_score->addStaff(this);
}
......@@ -55,9 +55,19 @@ void TstaffObject::addNote(TnotePair* np) {
}
void TstaffObject::appendNewNotes(int segmentId, int count) {
for (int n = segmentId; n < segmentId + count; ++n) {
auto np = m_score->noteSegment(n);
np->object()->setIndex(m_notes.count());
m_notes.append(np);
}
fit();
}
void TstaffObject::setNote(int noteNr, const Tnote& n) {
if (noteNr < 0 || noteNr >= m_notes.count()) {
qDebug() << "[TstaffObject] There is no note with number" << noteNr;
qDebug() << debug() << "There is no note with number" << noteNr;
return;
}
// m_notes[noteNr]->setNote(n);
......@@ -93,13 +103,20 @@ void TstaffObject::setNotesIndent(qreal ni) {
}
char TstaffObject::debug() {
QTextStream o(stdout);
o << "\033[01;34m[" << number() + 1 << " STAFF]\033[01;00m";
return 32; // fake
}
//#################################################################################################
//################### PROTECTED ############################################
//#################################################################################################
void TstaffObject::fit() {
if (m_notes.isEmpty()) {
qDebug() << "[TstaffObject] Empty staff - nothing to fit";
qDebug() << debug() << "Empty staff - nothing to fit";
return;
}
......@@ -124,7 +141,7 @@ void TstaffObject::fit() {
void TstaffObject::updateNotesPos(int startId) {
qDebug() << "updating notes positions from" << startId;
qDebug() << debug() << "updating notes positions from" << startId;
if (startId == 0)
m_notes[0]->object()->setX(m_notesIndent);
......
......@@ -55,6 +55,11 @@ public:
void addNote(TnotePair* np);
/**
* Adds list of @p count notes at the staff end starting from @p segmentId note number in the score
*/
void appendNewNotes(int segmentId, int count);
void setNote(int noteNr, const Tnote& n);
/**
......@@ -88,6 +93,11 @@ public:
*/
qreal gapFactor() { return m_gapFactor; }
/**
* Prints to std out debug info about this staff: [nr STAFF] in color
*/
char debug();
signals:
void upperLineChanged();
void firstMeasureNrChanged();
......
......@@ -53,7 +53,9 @@ bool initCoreLibrary() {
Tcore::androidStyle = QStyleFactory::create(QStringLiteral("android"));
#endif
Trhythm::initialize();
Tcolor::setShadow(qApp->palette());
#if defined(Q_OS_MAC)
QDir dir(qApp->applicationDirPath());
dir.cdUp();
......
......@@ -81,12 +81,6 @@ ApplicationWindow {
width: parent.width
height: parent.height - header.height - instrument.height
enableKeySign: true
Component.onCompleted: {
for (var n = 1; n < 8; ++n) {
addNote(Noo.note(1 + Math.random() * 7, -2 + Math.random() * 5, Math.min(Math.max(-2, -3 + Math.random() * 6), 2),
3 + Math.random() * 3))
}
}
}
Instrument {
......@@ -97,11 +91,20 @@ ApplicationWindow {
}
}
// Component.onCompleted: {
// for (var n = 1; n < 8; ++n) {
// score.addNote(Noo.note(1 + Math.random() * 7, -2 + Math.random() * 5, Math.min(Math.max(-2, -3 + Math.random() * 6), 2),
// 2 + Math.random() * 4))
// }
// }
// Timer {
// interval: 1000
// interval: 5000
// running: true
// repeat: true
// onTriggered: {
// score.addNote(Noo.note(1 + Math.random() * 7, -2 + Math.random() * 5, Math.min(Math.max(-2, -3 + Math.random() * 6), 2),
// 2 + Math.random() * 4))
// var noteNr = Math.random() * 7
// var rest = Math.floor((Math.random() * 100) % 2)
// var accid = rest ? 0 : Math.min(Math.max(-2, -3 + Math.random() * 6), 2)
......@@ -116,11 +119,13 @@ ApplicationWindow {
// }
// }
function randNotes() {
var noteNr = Math.random() * 7
var rest = Math.floor((Math.random() * 100) % 2)
var accid = rest ? 0 : Math.min(Math.max(-2, -3 + Math.random() * 6), 2)
var note = Noo.note(1 + Math.random() * 7, -3 + Math.random() * 7, accid, 1 + Math.random() * 5, rest)
score.setNote(0, noteNr, note)
score.addNote(Noo.note(1 + Math.random() * 7, -2 + Math.random() * 5, Math.min(Math.max(-2, -3 + Math.random() * 6), 2),
2 + Math.random() * 4))
// var noteNr = Math.random() * 7
// var rest = Math.floor((Math.random() * 100) % 2)
// var accid = rest ? 0 : Math.min(Math.max(-2, -3 + Math.random() * 6), 2)
// var note = Noo.note(1 + Math.random() * 7, -3 + Math.random() * 7, accid, 1 + Math.random() * 5, rest)
// score.setNote(0, noteNr, note)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment