diff --git a/changes b/changes index 6c75cde89d04306c55eff1a7fa6c558a488f3d93..4f99d42e4696ea212dae975b18f72b1cefc51b56 100644 --- a/changes +++ b/changes @@ -1,5 +1,6 @@ 1.5.3 beta1 - added support for scientific octave numbers in note names + - read/save melody title, composer, tempo, beat - restored charts (analysis) with updated look - improved start wizard to handle more supported instruments diff --git a/src/libs/core/music/tmelody.cpp b/src/libs/core/music/tmelody.cpp index 54e29c0010ece19721a39c197a07247517627ca5..e895c764883ba6cc4b943784910b7c02f3999270 100644 --- a/src/libs/core/music/tmelody.cpp +++ b/src/libs/core/music/tmelody.cpp @@ -111,6 +111,22 @@ void Tmelody::toXml(QXmlStreamWriter& xml) { xml.writeTextElement(QStringLiteral("staves"), QStringLiteral("2")); Tclef(m_clef).toXml(xml); xml.writeEndElement(); // attributes + xml.writeStartElement(QStringLiteral("direction")); + xml.writeAttribute(QStringLiteral("placement"), QStringLiteral("above")); + xml.writeStartElement(QStringLiteral("direction-type")); + xml.writeStartElement(QStringLiteral("metronome")); + QString beatUnitString = QStringLiteral("quarter"); + if (beat() == Tmeter::BeatEighth) + beatUnitString = QStringLiteral("eighth"); + else if (beat() == Tmeter::BeatHalf) + QStringLiteral("half"); + xml.writeTextElement(QStringLiteral("beat-unit"), beatUnitString); + if (beat() == Tmeter::BeatQuarterDot) + xml.writeEmptyElement(QStringLiteral("beat-unit-dot")); + xml.writeTextElement(QStringLiteral("per-minute"), QString::number(tempo())); + xml.writeEndElement(); // metronome + xml.writeEndElement(); // direction-type + xml.writeEndElement(); // direction } int staffNr_1 = 1, staffNr_2 = 2; int *staffPtr = 0; @@ -134,6 +150,7 @@ bool Tmelody::fromXml(QXmlStreamReader& xml) { m_notes.clear(); m_measures.clear(); m_meter->setMeter(Tmeter::NoMeter); + setTempo(0); // reset tempo, try to read from XML while (xml.readNextStartElement()) { /** [measure] */ if (xml.name() == QLatin1String("measure")) { @@ -226,7 +243,7 @@ bool Tmelody::fromXml(QXmlStreamReader& xml) { if (xml.name() == QLatin1String("direction-type")) { while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("words")) { // fingering and bowing can be read this way - QString words = xml.readElementText(); + auto words = xml.readElementText(); if (words == QLatin1String("(A)")) technical.setBowing(Ttechnical::BowDown); else if (words == QLatin1String("(C)")) @@ -237,9 +254,55 @@ bool Tmelody::fromXml(QXmlStreamReader& xml) { if (isNumber) technical.setFinger(finger); } + } else if (xml.name() == QLatin1String("metronome")) { + int tempoWillBe = 120; + auto beatWillBe = Tmeter::EbeatUnit::BeatQuarter; + bool dotWillBe = false; + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("beat-unit")) { + auto beatUnit = xml.readElementText(); + if (beatUnit == QLatin1String("quarter")) + beatWillBe = Tmeter::BeatQuarter; + else if (beatUnit == QLatin1String("eighth")) + beatWillBe = Tmeter::BeatEighth; + else if (beatUnit == QLatin1String("half")) + beatWillBe = Tmeter::BeatHalf; + else + qDebug() << "[Tmelody] Unknown 'beat-unit' value. Stay with 'quarter' beat."; + } else if (xml.name() == QLatin1String("beat-unit-dot")) { + dotWillBe = true; + xml.skipCurrentElement(); + } else if (xml.name() == QLatin1String("per-minute")) { + tempoWillBe = xml.readElementText().toInt(); + } else + xml.skipCurrentElement(); + } + if (dotWillBe) { + if (beatWillBe == Tmeter::BeatQuarter) + beatWillBe = Tmeter::BeatQuarterDot; + else + qDebug() << "[Tmelody] Metronome beat with dot only supports quarter. Ignore dot then!"; + } + int quarterTempo = tempoWillBe / Tmeter::beatTempoFactor(beatWillBe); + if (nr == 1 || tempo() == 0) { // read metronome tempo but only for 1st bar or if not yet set + if (quarterTempo >= 40 && quarterTempo <= 180) { + setTempo(tempoWillBe); + setBeat(beatWillBe); + } else // too fast or to slow + qDebug() << "[Tmelody]" << beatWillBe << "for tempo" << tempoWillBe << "is not supported. (Too fast or too slow)"; + } } else xml.skipCurrentElement(); } + } else if (xml.name() == QLatin1String("sound")) { + int t = xml.attributes().value(QStringLiteral("tempo")).toInt(); + if (m_tempo == 0) { // always prefer 'metronome' tag, so skip this, if tempo has been set already + if (t >= 40 && t <= 180) // but keep tempo in Nootka supported boundaries + setTempo(t); + else if (t > 180) + setTempo(180); + } + xml.skipCurrentElement(); } else xml.skipCurrentElement(); } @@ -254,6 +317,8 @@ bool Tmelody::fromXml(QXmlStreamReader& xml) { xml.skipCurrentElement(); } + if (tempo() == 0) + setTempo(120); return ok; } @@ -265,7 +330,7 @@ bool Tmelody::saveToMusicXml(const QString& xmlFileName) { xml.setAutoFormatting(true); xml.setAutoFormattingIndent(2); xml.writeStartDocument(); - xml.writeDTD(QStringLiteral("<!DOCTYPE score-partwise PUBLIC \"-//Recordare//DTD MusicXML 3.0 Partwise//EN\" \"http://www.musicxml.org/dtds/partwise.dtd\">")); + xml.writeDTD(QStringLiteral("<!DOCTYPE score-partwise PUBLIC \"-//Recordare//DTD MusicXML 3.1 Partwise//EN\" \"http://www.musicxml.org/dtds/partwise.dtd\">")); xml.writeStartElement(QStringLiteral("score-partwise")); xml.writeStartElement(QStringLiteral("work")); xml.writeTextElement(QStringLiteral("work-title"), title()); diff --git a/src/libs/core/music/tmelody.h b/src/libs/core/music/tmelody.h index bece8f6e4b68df67ba7724775369619d0370fd3d..19df2ece5e6abcdf5871b0083cb033566a1ffc4c 100644 --- a/src/libs/core/music/tmelody.h +++ b/src/libs/core/music/tmelody.h @@ -71,6 +71,9 @@ public: int tempo() const { return m_tempo; } void setTempo(int tmp) { m_tempo = tmp; } + Tmeter::EbeatUnit beat() const { return m_beat; } + void setBeat(Tmeter::EbeatUnit bu) { m_beat = bu; } + TkeySignature const key() { return m_key; } void setKey(const TkeySignature& k) { m_key = k; } @@ -94,6 +97,7 @@ private: QList<Tmeasure> m_measures; QList<Tchunk*> m_notes; /**< List of pointers to ordered notes */ int m_tempo; + Tmeter::EbeatUnit m_beat = Tmeter::BeatQuarter; TkeySignature m_key; Tmeter *m_meter = nullptr; Tclef::EclefType m_clef; diff --git a/src/libs/core/music/tmeter.cpp b/src/libs/core/music/tmeter.cpp index db27d20c23f468bc7a7c5611f1bf20458a10720f..e854c3f8f2ca65e86a4aa3a808155e33284a0354 100644 --- a/src/libs/core/music/tmeter.cpp +++ b/src/libs/core/music/tmeter.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2016-2018 by Tomasz Bojczuk * + * Copyright (C) 2016-2019 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -190,4 +190,15 @@ void Tmeter::fillMeterGroups(QList<quint8>& durationList) { } +Tmeter::EbeatUnit Tmeter::optimalBeat(Tmeter::Emeter m) { + if (m <= Meter_7_4) // all time signatures with quarter + return BeatQuarter; + return BeatEighth; +} + + +qreal Tmeter::beatTempoFactor(Tmeter::EbeatUnit bu) { + static const qreal beatsArray[4] = { 1.0, 2.0, 0.75, 0.5 }; + return beatsArray[static_cast<int>(bu)]; +} diff --git a/src/libs/core/music/tmeter.h b/src/libs/core/music/tmeter.h index 6b591d03da712cd8ce917b548ae95b6748325d61..01494c849a7b3188158fef1bac66aba161abb90a 100644 --- a/src/libs/core/music/tmeter.h +++ b/src/libs/core/music/tmeter.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2014-2018 by Tomasz Bojczuk * + * Copyright (C) 2014-2019 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -95,6 +95,25 @@ public: void fillMeterGroups(QList<quint8>& durationList); + enum EbeatUnit { + BeatQuarter = 0, + BeatEighth = 1, + BeatQuarterDot = 2, + BeatHalf = 3 + }; + + Q_ENUM(EbeatUnit) + + static EbeatUnit optimalBeat(Emeter m); + + Q_INVOKABLE EbeatUnit optimalBeat() const { return optimalBeat(m_meter); } + + /** + * Returns tempo multiplexer depending on beat unit: + * i.e.: for eighth note it is @p 2 as such as tempo with eighths is 2 times faster than tempo with quarters + */ + Q_INVOKABLE static qreal beatTempoFactor(EbeatUnit bu); + private: Emeter m_meter; }; diff --git a/src/libs/core/score/tscoreobject.cpp b/src/libs/core/score/tscoreobject.cpp index 64c44e04ba61d67116bcdb49c4a91c76c2f447c0..c04c0a1f6d5d51b82d92f1677fcbff22602b663a 100644 --- a/src/libs/core/score/tscoreobject.cpp +++ b/src/libs/core/score/tscoreobject.cpp @@ -457,12 +457,17 @@ void TscoreObject::setCursorAlter(int curAlt) { } -void TscoreObject::openMusicXml(const QString& musicFile) { +void TscoreObject::openMusicXml(const QString& musicFile, Tmelody* melody) { if (!musicFile.isEmpty()) { - auto melody = new Tmelody(); + bool melodyCreated = false; + if (!melody) { + melody = new Tmelody(); + melodyCreated = true; + } if (melody->grabFromMusicXml(musicFile)) setMelody(melody); - delete melody; + if (melodyCreated) + delete melody; } } diff --git a/src/libs/core/score/tscoreobject.h b/src/libs/core/score/tscoreobject.h index e412f66052c58e38edac08ee8f60e65fc0128007..372c08a539d08c357c756108c5b7604c8a82736b 100644 --- a/src/libs/core/score/tscoreobject.h +++ b/src/libs/core/score/tscoreobject.h @@ -181,7 +181,13 @@ public: int cursorAlter() const { return m_cursorAlter; } void setCursorAlter(int curAlt); - Q_INVOKABLE void openMusicXml(const QString& musicFile); + /** + * Grabs melody from file and sets it into score. + * If @p melody parameter is set - it loads XML file to it, + * so this method caller can read more from opened melody + */ + Q_INVOKABLE void openMusicXml(const QString& musicFile, Tmelody* melody = nullptr); + Q_INVOKABLE void saveMusicXml(const QString& musicFile, const QString& title = QString(), const QString& composer = QString()); /** diff --git a/src/libs/sound/tsound.cpp b/src/libs/sound/tsound.cpp index e50f99d67d43e39ab73ca0a8c90600a2139ed360..e5792dce11d1b919e27a9d11c7bc400a17081702 100644 --- a/src/libs/sound/tsound.cpp +++ b/src/libs/sound/tsound.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2011-2018 by Tomasz Bojczuk * + * Copyright (C) 2011-2019 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -134,7 +134,9 @@ void Tsound::playMelody(Tmelody* mel, int transposition) { void Tsound::playScoreNotes(QList<Tnote>& notes, int firstNote) { if (player && !notes.isEmpty()) - player->playNotes(std::addressof(notes), m_tempo, firstNote); + player->playNotes(std::addressof(notes), // beat unit has to be converted to quarter here + qRound(static_cast<qreal>(m_tempo) / Tmeter::beatTempoFactor(static_cast<Tmeter::EbeatUnit>(m_beatUnit))), + firstNote); } @@ -292,6 +294,27 @@ void Tsound::setTempo(int t) { } +void Tsound::setBeatUnit(int bu) { + if (bu != m_beatUnit) { + m_beatUnit = bu; + m_tempo *= Tmeter::beatTempoFactor(static_cast<Tmeter::EbeatUnit>(bu)); + emit tempoChanged(); + } +} + + +void Tsound::setMetronome(int t, int beat) { + if (beat != m_beatUnit || t != m_tempo) { + int quarterTempo = t / Tmeter::beatTempoFactor(static_cast<Tmeter::EbeatUnit>(beat)); + if (quarterTempo >= 40 && quarterTempo <= 180) { + m_tempo = t; + m_beatUnit = beat; + emit tempoChanged(); + } + } +} + + /** * @p m_quantVal is expressed in @p Trhythm duration of: Sixteenth triplet -> 4 or just Sixteenth -> 6 or Eighth -> 12 */ @@ -501,7 +524,7 @@ void Tsound::noteFinishedSlot(const TnoteStruct& note) { if (note.pitch.isValid()) m_detectedNote = note.pitch; if (GLOB->rhythmsEnabled()) { - qreal rFactor = 2500.0 / m_tempo; + qreal rFactor = 2500.0 / (m_tempo / Tmeter::beatTempoFactor(static_cast<Tmeter::EbeatUnit>(m_beatUnit))); qreal dur = (note.duration * 1000.0) / rFactor; int quant = dur > 20.0 ? 12 : 6; // avoid sixteenth dots int normDur = qRound(dur / static_cast<qreal>(quant)) * quant; diff --git a/src/libs/sound/tsound.h b/src/libs/sound/tsound.h index 2543676d637795cdfb7c2b19ca62451d776614b1..d715701bf80bd2e73223be4757cd81bddd5c2d64 100644 --- a/src/libs/sound/tsound.h +++ b/src/libs/sound/tsound.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2011-2017 by Tomasz Bojczuk * + * Copyright (C) 2011-2019 by Tomasz Bojczuk * * seelook@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -51,6 +51,7 @@ class NOOTKASOUND_EXPORT Tsound : public QObject Q_OBJECT Q_PROPERTY(int tempo READ tempo WRITE setTempo NOTIFY tempoChanged) + Q_PROPERTY(int beatUnit READ beatUnit WRITE setBeatUnit NOTIFY tempoChanged) Q_PROPERTY(int quantization READ quantization WRITE setQuantization NOTIFY quantizationChanged) Q_PROPERTY(bool stoppedByUser READ stoppedByUser WRITE setStoppedByUser NOTIFY stoppedByUserChanged) Q_PROPERTY(bool listening READ listening NOTIFY listeningChanged) @@ -136,6 +137,11 @@ public: int tempo() const { return m_tempo; } void setTempo(int t); + int beatUnit() const { return m_beatUnit; } + void setBeatUnit(int bu); + + Q_INVOKABLE void setMetronome(int t, int beat); + /** * Quantization value determines accuracy of detecting rhythm of played note by its duration. */ @@ -210,6 +216,7 @@ private: bool m_stopSniffOnce, m_userState;; Tmelody *m_playedMelody; int m_tempo; + int m_beatUnit = 0; /**< corresponds with Tmeter::EbeatUnit enum. Quarter by default */ int m_quantVal; static Tsound *m_instance; diff --git a/src/main/tmainscoreobject.cpp b/src/main/tmainscoreobject.cpp index a5f09448e963326c116d8f7ca980db6039c27ff3..4b621f05aa5cd573b0b5376759d28e05a48cecc4 100644 --- a/src/main/tmainscoreobject.cpp +++ b/src/main/tmainscoreobject.cpp @@ -459,7 +459,10 @@ void TmainScoreObject::saveMusicXml(const QString& fileName, const QString& titl void TmainScoreObject::openXmlActSlot() { SOUND->stopListen(); - m_scoreObj->openMusicXml(NOO->getXmlToOpen()); + auto m = new Tmelody(); + m_scoreObj->openMusicXml(NOO->getXmlToOpen(), m); + SOUND->setMetronome(m->tempo(), m->beat()); + delete m; SOUND->startListen(); } diff --git a/src/qml/sound/TempoBar.qml b/src/qml/sound/TempoBar.qml index 83685f17460b0aac76f51388ba685c57cdf5e624..659c8a9818f3f37d9ab1e3a53415e60cb4fb6ef3 100644 --- a/src/qml/sound/TempoBar.qml +++ b/src/qml/sound/TempoBar.qml @@ -1,5 +1,5 @@ /** This file is part of Nootka (http://nootka.sf.net) * - * Copyright (C) 2017-2018 by Tomasz Bojczuk (seelook@gmail.com) * + * Copyright (C) 2017-2019 by Tomasz Bojczuk (seelook@gmail.com) * * on the terms of GNU GPLv3 license (http://www.gnu.org/licenses) */ import QtQuick 2.9 @@ -8,16 +8,19 @@ import Nootka 1.0 Item { - id: root + id: tempoBar // private - property var menu: null + property var tMenu: null property int hiTick: -1 property int cnt: 1 property var hArray: [ 0.6, 0, 0.3, 0, 0.6] property var gArray: [ "\ue1d5", "\ue1d9", "\ue1d7", "\ue1d9", "\ue1d5" ] property int countTo: Noo.meter(score.meter).countTo() + // protected + property var beatModel: [ "\ue1d5", "\ue1d7", "\ue1d5\ue1e7", "\ue1d3" ] + MouseArea { anchors.fill: parent hoverEnabled: true @@ -34,7 +37,7 @@ Item { y: parent.height * -0.7 x: width * 0.2 font { family: "Scorek"; pixelSize: parent.height * 0.7 } - text: "\ue1d5=" + SOUND.tempo + text: beatModel[SOUND.beatUnit] + "=" + SOUND.tempo color: activPal.text } MouseArea { @@ -42,11 +45,11 @@ Item { hoverEnabled: true anchors.fill: parent onClicked: { - if (!menu) { + if (!tMenu) { var c = Qt.createComponent("qrc:/sound/TempoMenu.qml") - menu = c.createObject(root) + tMenu = c.createObject(tempoBar) } - menu.open() + tMenu.open() } onEntered: Noo.setStatusTip(qsTr("Tempo"), Item.TopLeft) onExited: Noo.setStatusTip("", Item.TopLeft) @@ -58,7 +61,7 @@ Item { model: 5 Rectangle { readonly property color bgColor: Qt.tint(activPal.window, Noo.alpha(activPal.base, 100)) - x: root.width / 3 + index * (parent.height) - width / 2 + x: tempoBar.width / 3 + index * (parent.height) - width / 2 y: parent.height * (Math.abs(0.6 - hArray[index]) / 5) width: parent.height * (0.9 - (Math.abs(0.6 - hArray[index]) / 3)); height: parent.height * 0.8 + hArray[index] * parent.height radius: width / 2 @@ -74,7 +77,7 @@ Item { x: (parent.width - width) / 2 } Text { // count - visible: pitchView.active && index === hiTick && (!menu || (menu.count && menu.tickEnable)) + visible: pitchView.active && index === hiTick && (!tMenu || (tMenu.count && tMenu.tickEnable)) font { pixelSize: parent.height; family: "Scorek" } color: activPal.base text: cnt @@ -86,7 +89,7 @@ Item { Timer { id: timer - running: visible && pitchView.active && (!menu || menu.tickEnable); repeat: true + running: visible && pitchView.active && (!tMenu || tMenu.tickEnable); repeat: true interval: (SOUND.tempo < 110 ? 15000 : 30000) / SOUND.tempo property real elap: 0 property real lag: 0 diff --git a/src/qml/sound/TempoMenu.qml b/src/qml/sound/TempoMenu.qml index a4320a132415108dc4642c983f34a56aedf03104..0d4a850b0ad2e39bc21a7de80ad8ccb8926f3e6c 100644 --- a/src/qml/sound/TempoMenu.qml +++ b/src/qml/sound/TempoMenu.qml @@ -1,5 +1,5 @@ /** This file is part of Nootka (http://nootka.sf.net) * - * Copyright (C) 2017-2018 by Tomasz Bojczuk (seelook@gmail.com) * + * Copyright (C) 2017-2019 by Tomasz Bojczuk (seelook@gmail.com) * * on the terms of GNU GPLv3 license (http://www.gnu.org/licenses) */ import QtQuick 2.9 @@ -22,26 +22,67 @@ Popup { signal accepted() + // private + property var beatFactor: [ 1, 2, 0.75, 0.5 ] + Column { spacing: Noo.fontSize() / 2 Row { spacing: Noo.fontSize() - anchors.horizontalCenter: parent.horizontalCenter - Text { text: qsTr("tempo"); color: activPal.text; anchors.verticalCenter: parent.verticalCenter } - SpinBox { - id: tempoSpin - from: 40; to: 180; editable: true - value: SOUND.tempo + Tumbler { + id: beatUnitTumb + background: Rectangle { color: activPal.base } + anchors.verticalCenter: parent.verticalCenter + height: Noo.fontSize() * 6; width: Noo.fontSize() * 2 + model: tempoBar.beatModel + visibleItemCount: 3; wrap: true + currentIndex: SOUND.beatUnit + delegate: Text { + text: modelData + color: activPal.text + height: Noo.fontSize() * 2.5 + horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter + opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2) + font { pixelSize: Noo.fontSize() * 2; family: "Scorek" } + MouseArea { + id: tumblerArea + anchors.fill: parent + onClicked: { +// var prevBeatFactor = beatFactor[beatUnitTumb.currentIndex] + beatUnitTumb.currentIndex = beatUnitTumb.currentIndex === beatUnitTumb.count - 1 ? beatUnitTumb.currentIndex = 0 : beatUnitTumb.currentIndex + 1 +// tempoSpin.value /= prevBeatFactor / beatFactor[beatUnitTumb.currentIndex] + } + } + } + } + Text { + anchors.verticalCenter: parent.verticalCenter + text: "=" + color: activPal.text + font { pixelSize: Noo.fontSize() * 2 } + } + Column { + anchors.verticalCenter: parent.verticalCenter + Row { + spacing: Noo.fontSize() + anchors.horizontalCenter: parent.horizontalCenter + Text { text: qsTr("tempo"); color: activPal.text; anchors.verticalCenter: parent.verticalCenter } + SpinBox { + id: tempoSpin + from: 40; to: 180 * beatFactor[beatUnitTumb.currentIndex]; editable: true + value: SOUND.tempo + } + } + Slider { + width: parent.width * 0.96 + anchors.horizontalCenter: parent.horizontalCenter + value: tempoSpin.value + from: 40; to: 180 * beatFactor[beatUnitTumb.currentIndex] + onValueChanged: tempoSpin.value = value + stepSize: 10 + } } - } - Slider { - width: parent.width * 0.96 - anchors.horizontalCenter: parent.horizontalCenter - value: tempoSpin.value - from: 40; to: 180; - onValueChanged: tempoSpin.value = value - stepSize: 10 } TiconButton { @@ -52,27 +93,6 @@ Popup { onClicked: tapTempo() } - ButtonGroup { buttons: radioRow.children } - - Row { - id: radioRow - spacing: Noo.fontSize() - anchors.horizontalCenter: parent.horizontalCenter - Text { text: qsTr("round to:"); color: activPal.text; anchors.verticalCenter: parent.verticalCenter } - RadioButton { - id: radio16 - font { family: "Nootka"; pixelSize: Noo.fontSize() * 2 } - text: "G" - checked: SOUND.quantization === 6 - } - RadioButton { - id: radio8 - font { family: "Nootka"; pixelSize: Noo.fontSize() * 2 } - text: "F" - checked: SOUND.quantization === 12 - } - } - TcheckBox { id: meterTickChB text: qsTr("Enable metronome ticking") @@ -85,12 +105,41 @@ Popup { checked: true } + ButtonGroup { buttons: radioRow.children } + Item { + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width; height: radioRow.height + MouseArea { + anchors.fill: parent; hoverEnabled: true + onEntered: Noo.setStatusTip(qsTr("Detected rhythmic units are rounded (quantization). Shortest units require more rhythmical accuracy."), Item.TopLeft) + onExited: Noo.setStatusTip("", Item.TopLeft) + } + Row { + id: radioRow + spacing: Noo.fontSize() + anchors.horizontalCenter: parent.horizontalCenter + Text { text: qsTr("round to:"); color: activPal.text; anchors.verticalCenter: parent.verticalCenter } + RadioButton { + id: radio16 + font { family: "Nootka"; pixelSize: Noo.fontSize() * 2 } + text: "G" + checked: SOUND.quantization === 6 + } + RadioButton { + id: radio8 + font { family: "Nootka"; pixelSize: Noo.fontSize() * 2 } + text: "F" + checked: SOUND.quantization === 12 + } + } + } + TiconButton { text: Noo.TR("QPlatformTheme", "Apply") pixmap: Noo.pix("check") anchors.horizontalCenter: parent.horizontalCenter onClicked: { - SOUND.tempo = tempoSpin.value + SOUND.setMetronome(tempoSpin.value, beatUnitTumb.currentIndex) SOUND.quantization = radio16.checked ? 6 : 12 // See Tsound doc for values explanation tempoSpin.value = SOUND.tempo accepted() @@ -99,8 +148,8 @@ Popup { } } - onOpened: { SOUND.stopListen(); spaceShort.enabled = true } - onClosed: { SOUND.startListen(); spaceShort.enabled = false } + onOpened: { SOUND.stopListen(); spaceShort.enabled = true; tempoSpin.value = SOUND.tempo; beatUnitTumb.currentIndex = SOUND.beatUnit } + onClosed: { SOUND.startListen(); spaceShort.enabled = false } Shortcut { id: spaceShort; sequence: " "; onActivated: tapTempo() }