Newer
Older
/***************************************************************************
* Copyright (C) 2017-2018 by Tomasz Bojczuk *
* seelook@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "tnootkaqml.h"
#include "qtr.h"
#include "nootkaconfig.h"
#include "tglobals.h"
#include "tpath.h"
#include "music/tkeysignature.h"
#include "music/tnamestylefilter.h"
#include "score/tnotepair.h"
#include "score/tscoreobject.h"
#include "score/tstaffitem.h"
#include "score/tnoteitem.h"
#include "score/tstafflines.h"
#include "instruments/tguitarbg.h"
#include "instruments/tpianobg.h"
#include "instruments/tbandoneonbg.h"
SeeLook
committed
#include "instruments/tsaxbg.h"
SeeLook
committed
#include "taction.h"
#include "music/ttuneobject.h"
#include "tmtr.h"
#include "tcolor.h"
#include <QtQml/qqmlengine.h>
SeeLook
committed
#include <QtCore/qfileinfo.h>
SeeLook
committed
#include <QtCore/qdatetime.h>
#include <QtCore/qbuffer.h>
#include <QtWidgets/qapplication.h>
#include <QtGui/qdesktopservices.h>
SeeLook
committed
#if defined (Q_OS_ANDROID)
#include "Android/tfiledialog.h"
#include "Android/tmobilemenu.h"
#else
#include <QtWidgets/qfiledialog.h>
#endif
#include <QtCore/qdebug.h>
TnootkaQML* TnootkaQML::m_instance = nullptr;
TnootkaQML::TnootkaQML(QObject* parent) :
QObject(parent)
{
if (m_instance) {
qDebug() << "TnootkaQML instance already exists!";
return;
}
m_instance = this;
SeeLook
committed
qsrand(QDateTime::currentDateTime().toTime_t());
qRegisterMetaType<Tclef>();
qmlRegisterUncreatableType<Tclef>("Score", 1, 0, "Tclef", QStringLiteral("You cannot create an instance of the Tclef."));
qmlRegisterUncreatableType<Tmeter>("Score", 1, 0, "Tmeter", QStringLiteral("You cannot create an instance of the Tmeter."));
qmlRegisterUncreatableType<Trhythm>("Score", 1, 0, "Trhythm", QStringLiteral("You cannot create an instance of the Trhythm."));
qRegisterMetaType<Trhythm>();
qmlRegisterType<TscoreObject>("Score", 1, 0, "TscoreObject");
qmlRegisterType<TstaffItem>("Score", 1, 0, "TstaffItem");
qmlRegisterType<TnoteItem>("Score", 1, 0, "TnoteItem");
qmlRegisterType<TstaffLines>("Score", 1, 0, "TstaffLines");
qmlRegisterType<TaddNoteItem>("Score", 1, 0, "TaddNoteItem");
qmlRegisterUncreatableType<TcommonInstrument>("Nootka", 1, 0, "TcommonInstrument", QStringLiteral("You cannot create an instance of the TcommonInstrument."));
qmlRegisterType<TguitarBg>("Nootka", 1, 0, "TguitarBg");
qmlRegisterType<TpianoBg>("Nootka", 1, 0, "TpianoBg");
qmlRegisterType<TbandoneonBg>("Nootka", 1, 0, "TbandoneonBg");
SeeLook
committed
qmlRegisterType<TsaxBg>("Nootka", 1, 0, "TsaxBg");
SeeLook
committed
qmlRegisterType<Taction>("Nootka", 1, 0, "Taction");
SeeLook
committed
#if defined (Q_OS_ANDROID)
qmlRegisterType<TmobileMenu>("Nootka", 1, 0, "TmobileMenu");
#endif
SeeLook
committed
qmlRegisterUncreatableType<TnootkaQML>("Nootka", 1, 0, "Nootka", QStringLiteral("You cannot create an instance of the TnootkaQML."));
qRegisterMetaType<Tinstrument>();
qmlRegisterUncreatableType<Tinstrument>("Nootka", 1, 0, "Tinstrument", QStringLiteral("You cannot create an instance of the Tinstrument."));
qRegisterMetaType<Ttune>();
qmlRegisterUncreatableType<Ttune>("Nootka", 1, 0, "Ttune", QStringLiteral("You cannot create an instance of the Ttune."));
qmlRegisterType<TtuneObject>("Nootka", 1, 0, "TtuneObject");
TnootkaQML::~TnootkaQML()
{
m_instance = nullptr;
}
//#################################################################################################
//################### INVOKABLE METHODS ############################################
//#################################################################################################
QString TnootkaQML::version() { return NOOTKA_VERSION; }
Tclef TnootkaQML::clef(int type) {
return Tclef(static_cast<Tclef::EclefType>(type));
}
Tmeter TnootkaQML::meter(int m) {
return Tmeter(static_cast<Tmeter::Emeter>(m));
}
Tnote TnootkaQML::note(int pitch, int octave, int alter, int rhythm, bool rest, bool dot) {
return Tnote(static_cast<char>(pitch), static_cast<char>(octave), static_cast<char>(alter),
Trhythm(static_cast<Trhythm::Erhythm>(rhythm), rest, dot, false));
}
Tnote TnootkaQML::note(const Tnote& n, int rhythm, bool rest, bool dot) {
return Tnote(n, Trhythm(static_cast<Trhythm::Erhythm>(rhythm), rest, dot, false));
}
Tnote TnootkaQML::note(const Tnote& n, const Trhythm& r) {
return Tnote(n, r);
}
Tnote TnootkaQML::note(int chroma, bool sharp) {
Tnote n(static_cast<short>(chroma));
if (!sharp && (n.alter() != 0 || (n.alter() == 0 && n.note() != 3 && n.note() != 7))) // but skip e and b - otherwise they become fb or cb
n = n.showWithFlat();
return n;
}
Tnote TnootkaQML::transpose(Tnote n, int semitones) {
n.transpose(semitones);
return n;
}
Trhythm TnootkaQML::rhythm(int rtm, bool rest, bool dot, bool triplet) {
return Trhythm(static_cast<Trhythm::Erhythm>(rtm), rest, dot, triplet);
}
/**
* It returns glyphs of 'Nootka' font in contrary to TnoteObject::getHeadText()
*/
QString TnootkaQML::rhythmText(const Trhythm& r) {
if (r.rhythm() == Trhythm::NoRhythm)
return QStringLiteral("z"); // just black note-head
QString out;
if (r.isRest())
out = QString(QChar(0xe106 + static_cast<int>(r.rhythm())));
else
out = QString(QChar(66 + static_cast<int>(r.rhythm())));
if (r.hasDot())
out += QStringLiteral(".");
return out;
}
QString TnootkaQML::noteName(const Tnote& n, int style, bool showOctave) {
// Tnote::toText() method returns only names in user preferred according to settings
// To cheat it and force note name in any given style we are resetting pointer of is7th_B
// then Tnote skips filtering of a style during name generation.
auto tmpPtr = TnameStyleFilter::is7th_B();
TnameStyleFilter::setStyleFilter(nullptr, TnameStyleFilter::solfegeStyle());
auto name = n.toText(static_cast<Tnote::EnameStyle>(style), showOctave);
TnameStyleFilter::setStyleFilter(tmpPtr, TnameStyleFilter::solfegeStyle()); // restore is7th_B settings
return name;
}
QString TnootkaQML::majorKeyName(int key) {
return TkeySignature(static_cast<char>(key)).getMajorName();
}
QString TnootkaQML::minorKeyName(int key) {
return TkeySignature(static_cast<char>(key)).getMinorName();
}
QString TnootkaQML::majAndMinKeyName(int key) {
return majorKeyName(key) + QLatin1String("<br>") + minorKeyName(key);
}
QString TnootkaQML::getLicense() {
QFile file(Tpath::main + QLatin1String("gpl"));
QString license;
QTextStream in;
if (!file.exists()) { // Debian based
QDir d(Tpath::main);
d.cdUp();
file.setFileName(d.path() + QLatin1String("/doc/nootka/copyright"));
}
if(file.open(QFile::ReadOnly | QFile::Text)) {
QTextStream in(&file);
in.setCodec("UTF-8");
license = in.readAll();
}
file.close();
return license;
}
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
QString TnootkaQML::getChanges() {
QFile file(Tpath::main + QLatin1String("changes"));
QString chLog;
QTextStream in;
if(file.open(QFile::ReadOnly | QFile::Text)) {
QTextStream in(&file);
in.setCodec("UTF-8");
QStringList htmlText = in.readAll().replace(QLatin1String(" "), QLatin1String(" ")).split(QLatin1String("\n"));
for (int i = 0; i < htmlText.size(); i++) {
if (htmlText[i].startsWith(QLatin1String("0.")) || htmlText[i].startsWith(QLatin1String("1.")))
htmlText[i] = QLatin1String("<span style=\"font-size: x-large; color: #0000ff\"><b> ") + htmlText[i] + QLatin1String("</b></span>");
else if (htmlText[i].contains(QLatin1String("======")))
htmlText[i] = QStringLiteral("<br><hr><b><big><center>Nootka ONE</big></b></center><hr>");
else if (htmlText[i].contains(QLatin1String("BUG")))
htmlText[i] = QStringLiteral(" <u>BUG FIXES</u>");
else if (htmlText[i].contains(QLatin1String("Under the hood")))
htmlText[i] = QStringLiteral(" <u>Under the hood</u>");
else if (!htmlText[i].contains(QLatin1String(" - ")))
htmlText[i] = QLatin1String("<b>") + htmlText[i] + QLatin1String("</b>");
htmlText[i].append(QLatin1String("<br>"));
}
chLog = htmlText.join(QString());
}
file.close();
return chLog;
}
bool TnootkaQML::isAndroid() {
#if defined (Q_OS_ANDROID)
return true;
#else
return false;
#endif
}
bool TnootkaQML::isWindows() {
#if defined (Q_OS_WIN)
return true;
#else
return false;
#endif
}
bool TnootkaQML::isMac() {
#if defined (Q_OS_MACOS)
return true;
#else
return false;
#endif
}
QStringList TnootkaQML::guitarTunings() {
QStringList tunList;
int start = static_cast<int>(Ttune::Standard_EADGBE);
for (int t = start; t < start + 5; ++t)
tunList << Ttune::definedName(static_cast<Ttune::Etunings>(t));
tunList << QApplication::translate("InstrumentPage", "Custom tuning");
return tunList;
}
QStringList TnootkaQML::bassTunings() {
QStringList tunList;
int start = static_cast<int>(Ttune::Bass4_EADG);
for (int t = start; t < start + 4; ++t)
tunList << Ttune::definedName(static_cast<Ttune::Etunings>(t));
tunList << QApplication::translate("InstrumentPage", "Custom tuning");
return tunList;
}
Ttune TnootkaQML::tuning(int tuningType) {
if (tuningType > -1) {
if (tuningType == 0)
return Ttune::stdTune;
if (tuningType < 5)
return Ttune::tunes[tuningType - 1];
if (tuningType > 99 && tuningType < 104)
return Ttune::bassTunes[tuningType - 100];
}
return Ttune();
}
/**
* When third note @s3 is valid the tuning represents real guitar tuning
* otherwise it is an instrument scale
*/
Ttune TnootkaQML::tuning(const Tnote& s1, const Tnote& s2, const Tnote& s3, const Tnote& s4, const Tnote& s5, const Tnote& s6) {
return Ttune(QApplication::translate("InstrumentPage", "Custom tuning"), s1, s2, s3, s4, s5, s6, s3.isValid() ? Ttune::Custom : Ttune::Scale);
}
Tinstrument TnootkaQML::instr(int type) {
return Tinstrument(static_cast<Tinstrument::Etype>(type < 0 || type > INSTR_COUNT - 1 ? 0 : type));
QString TnootkaQML::getXmlToOpen() {
SeeLook
committed
#if defined (Q_OS_ANDROID)
openFile = TfileDialog::getOpenFileName(nullptr, GLOB->lastXmlDir(), QStringLiteral("xml"));
SeeLook
committed
#else
openFile = QFileDialog::getOpenFileName(nullptr, qApp->translate("TmelMan", "Open melody file"), GLOB->lastXmlDir(),
qApp->translate("TmelMan", "MusicXML file") + QLatin1String(" (*.xml)"));
SeeLook
committed
#endif
if (!openFile.isEmpty())
GLOB->setLastXmlDir(QFileInfo(openFile).absoluteDir().path());
return openFile;
}
QString TnootkaQML::getXmlToSave() {
SeeLook
committed
#if defined (Q_OS_ANDROID)
saveFile = TfileDialog::getSaveFileName(nullptr, GLOB->lastXmlDir(), QStringLiteral("xml"));
SeeLook
committed
#else
saveFile = QFileDialog::getSaveFileName(nullptr, qApp->translate("TmelMan", "Save melody as:"), GLOB->lastXmlDir(),
qTR("TmelMan", "MusicXML file") + QLatin1String(" (*.xml)"));
SeeLook
committed
#endif
if (!saveFile.isEmpty())
GLOB->setLastXmlDir(QFileInfo(saveFile).absoluteDir().path());
return saveFile;
}
QString TnootkaQML::pix(const QString& imageFileName) {
return Tpath::pix(imageFileName);
}
SeeLook
committed
QString TnootkaQML::TR(const QString& context, const QString& text, const QString& disambiguation, int n) {
return qTR(qPrintable(context), qPrintable(text), qPrintable(disambiguation), n);
}
SeeLook
committed
QColor TnootkaQML::alpha(const QColor& c, int a) {
return Tcolor::alpha(c, a);
SeeLook
committed
}
QColor TnootkaQML::randomColor(int alpha, int level) {
return QColor(qrand() % level, qrand() % level, qrand() % level, alpha);
}
QColor TnootkaQML::invert(const QColor& c) {
return Tcolor::invert(c);
}
int TnootkaQML::fontSize() {
#if defined (Q_OS_ANDROID)
return (qApp->font().pixelSize() > 0 ? qApp->font().pixelSize() : qApp->font().pointSize()) * 0.8 * GLOB->guiScale();
#else
return Tmtr::fingerPixels() * 0.45 * GLOB->guiScale();
#endif
}
QString TnootkaQML::pixToHtml(const QString& pixName, int height) {
if (height == 0)
return QString("<img src=\"%1\">").arg(pixName);
QPixmap pix;
if (!pix.load(Tpath::img(qPrintable(pixName))))
return QString();
QByteArray byteArray;
QBuffer buffer(&byteArray);
pix.scaled(qRound(height * (static_cast<qreal>(pix.height()) / static_cast<qreal>(pix.width()))),
height, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(&buffer, "PNG");
return QString("<img src=\"data:image/png;base64,") + byteArray.toBase64() + "\"/>";
}
void TnootkaQML::openHelpLink(const QString& hash) {
QDesktopServices::openUrl(QUrl(QString("https://nootka.sourceforge.io/index.php?L=%1&C=doc#" + hash).
arg(QString(qgetenv("LANG")).left(2).toLower()), QUrl::TolerantMode));
}
qreal TnootkaQML::bound(qreal min, qreal val, qreal max) {
return qBound(min, val, max);
}
void TnootkaQML::setQmlEngine(QQmlEngine* e) {
m_qmlEngine = e;
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
if (m_scoreAct) {
delete m_scoreAct;
delete m_settingsAct;
delete m_levelAct;
delete m_chartsAct;
delete m_melodyAct;
delete m_examAct;
delete m_aboutAct;
}
m_settingsAct = new Taction(QApplication::translate("TtoolBar", "Settings"), QStringLiteral("systemsettings"), this);
connect(m_settingsAct, &Taction::triggered, this, &TnootkaQML::settingsActTriggered);
m_settingsAct->setTip(QApplication::translate("TtoolBar", "Application preferences"), QQuickItem::TopRight);
m_levelAct = new Taction(QApplication::translate("TtoolBar", "Level"), QStringLiteral("levelCreator"), this);
connect(m_levelAct, &Taction::triggered, this, &TnootkaQML::levelActTriggered);
m_levelAct->setTip(QApplication::translate("TtoolBar", "Levels creator"), QQuickItem::TopRight);
m_chartsAct = new Taction(QApplication::translate("TtoolBar", "Analyze"), QStringLiteral("charts"), this);
connect(m_chartsAct, &Taction::triggered, this, &TnootkaQML::chartsActTriggered);
m_chartsAct->setTip(tr("Analysis of exam results"), QQuickItem::TopRight);
m_scoreAct = new Taction(QApplication::translate("TtoolBar", "Score", "it could be 'notation', 'staff' or whatever is associated with that 'place to display musical notes' and this the name is quite short and looks well."), QStringLiteral("score"), this);
connect(m_scoreAct, &Taction::triggered, this, &TnootkaQML::scoreActTriggered);
m_scoreAct->setTip(QApplication::translate("TtoolBar", "Manage and navigate the score."), QQuickItem::TopRight);
m_melodyAct = new Taction(QApplication::translate("TtoolBar", "Melody"), QStringLiteral("melody"), this);
connect(m_melodyAct, &Taction::triggered, this, &TnootkaQML::melodyActTriggered);
m_melodyAct->setTip(tr("Open, save, generate and play a melody."), QQuickItem::TopRight);
m_examAct = new Taction(QApplication::translate("TtoolBar", "Lessons"), QStringLiteral("startExam"), this);
connect(m_examAct, &Taction::triggered, this, &TnootkaQML::examActTriggered);
m_examAct->setTip(QApplication::translate("TtoolBar", "Start exercises or an exam"), QQuickItem::TopRight);
m_aboutAct = new Taction(this);
connect(m_aboutAct, &Taction::triggered, this, &TnootkaQML::aboutActTriggered);
SeeLook
committed
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
/**
* Opening files from command line argument starts here, but it is a bit clumsy:
* - for Music XML is fine, we have @p TscoreObject here so just call @p openMusicXml()
* - but for exam and level files only @p wantOpenFile() signal is emitted
* - then @p MainWindow.qml handles it and creates @p DialogLoader.qml
* - 'dialog loader' invokes @p TdialogLoaderObject::openFile() of its object
* - and there the file is distinguished (exam or level) and appropriate signal is emitted
* - again, 'dialog loader' handles those signals and creates 'exam executor' or 'level creator' apparently
*/
void TnootkaQML::openFile(const QString& runArg) {
if (GLOB->isExam()) {
qDebug() << "--- Exam or exercise is running. File cannot be opened! ---";
return;
}
if (QFile::exists(runArg)) {
QFile file(runArg);
auto ext = QFileInfo(file).suffix();
if (ext == QLatin1String("xml")) { // TODO mxl - compressed format
auto fullPath = QDir(file.fileName()).absolutePath();
m_scoreObject->openMusicXml(fullPath);
} else {
emit GLOB->wantOpenFile(runArg);
}
}
}
void TnootkaQML::setStatusTip(const QString& statusText, int tipPos) {
if (GLOB->showHints())
emit statusTip(statusText, tipPos);
}
QString TnootkaQML::qaTypeText(int qaType) {
switch (qaType) {
case 0: return QApplication::translate("Texam", "as note on the staff");
case 1: return QApplication::translate("Texam", "as note name");
case 2: return QApplication::translate("Texam", "on instrument");
case 3: return QApplication::translate("Texam", "as played sound");
default: return QString();
}
}
//#################################################################################################
//################### CONNECTIONS NODE ############################################
//#################################################################################################
void TnootkaQML::noteStarted(const Tnote& n) {
SeeLook
committed
Tnote transNote = n;
if (transNote.isValid())
transNote.transpose(-GLOB->transposition());
if (m_scoreObject->keySignature() < 0 || (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats))
transNote = transNote.showWithFlat();
m_ignoreScore = true;
if (m_scoreObject->singleNote()) {
if (!transNote.isRest()) {
transNote.setRhythm(Trhythm::NoRhythm);
m_scoreObject->setNote(0, transNote);
m_scoreObject->addNote(transNote, true);
}
void TnootkaQML::noteFinished(const Tnote& n) {
m_instrument->setNote(n);
Tnote transNote = n;
SeeLook
committed
if (transNote.isValid())
transNote.transpose(-GLOB->transposition());
if (m_scoreObject->keySignature() < 0 || (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats))
transNote = transNote.showWithFlat();
if (m_scoreObject->singleNote())
transNote.setRhythm(Trhythm::NoRhythm);
if (m_scoreObject->selectedItem() && (!m_scoreObject->singleNote() || (m_scoreObject->singleNote() && !transNote.isRest())))
m_scoreObject->setNote(m_scoreObject->selectedItem(), transNote);
SeeLook
committed
// else // naughty user pressed arrow key
// TODO remember to treat tied notes as a single one when setNote will be implemented
}
void TnootkaQML::setMainScore(QQuickItem* ms) {
if (!m_mainScore) {
m_mainScore = ms;
m_scoreObject = qobject_cast<TscoreObject*>(qvariant_cast<QObject*>(m_mainScore->property("scoreObj")));
connect(m_scoreObject, &TscoreObject::selectedNoteChanged, this, &TnootkaQML::scoreChangedNoteSlot);
connect(GLOB, &Tglobals::isExamChanged, this, &TnootkaQML::examStartStop);
if (m_scoreObject && !m_nodeConnected)
connectInstrument();
}
}
void TnootkaQML::setInstrument(TcommonInstrument* ci) {
if (m_instrument != ci) {
if (m_instrument != nullptr)
m_nodeConnected = false; // reset connection of instrument signal when instrument type changed
m_instrument = ci;
SeeLook
committed
if (m_scoreObject && !m_nodeConnected && !GLOB->isExam())
connectInstrument();
}
}
void TnootkaQML::connectInstrument() {
m_nodeConnected = true;
connect(m_instrument, &TcommonInstrument::noteChanged, this, &TnootkaQML::instrumentChangesNoteSlot);
}
void TnootkaQML::instrumentChangesNoteSlot() {
Tnote rawNote = m_instrument->note();
qDebug() << "instrument send note" << rawNote.toText();
m_ignoreScore = true;
emit playNote(m_instrument->note()); // not yet transposed - to sound properly
rawNote.transpose(-GLOB->transposition());
if (m_scoreObject->keySignature() < 0 || (m_scoreObject->keySignature() == 0 && GLOB->GpreferFlats))
rawNote = rawNote.showWithFlat();
if (m_scoreObject->singleNote()) {
m_scoreObject->setNote(0, rawNote);
SeeLook
committed
m_scoreObject->setTechnical(0, m_instrument->technical());
} else {
if (m_scoreObject->selectedItem()) {
rawNote.setRhythm(m_scoreObject->selectedItem()->note()->rtm);
m_scoreObject->setNote(m_scoreObject->selectedItem(), rawNote);
} else {
rawNote.setRhythm(m_scoreObject->workRhythm());
SeeLook
committed
m_scoreObject->addNote(rawNote, true);
if (GLOB->instrument().type() == Tinstrument::Bandoneon) {
auto seg = m_scoreObject->selectedItem() ? m_scoreObject->noteSegment(m_scoreObject->selectedItem()->index()) : m_scoreObject->lastSegment();
SeeLook
committed
Ttechnical instrData(m_instrument->technical());
if (seg->index() > 0) {
for (int i = seg->index() - 1; i >= 0; --i) {
auto searchNoteData = m_scoreObject->noteSegment(i)->techicalData();
if (searchNoteData.bowing()) { // Show bowing but only when it changes comparing to the previously set bow direction
if (searchNoteData.bowing() == instrData.bowing()) // if it is the same - just reset bowing on note data from the instrument
SeeLook
committed
instrData.setBowing(Ttechnical::BowUndefined);
break;
}
}
}
seg->setTechnical(instrData.data());
}
}
}
void TnootkaQML::examStartStop() {
if (GLOB->isExam()) {
disconnect(m_instrument, &TcommonInstrument::noteChanged, this, &TnootkaQML::instrumentChangesNoteSlot);
disconnect(m_scoreObject, &TscoreObject::selectedNoteChanged, this, &TnootkaQML::scoreChangedNoteSlot);
} else {
m_nodeConnected = false;
connectInstrument();
connect(m_scoreObject, &TscoreObject::selectedNoteChanged, this, &TnootkaQML::scoreChangedNoteSlot);
}
void TnootkaQML::scoreChangedNoteSlot() {
if (m_ignoreScore) {
m_ignoreScore = false;
return;
}
auto n = m_scoreObject->selectedNote();
SeeLook
committed
if (n.isValid())
n.transpose(GLOB->transposition());
m_instrument->setNote(n, getTechicalFromScore());
emit playNote(n);
qDebug() << "Got note from score" << n.toText() << n.chromatic();
}
int TnootkaQML::selectedNoteId() const {
return m_scoreObject->selectedItem() ? m_scoreObject->selectedItem()->index() : -1;
}
int TnootkaQML::getTechicalFromScore() {
quint32 technical = 255; // empty by default
if (GLOB->instrument().type() == Tinstrument::Bandoneon && m_scoreObject->selectedItem()) {
auto selectedSegment = m_scoreObject->noteSegment(m_scoreObject->selectedItem()->index());
SeeLook
committed
Ttechnical dataToSet = selectedSegment->technical();
if (!dataToSet.bowing()) { // no bowing, so look up for any previous note with bowing mark
for (int i = selectedSegment->index(); i >= 0; --i) {
auto searchNoteData = m_scoreObject->noteSegment(i)->techicalData();
if (searchNoteData.bowing()) {
dataToSet.setBowing(searchNoteData.bowing());
break;
}
}
}
technical = dataToSet.data();
}
return technical;
SeeLook
committed
}
void TnootkaQML::selectPlayingNote(int id) {
m_ignoreScore = true;
m_scoreObject->setSelectedItem(m_scoreObject->note(id));
auto n = m_scoreObject->selectedNote();
if (n.isValid())
n.transpose(GLOB->transposition());
m_instrument->setNote(n, getTechicalFromScore());
SeeLook
committed
}
int TnootkaQML::scoreNotesCount() const {
return m_scoreObject->notesCount();
}
QList<Tnote>& TnootkaQML::scoreNoteList() const {
return m_scoreObject->noteList();
}