1/*
2 * Copyright (c) 2013-2021 Meltytech, LLC
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18import QtQuick 2.12
19import QtQuick.Controls 2.12
20import QtQuick.Layouts 1.12
21import Shotcut.Controls 1.0 as Shotcut
22
23Item {
24    width: 200
25    height: 50
26    property bool blockUpdate: true
27    property double startValue: 0.0
28    property double middleValue: 0.0
29    property double endValue: 0.0
30
31    Component.onCompleted: {
32        if (filter.isNew) {
33            // Set default parameter values
34            filter.set('level', 0)
35            filter.savePreset(preset.parameters)
36        } else {
37            // Convert old version of filter.
38            if (filter.getDouble('gain') !== 0.0) {
39                filter.set('level', toDb(filter.getDouble('gain')))
40                filter.resetProperty('gain')
41            }
42
43            middleValue = filter.getDouble('level', filter.animateIn)
44            if (filter.animateIn > 0)
45                startValue = filter.getDouble('level', 0)
46            if (filter.animateOut > 0)
47                endValue = filter.getDouble('level', filter.duration - 1)
48        }
49        setControls()
50    }
51
52    Connections {
53        target: filter
54        onInChanged: updateFilter(null)
55        onOutChanged: updateFilter(null)
56        onAnimateInChanged: updateFilter(null)
57        onAnimateOutChanged: updateFilter(null)
58    }
59
60    Connections {
61        target: producer
62        onPositionChanged: {
63            if (filter.animateIn > 0 || filter.animateOut > 0) {
64                setControls()
65            } else {
66                blockUpdate = true
67                gainSlider.value = filter.getDouble('level', getPosition())
68                blockUpdate = false
69                gainSlider.enabled = true
70            }
71        }
72    }
73
74    function toDb(value) {
75        return 20 * Math.log(value) / Math.LN10
76    }
77
78    function getPosition() {
79        return Math.max(producer.position - (filter.in - producer.in), 0)
80    }
81
82    function setControls() {
83        var position = getPosition()
84        blockUpdate = true
85        gainSlider.value = filter.getDouble('level', position)
86        blockUpdate = false
87        gainSlider.enabled = position <= 0 || (position >= (filter.animateIn - 1) && position <= (filter.duration - filter.animateOut)) || position >= (filter.duration - 1)
88        gainKeyframesButton.checked = filter.keyframeCount('level') > 0 && filter.animateIn <= 0 && filter.animateOut <= 0
89    }
90
91    function updateFilter(position) {
92        if (blockUpdate) return
93
94        if (position !== null) {
95            if (position <= 0 && filter.animateIn > 0)
96                startValue = gainSlider.value
97            else if (position >= filter.duration - 1 && filter.animateOut > 0)
98                endValue = gainSlider.value
99            else
100                middleValue = gainSlider.value
101        }
102
103        if (filter.animateIn > 0 || filter.animateOut > 0) {
104            filter.resetProperty('level')
105            gainKeyframesButton.checked = false
106            if (filter.animateIn > 0) {
107                filter.set('level', startValue, 0)
108                filter.set('level', middleValue, filter.animateIn - 1)
109            }
110            if (filter.animateOut > 0) {
111                filter.set('level', middleValue, filter.duration - filter.animateOut)
112                filter.set('level', endValue, filter.duration - 1)
113            }
114        } else if (!gainKeyframesButton.checked) {
115            filter.resetProperty('level')
116            filter.set('level', middleValue)
117        } else if (position !== null) {
118            filter.set('level', gainSlider.value, position)
119        }
120    }
121
122    GridLayout {
123        columns: 4
124        anchors.fill: parent
125        anchors.margins: 8
126
127        Label {
128            text: qsTr('Preset')
129            Layout.alignment: Qt.AlignRight
130        }
131        Shotcut.Preset {
132            id: preset
133            Layout.columnSpan: parent.columns - 1
134            parameters: ['level']
135            onBeforePresetLoaded: {
136                filter.resetProperty(parameters[0])
137            }
138            onPresetSelected: {
139                setControls()
140                middleValue = filter.getDouble(parameters[0], filter.animateIn)
141                if (filter.animateIn > 0)
142                    startValue = filter.getDouble(parameters[0], 0)
143                if (filter.animateOut > 0)
144                    endValue = filter.getDouble(parameters[0], filter.duration - 1)
145            }
146        }
147
148        Label {
149            text: qsTr('Level')
150            Layout.alignment: Qt.AlignRight
151        }
152        Shotcut.SliderSpinner {
153            id: gainSlider
154            minimumValue: -70
155            maximumValue: 24
156            suffix: ' dB'
157            decimals: 1
158            onValueChanged: updateFilter(getPosition())
159        }
160        Shotcut.UndoButton {
161            onClicked: gainSlider.value = 0.0
162        }
163        Shotcut.KeyframesButton {
164            id: gainKeyframesButton
165            onToggled: {
166                if (checked) {
167                    blockUpdate = true
168                    filter.clearSimpleAnimation('level')
169                    blockUpdate = false
170                    filter.set('level', gainSlider.value, getPosition())
171                } else {
172                    filter.resetProperty('level')
173                    filter.set('level', gainSlider.value)
174                }
175            }
176        }
177
178        Item {
179            Layout.fillHeight: true
180        }
181    }
182}
183