diff --git a/changes b/changes index 7aa6076e0589e353b61e6340115812205f4696ba..0f0b29e1a6a791bd8afc460b4b0a55c01de58b0a 100644 --- a/changes +++ b/changes @@ -1,6 +1,10 @@ 1.5.1 alpha2 - - simple metronome and menu for configuring tempo, quantization, etc. + - detecting duration (rhythm) of sound along with its pitch + - simple metronome and its menu for configuring parameters + - added bandoneon and saxophone views + - audio plays different sounds (piano, bandoneon, sax) - real support for transposing instruments + - more stuff ported to QML 1.5.0 alpha1 - added support for rhythms to the score @@ -10,6 +14,12 @@ - many improvements of user interface - option for enable/disable animations +1.4.4 + - German translation is complete + - support for marking more frets on the fingerboard + BUGS FIXES + - fixed annoying crash on entering settings when 'other' instrument was used + 1.4.3 ANDROID - attempt to fix random crashes related to audio input diff --git a/src/libs/core/tglobals.cpp b/src/libs/core/tglobals.cpp index 196f441eba488a36cd1883d5afd828b598092204..859e1ccb14b1b479c86d5fd23045f73e3f4eb105 100755 --- a/src/libs/core/tglobals.cpp +++ b/src/libs/core/tglobals.cpp @@ -253,6 +253,9 @@ void Tglobals::setMarkedFrets(const QString& frets) { /* ------------------ Sound switches ------------------ */ +bool Tglobals::audioInEnabled() const { return A->INenabled; } +void Tglobals::setAudioInEnabled(bool inEnabled) { A->INenabled = inEnabled; } + QString Tglobals::inDevName() const { return A->INdevName; } void Tglobals::setInDevName(const QString& inName) { A->INdevName = inName; } @@ -283,6 +286,14 @@ void Tglobals::setSkipStillerVal(qreal ssv) { A->skipStillerVal = ssv; } bool Tglobals::useFilter() const { return A->equalLoudness; } void Tglobals::setUseFilter(bool use) { A->equalLoudness = use; } +bool Tglobals::audioOutEnabled() const { return A->OUTenabled; } +void Tglobals::setAudioOutEnabled(bool outEnabled) { A->OUTenabled = outEnabled; } + +QString Tglobals::outDevName() const { return A->OUTdevName; } +void Tglobals::setOutDevName(const QString& odn) { A->OUTdevName = odn; } + +bool Tglobals::forwardInput() const { return A->forwardInput; } +void Tglobals::setForwardInput(bool fi) { A->forwardInput = fi; } void Tglobals::setGuitarParams(int fretNr, int stringNr) { diff --git a/src/libs/core/tglobals.h b/src/libs/core/tglobals.h index 5e115ddf97970164589260d8e0a5862634bb216a..106dbee98e2aaa1ef36e18f380f084d03d9f4307 100644 --- a/src/libs/core/tglobals.h +++ b/src/libs/core/tglobals.h @@ -82,6 +82,7 @@ class NOOTKACORE_EXPORT Tglobals : public QObject Q_PROPERTY(QString markedFrets READ markedFrets WRITE setMarkedFrets); /* Sound switches */ + Q_PROPERTY(bool audioInEnabled READ audioInEnabled WRITE setAudioInEnabled) Q_PROPERTY(int audioInstrument READ audioInstrument WRITE setAudioInstrument) Q_PROPERTY(QString inDevName READ inDevName WRITE setInDevName); Q_PROPERTY(qreal minDuration READ minDuration WRITE setMinDuration) @@ -90,6 +91,9 @@ class NOOTKACORE_EXPORT Tglobals : public QObject Q_PROPERTY(qreal minSplitVol READ minSplitVol WRITE setMinSplitVol) Q_PROPERTY(qreal skipStillerVal READ skipStillerVal WRITE setSkipStillerVal) Q_PROPERTY(bool useFilter READ useFilter WRITE setUseFilter) + Q_PROPERTY(bool audioOutEnabled READ audioOutEnabled WRITE setAudioOutEnabled) + Q_PROPERTY(QString outDevName READ outDevName WRITE setOutDevName); + Q_PROPERTY(bool forwardInput READ forwardInput WRITE setForwardInput) public: @@ -184,6 +188,9 @@ public: void setMarkedFrets(const QString& frets); /* ------------------ Sound switches ------------------ */ + bool audioInEnabled() const; + void setAudioInEnabled(bool inEnabled); + int audioInstrument() const; void setAudioInstrument(int ai); @@ -208,6 +215,15 @@ public: bool useFilter() const; void setUseFilter(bool use); + bool audioOutEnabled() const; + void setAudioOutEnabled(bool outEnabled); + + QString outDevName() const; + void setOutDevName(const QString& odn); + + bool forwardInput() const; + void setForwardInput(bool fi); + /** * Updates key signature names according to name style and major/minor suffixes. * Emits @p keyNameChanged() to inform MainScore.qml diff --git a/src/qml/settings/SoundPage.qml b/src/qml/settings/SoundPage.qml index f52f5b1555740d47859a8deb23832b39fc169d70..bdbdec8d3fe09dbf8f24fafd03f9047c41c63d59 100644 --- a/src/qml/settings/SoundPage.qml +++ b/src/qml/settings/SoundPage.qml @@ -4,241 +4,345 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 import "../" -Flickable { - clip: true - contentHeight: audioCol.height - contentWidth: width +Column { + spacing: nootkaWindow.fontSize + width: parent.width; height: parent.height - Column { - id: audioCol + ListView { + id: headList + orientation: ListView.Horizontal + spacing: nootkaWindow.fontSize width: parent.width - TcheckBox { - id: enableInChB - text: qsTr("enable pitch detection") - anchors.horizontalCenter: parent.horizontalCenter - checked: true + model: ListModel { + ListElement { head: QT_TR_NOOP("listening") } + ListElement { head: QT_TR_NOOP("playing") } } - Column { - enabled: enableInChB.checked - width: parent.width - spacing: nootkaWindow.fontSize + delegate: Button { + text: qsTranslate("TscoreSettings", head) + onClicked: { stack.currentIndex = index; headList.currentIndex = index } + highlighted: index === stack.currentIndex + Component.onCompleted: headList.height = Math.max(height, headList.height) + } + } - Tile { - description: qsTr("Be sure your input device (microphone, webcam, instrument, etc.) is plugged in, properly configured, and working.") - Row { - spacing: nootkaWindow.fontSize - anchors.horizontalCenter: parent.horizontalCenter - TlabelText { text: qsTr("input device") } - ComboBox { - id: inDevCombo - width: nootkaWindow.fontSize * 20 - model: SOUND.inputDevices() - delegate: ItemDelegate { text: modelData } - } - TcheckBox { - visible: false // TODO - id:jackChB - text: "JACK" - anchors.verticalCenter: parent.verticalCenter - } - } - } + StackLayout { + id: stack + height: parent.height - headList.height - nootkaWindow.fontSize + width: parent.width + currentIndex: -1 - Tile { - description: qsTr("Only sounds longer than the selected time will be pitch-detected.<br>Selecting a longer minimum note duration helps avoid capturing fret noise or other unexpected sounds but decreases responsiveness.") - Row { - spacing: nootkaWindow.fontSize - anchors.horizontalCenter: parent.horizontalCenter - TlabelText { text: qsTr("minimum note duration") } - SpinBox { - id: minDurSpin - from: 50; to: 1000 - stepSize: 10 - editable: true - } - TlabelText { text: qsTr("[milliseconds]") } - } - } + Tflickable { // "listening" page + contentHeight: inCol.height + contentWidth: width - Tile { - description: qsTr("Minimum volume of a sound to be pitch-detected") - Row { - spacing: nootkaWindow.fontSize - anchors.horizontalCenter: parent.horizontalCenter - Text { text: qsTr("minimum volume"); anchors.verticalCenter: parent.verticalCenter; color: enabled ? activPal.text : disdPal.text } - Slider { - anchors.verticalCenter: parent.verticalCenter - width: Math.min(nootkaWindow.fontSize * 15, parent.parent.width / 3) - from: 10; to: 80 - value: volSpin.value - onValueChanged: volSpin.value = value - stepSize: 5 - } - SpinBox { - id: volSpin - from: 10; to: 80 - value: GLOB.minVolume * 100 - editable: true - } - TlabelText { text: "%" } - } - } + Column { + id: inCol + width: parent.width - Tile { - enabled: false // TODO - description: qsTr("The base frequency of <i>middle a</i>.<br>Detection of the proper pitch of notes is relative to this value. This also affects the pitch of played sounds.") - Column { - width: parent.width - Row { - spacing: nootkaWindow.fontSize - anchors.horizontalCenter: parent.horizontalCenter - TlabelText { text: qsTr("middle A") } - Text { // staff - height: nootkaWindow.fontSize * 4.5 - y: nootkaWindow.fontSize * -1.6 - font { pixelSize: nootkaWindow.fontSize * 2.5; family: "scorek" } - text: "\ue014\ue014\ue014\ue014" - color: enabled ? activPal.text : disdPal.text - Text { // clef - x: nootkaWindow.fontSize / 4 - y: nootkaWindow.fontSize * -0.6 - color: parent.color - font: parent.font - text: "\ue050" - } - Text { // note - color: parent.color - font: parent.font - x: nootkaWindow.fontSize * 2.7 - y: nootkaWindow.fontSize * -0.9 - text: "\ue1d7" - } - } - SpinBox { - id: freqSpin - anchors.verticalCenter: parent.verticalCenter; - from: 391; to: 493 // in range of two semitones up and down around middle A (440Hz) - value: 440 - editable: true - } - TlabelText { text: qsTr("[Hz]") } - } + TcheckBox { + id: enableInChB + text: qsTr("enable pitch detection") + anchors.horizontalCenter: parent.horizontalCenter + checked: true } - } - Frame { // advanced settings - width: parent.width * 0.99 - anchors.horizontalCenter: parent.horizontalCenter - background: Rectangle { - color: "transparent" - border.color: "red" - radius: nootkaWindow.fontSize / 2 - } Column { + enabled: enableInChB.checked width: parent.width spacing: nootkaWindow.fontSize - Switch { - id: advSwitch - text: qsTr("Advanced") - checked: false - } + Tile { - visible: advSwitch.checked + description: qsTr("Be sure your input device (microphone, webcam, instrument, etc.) is plugged in, properly configured, and working.") Row { - anchors.horizontalCenter: parent.horizontalCenter spacing: nootkaWindow.fontSize - TlabelText { text: qsTr("pitch detection mode") } + anchors.horizontalCenter: parent.horizontalCenter + TlabelText { text: qsTr("input device") } ComboBox { - id: methodCombo + id: inDevCombo width: nootkaWindow.fontSize * 20 - model: ["MPM", "autocorrelation", "MPM + modified cepstrum"] + model: SOUND.inputDevices() delegate: ItemDelegate { text: modelData } } + TcheckBox { + visible: false // TODO + id: jackInChB + text: "JACK" + anchors.verticalCenter: parent.verticalCenter + } } } + Tile { - visible: advSwitch.checked - description: qsTr("Helps to properly detect the same notes repeated quickly on the guitar.") + description: qsTr("Only sounds longer than the selected time will be pitch-detected.<br>Selecting a longer minimum note duration helps avoid capturing fret noise or other unexpected sounds but decreases responsiveness.") Row { - anchors.horizontalCenter: parent.horizontalCenter spacing: nootkaWindow.fontSize - TcheckBox { - id: splitVolChB - text: qsTr("split when volume rise") - } + anchors.horizontalCenter: parent.horizontalCenter + TlabelText { text: qsTr("minimum note duration") } SpinBox { - id: splitVolSpin - from: 5; to: 50 - enabled: splitVolChB.checked + id: minDurSpin + from: 50; to: 1000 + stepSize: 10 editable: true } - TlabelText { text: "%" } + TlabelText { text: qsTr("[milliseconds]") } } } + Tile { - visible: advSwitch.checked - description: qsTr("Skips stiller sounds, below given percent of average volume. It prevents detecting of harmonics on classical or acoustic guitar but requires playing with similar strength.") + description: qsTr("Minimum volume of a sound to be pitch-detected") Row { - anchors.horizontalCenter: parent.horizontalCenter spacing: nootkaWindow.fontSize - TcheckBox { - id: skipStillerChB - text: qsTr("skip stiller than") + anchors.horizontalCenter: parent.horizontalCenter + Text { text: qsTr("minimum volume"); anchors.verticalCenter: parent.verticalCenter; color: enabled ? activPal.text : disdPal.text } + Slider { + anchors.verticalCenter: parent.verticalCenter + width: Math.min(nootkaWindow.fontSize * 15, parent.parent.width / 3) + from: 10; to: 80 + value: volSpin.value + onValueChanged: volSpin.value = value + stepSize: 5 } SpinBox { - id: skipStillerSpin - from: 10; to: 95 - enabled: skipStillerChB.checked + id: volSpin + from: 10; to: 80 + value: Math.ceil(GLOB.minVolume * 100) editable: true } TlabelText { text: "%" } } } + + Tile { + enabled: false // TODO + description: qsTr("The base frequency of <i>middle a</i>.<br>Detection of the proper pitch of notes is relative to this value. This also affects the pitch of played sounds.") + Column { + width: parent.width + Row { + spacing: nootkaWindow.fontSize + anchors.horizontalCenter: parent.horizontalCenter + TlabelText { text: qsTr("middle A") } + Text { // staff + height: nootkaWindow.fontSize * 4.5 + y: nootkaWindow.fontSize * -1.6 + font { pixelSize: nootkaWindow.fontSize * 2.5; family: "scorek" } + text: "\ue014\ue014\ue014\ue014" + color: enabled ? activPal.text : disdPal.text + Text { // clef + x: nootkaWindow.fontSize / 4 + y: nootkaWindow.fontSize * -0.6 + color: parent.color + font: parent.font + text: "\ue050" + } + Text { // note + color: parent.color + font: parent.font + x: nootkaWindow.fontSize * 2.7 + y: nootkaWindow.fontSize * -0.9 + text: "\ue1d7" + } + } + SpinBox { + id: freqSpin + anchors.verticalCenter: parent.verticalCenter; + from: 391; to: 493 // in range of two semitones up and down around middle A (440Hz) + value: 440 + editable: true + } + TlabelText { text: qsTr("[Hz]") } + } + } + } + + Frame { // advanced settings + width: parent.width * 0.99 + anchors.horizontalCenter: parent.horizontalCenter + background: Rectangle { + color: "transparent" + border.color: enabled ? "red" : disdPal.text + radius: nootkaWindow.fontSize / 2 + } + Column { + width: parent.width + spacing: nootkaWindow.fontSize + Switch { + id: advSwitch + text: qsTr("Advanced") + checked: false + } + Tile { + visible: advSwitch.checked + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: nootkaWindow.fontSize + TlabelText { text: qsTr("pitch detection mode") } + ComboBox { + id: methodCombo + width: nootkaWindow.fontSize * 20 + model: ["MPM", "autocorrelation", "MPM + modified cepstrum"] + delegate: ItemDelegate { text: modelData } + } + } + } + Tile { + visible: advSwitch.checked + description: qsTr("Helps to properly detect the same notes repeated quickly on the guitar.") + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: nootkaWindow.fontSize + TcheckBox { + id: splitVolChB + anchors.verticalCenter: parent.verticalCenter + text: qsTr("split when volume rise") + } + SpinBox { + id: splitVolSpin + from: 5; to: 50 + enabled: splitVolChB.checked + editable: true + } + TlabelText { text: "%" } + } + } + Tile { + visible: advSwitch.checked + description: qsTr("Skips stiller sounds, below given percent of average volume. It prevents detecting of harmonics on classical or acoustic guitar but requires playing with similar strength.") + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: nootkaWindow.fontSize + TcheckBox { + id: skipStillerChB + anchors.verticalCenter: parent.verticalCenter + text: qsTr("skip stiller than") + } + SpinBox { + id: skipStillerSpin + from: 10; to: 95 + enabled: skipStillerChB.checked + editable: true + } + TlabelText { text: "%" } + } + } + Tile { + visible: advSwitch.checked + description: qsTr("It is rather necessary for mic input but may be switched off for an instrument plugged line-in with less noise.") + TcheckBox { + id: noiseFilterChB + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("noise filter") + } + } + } + } + + } + } + + } + + Tflickable { // "playing" page + contentHeight: outCol.height + contentWidth: width + Column { + id: outCol + width: parent.width + spacing: nootkaWindow.fontSize + + TcheckBox { + id: enableOutChB + text: qsTr("play sound") + anchors.horizontalCenter: parent.horizontalCenter + checked: true + } + + Column { + enabled: enableOutChB.checked + width: parent.width + spacing: nootkaWindow.fontSize + + Row { + spacing: nootkaWindow.fontSize + anchors.horizontalCenter: parent.horizontalCenter + TlabelText { text: qsTr("output device") } + ComboBox { + id: outDevCombo + width: nootkaWindow.fontSize * 20 + model: SOUND.outputDevices() + delegate: ItemDelegate { text: modelData } + } + TcheckBox { + visible: false // TODO + id: jackOutChB + text: "JACK" + anchors.verticalCenter: parent.verticalCenter + } + } Tile { - visible: advSwitch.checked - description: qsTr("It is rather necessary for mic input but may be switched off for an instrument plugged line-in with less noise.") + description: qsTr("All sounds captured by audio input will be forwarded directly to output device.") TcheckBox { - id: noiseFilterChB + id: forwardInChB anchors.horizontalCenter: parent.horizontalCenter - text: qsTr("noise filter") + text: qsTr("forward input to output") } } } } - } } - ScrollBar.vertical: ScrollBar { active: !Noo.isAndroid() } - Component.onCompleted: { - inDevCombo.currentIndex = inDevCombo.find(SOUND.currentInDevName()) - minDurSpin.value = GLOB.minDuration * 1000 + if (inDevCombo.currentIndex === -1) + inDevCombo.model = qsTr("no devices found") + else + inDevCombo.currentIndex = inDevCombo.find(SOUND.currentInDevName()) + minDurSpin.value = Math.ceil(GLOB.minDuration * 1000) methodCombo.currentIndex = GLOB.detectionMethod splitVolChB.checked = GLOB.minSplitVol > 0 splitVolSpin.value = GLOB.minSplitVol skipStillerChB.checked = GLOB.skipStillerVal > 0 skipStillerSpin.value = GLOB.skipStillerVal noiseFilterChB.checked = GLOB.useFilter + enableInChB.checked = GLOB.audioInEnabled + + if (outDevCombo.currentIndex === -1) + outDevCombo.model = qsTr("no devices found") + else + outDevCombo.currentIndex = outDevCombo.find(SOUND.currentOutDevName()) + forwardInChB.checked = GLOB.forwardInput + enableOutChB.checked = GLOB.audioOutEnabled } function save() { - GLOB.inDevName = inDevCombo.currentText - GLOB.minDuration = minDurSpin.value / 1000.0 - GLOB.minVolume = volSpin.value / 100.0 - GLOB.detectionMethod = methodCombo.currentIndex - GLOB.minSplitVol= splitVolChB.checked ? splitVolSpin.value : 0 - GLOB.skipStillerVal = skipStillerChB.checked ? skipStillerSpin.value : 0 - GLOB.useFilter = noiseFilterChB.checked + if (enableInChB.checked) { + GLOB.inDevName = inDevCombo.currentText + GLOB.minDuration = minDurSpin.value / 1000.0 + GLOB.minVolume = volSpin.value / 100.0 + GLOB.detectionMethod = methodCombo.currentIndex + GLOB.minSplitVol = splitVolChB.checked ? splitVolSpin.value : 0 + GLOB.skipStillerVal = skipStillerChB.checked ? skipStillerSpin.value : 0 + GLOB.useFilter = noiseFilterChB.checked + } + GLOB.audioInEnabled = enableInChB.checked + + if (enableOutChB.checked) { + GLOB.outDevName = outDevCombo.currentText + GLOB.forwardInput = forwardInChB.checked + } + GLOB.audioOutEnabled = enableOutChB.checked + SOUND.acceptSettings() } function defaults() { + enableInChB.checked = true + inDevCombo.currentIndex = 0 minDurSpin.value = 150 volSpin.value = 40 freqSpin.value = 440 @@ -248,5 +352,9 @@ Flickable { skipStillerChB.checked = true skipStillerSpin.value = 80 noiseFilterChB.checked = true + + enableOutChB.checked = true + outDevCombo.currentIndex = 0 + forwardInChB.checked = false } }