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