1/**************************************************************************** 2** 3** Copyright (C) 2015 The Qt Company Ltd. 4** Contact: http://www.qt.io/licensing/ 5** 6** This file is part of the QtDeclarative module of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:BSD$ 9** You may use this file under the terms of the BSD license as follows: 10** 11** "Redistribution and use in source and binary forms, with or without 12** modification, are permitted provided that the following conditions are 13** met: 14** * Redistributions of source code must retain the above copyright 15** notice, this list of conditions and the following disclaimer. 16** * Redistributions in binary form must reproduce the above copyright 17** notice, this list of conditions and the following disclaimer in 18** the documentation and/or other materials provided with the 19** distribution. 20** * Neither the name of The Qt Company Ltd nor the names of its 21** contributors may be used to endorse or promote products derived 22** from this software without specific prior written permission. 23** 24** 25** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 36** 37** $QT_END_LICENSE$ 38** 39****************************************************************************/ 40 41import QtQuick 1.0 42import "mycomponents" 43 44/* The base canvas for all QML drawing. */ 45Rectangle { 46 id: application 47 48 property int targetNoteIndex: 0 49 property alias frequency: noteChooser.currentFrequency 50 //Data provided to C++. 51 property bool isInput: true 52 property bool isMuted: false 53 property bool isAuto: true 54 property alias maxVoiceDifference: voiceDifferenceMeter.maxValue 55 property real volume: 0.5 56 property real sensitivity: 0.5 57 58 //Signals to C++. 59 signal volumeChanged(real volume) 60 signal microphoneSensitivityChanged(real sensitivity) 61 signal targetFrequencyChanged(real frequency) 62 signal modeChanged(bool isInput) 63 signal muteStateChanged(bool isMuted) 64 65 //Slots for signals coming from C++. 66 function voiceDifferenceChanged(difference) { 67 if (isAuto) timer.running = true; 68 voiceDifferenceMeter.valueChanged(difference); 69 noteImage.glowing = false 70 } 71 function correctFrequencyObtained() { 72 noteImage.glowing = true 73 } 74 function lowVoice() { 75 noteImage.glowing = false 76 } 77 78 //Private function for changing the target frequency automatically. 79 function calculateTargetFrequency(difference) { 80 var tempDifference = Math.abs(difference); 81 var tempIndex = targetNoteIndex 82 while (!(difference < 0 && tempIndex == 0) && 83 tempDifference >= notes.get(tempIndex-(difference<0)).interval/2) { 84 tempDifference -= notes.get(tempIndex-(difference<0)).interval; 85 tempIndex += difference/Math.abs(difference); 86 } 87 if (tempIndex != targetNoteIndex) { 88 targetNoteIndex = tempIndex 89 noteChooser.currentFrequency = notes.get(targetNoteIndex).frequency; 90 targetFrequencyChanged(frequency); 91 } 92 } 93 94 width: 360; height: 640 95 color: "black" 96 97 //Provides data for functions. 98 NotesModel {id: notes} 99 100 /* A timer for changing the target frequency automatically. 101 * This is needed for avoiding recursion. */ 102 Timer { 103 id: timer 104 105 interval: 1 106 onTriggered: calculateTargetFrequency(voiceDifferenceMeter.value) 107 } 108 109 //A meter for showing the difference between current and target frequency. 110 Meter { 111 id: voiceDifferenceMeter 112 113 maxValue: 12 114 minValue: -maxValue 115 height: imageSize.height/background.sourceSize.height*parent.height 116 width: imageSize.width/background.sourceSize.width*parent.width 117 anchors { 118 topMargin: 100/background.sourceSize.height*parent.height 119 horizontalCenter: parent.horizontalCenter 120 top: parent.top 121 } 122 } 123 124 Image { 125 id: background 126 127 anchors.fill: parent 128 smooth: true 129 source: "./mycomponents/images/guitartuner_skin.png" 130 } 131 132 //A button for quitting the application. 133 Image { 134 id: quitButton 135 136 width: sourceSize.width/background.sourceSize.width*parent.width 137 height: sourceSize.height/background.sourceSize.height*parent.height 138 source: "./mycomponents/images/power.png" 139 smooth: true 140 KeyNavigation.up: volumeAdjuster 141 KeyNavigation.down: modeButton 142 Keys.onEnterPressed: Qt.quit() 143 anchors{ 144 leftMargin: 297/background.sourceSize.width*parent.width 145 left: parent.left; 146 topMargin: 17/background.sourceSize.height*parent.height 147 top: parent.top 148 } 149 150 MouseArea { 151 anchors.fill: parent 152 onClicked: Qt.quit() 153 } 154 } 155 156 //An image for showing the target note. 157 Image { 158 id: noteImage 159 160 property bool glowing: false 161 162 width: sourceSize.width/background.sourceSize.width*parent.width 163 height: sourceSize.height/background.sourceSize.height*parent.height 164 source: glowing ? notes.get(targetNoteIndex).glowSource : notes.get(targetNoteIndex).bigSource 165 166 anchors { 167 topMargin: 273/background.sourceSize.height*parent.height 168 top: parent.top 169 horizontalCenter: parent.horizontalCenter 170 } 171 } 172 173 //A button for choosing the input/output mode. 174 Image { 175 id: modeButton 176 177 function buttonPressed() { 178 isInput = !isInput 179 modeChanged(isInput) 180 if (isInput) { 181 soundIcons.source = "./mycomponents/images/sensitivity.png" 182 source = "./mycomponents/images/voicemode_off.png" 183 volumeAdjuster.setValue(sensitivity) 184 } 185 else { 186 //Change off from "auto" mode 187 if (isAuto) { 188 noteChooser.pushButton(targetNoteIndex) 189 } 190 if (isMuted) { 191 soundIcons.source = "./mycomponents/images/volume_off.png"; 192 } 193 else 194 soundIcons.source = "./mycomponents/images/volume.png" 195 source = "./mycomponents/images/voicemode_on.png" 196 volumeAdjuster.setValue(volume) 197 } 198 } 199 200 width: sourceSize.width/background.sourceSize.width*parent.width 201 height: sourceSize.height/background.sourceSize.height*parent.height 202 smooth: true 203 source: "./mycomponents/images/voicemode_off.png" 204 KeyNavigation.up: quitButton 205 KeyNavigation.down: noteChooser 206 Keys.onEnterPressed: buttonPressed() 207 anchors { 208 leftMargin: 16/background.sourceSize.width*parent.width 209 left: parent.left 210 topMargin: 353/background.sourceSize.height*parent.height 211 top: parent.top 212 } 213 214 MouseArea { 215 anchors.fill: parent 216 onPressed: { 217 parent.focus = true 218 parent.scale = 0.95 219 } 220 onReleased: { 221 parent.scale = 1/0.95 222 } 223 onClicked: parent.buttonPressed() 224 } 225 } 226 227 //Buttons for choosing the target note. 228 NoteButtonView { 229 id: noteChooser 230 231 width: parent.width*0.95; height: width/model.count 232 onNoteSelected: { 233 if (note == "Auto") { 234 if (!isAuto) { 235 isAuto = true 236 } 237 if (!isInput) { 238 modeButton.buttonPressed() 239 } 240 } 241 else { 242 timer.running = false; 243 isAuto = false 244 targetNoteIndex = index 245 targetFrequencyChanged(frequency) 246 } 247 focus = true 248 } 249 KeyNavigation.up: modeButton 250 KeyNavigation.down: soundIcons 251 anchors { 252 horizontalCenter: parent.horizontalCenter 253 topMargin: 454/background.sourceSize.height*parent.height 254 top: parent.top 255 } 256 } 257 258 //An element for showing the mode and changing the mute state. 259 Image { 260 id: soundIcons 261 262 function stateChanged() { 263 isMuted = !isMuted 264 muteStateChanged(isMuted) 265 if (isMuted) { 266 source = "qrc:/src/mycomponents/images/volume_off.png" 267 } 268 else { 269 source = "qrc:/src/mycomponents/images/volume.png" 270 } 271 } 272 273 width: sourceSize.width/background.sourceSize.width*parent.width 274 height: sourceSize.height/background.sourceSize.height*parent.height 275 smooth: true 276 source: "./mycomponents/images/sensitivity.png" 277 Keys.onEnterPressed: stateChanged() 278 KeyNavigation.up: noteChooser 279 KeyNavigation.down: quitButton 280 KeyNavigation.left: volumeAdjuster 281 KeyNavigation.right: volumeAdjuster 282 anchors { 283 leftMargin: 42/background.sourceSize.width*parent.width 284 left: parent.left 285 topMargin: 565/background.sourceSize.height*parent.height 286 top: parent.top 287 } 288 289 MouseArea { 290 anchors.fill: parent 291 onClicked: { 292 if (!isInput) { 293 parent.stateChanged() 294 } 295 parent.focus = true 296 } 297 } 298 } 299 300 //An element for adjusting volume. 301 Adjuster { 302 id: volumeAdjuster 303 304 max: 1 305 value: 0.5 306 width: 222/background.sourceSize.width*parent.width 307 height: parent.height*0.1 308 onFocusChangedByClick: focus = true 309 onArrowPressedWhenValueOverLimits: soundIcons.focus = true 310 KeyNavigation.up: modeButton 311 KeyNavigation.down: quitButton 312 anchors { 313 leftMargin: 98/background.sourceSize.width*parent.width 314 left: parent.left 315 verticalCenter: soundIcons.verticalCenter 316 } 317 onValueChanged: { 318 if (isInput) { 319 sensitivity = value; 320 microphoneSensitivityChanged(1-sensitivity) 321 } 322 else { 323 volume = value 324 volumeChanged(volume) 325 } 326 } 327 } 328} 329