• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 26643984380

29 May 2026 02:45PM UTC coverage: 34.968% (-0.07%) from 35.037%
26643984380

Pull #2036

github

web-flow
Merge dd2197bec into f9fbc19ef
Pull Request #2036: engine/audio: configurable spectrum grid for Audio Triggers

17 of 188 new or added lines in 5 files covered. (9.04%)

13 existing lines in 4 files now uncovered.

18309 of 52359 relevant lines covered (34.97%)

41042.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.5
/ui/src/audiotriggerwidget.cpp
1
/*
2
  Q Light Controller Plus
3
  audiotriggerwidget.cpp
4

5
  Copyright (c) Massimo Callegari
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QPainter>
21
#include <QDebug>
22
#include <qmath.h>
23

24
#include "audiotriggerwidget.h"
25
#include "qlcmacros.h"
26

27
AudioTriggerWidget::AudioTriggerWidget(QWidget *parent) :
2✔
28
    QWidget(parent)
29
  , m_spectrumBands(NULL)
2✔
30
  , m_spectrumHeight(0)
2✔
31
  , m_volumeBarHeight(0)
2✔
32
  , m_barsNumber(0)
2✔
33
  , m_barWidth(0)
2✔
34
  , m_maxFrequency(0)
2✔
35
  , m_spectrumPixelWidth(0)
2✔
36
  , m_volumeBarPixelWidth(0)
2✔
37
{
38
}
2✔
39

40
AudioTriggerWidget::~AudioTriggerWidget()
2✔
41
{
42
    delete[] m_spectrumBands;
2✔
43
}
2✔
44

45
void AudioTriggerWidget::setBarsNumber(int num)
2✔
46
{
47
    m_barsNumber = num;
2✔
48
    delete[] m_spectrumBands;
2✔
49
    m_spectrumBands = new double[m_barsNumber];
2✔
50
    for (int i = 0; i < m_barsNumber; i++)
6✔
51
        m_spectrumBands[i] = 0;
4✔
52
    m_volumeBarHeight = 0;
2✔
53
    recomputeBarLayout();
2✔
54
    update();
2✔
55
}
2✔
56

57
int AudioTriggerWidget::barsNumber() const
×
58
{
59
    return m_barsNumber;
×
60
}
61

62
void AudioTriggerWidget::setMaxFrequency(int freq)
1✔
63
{
64
    m_maxFrequency = freq;
1✔
65
}
1✔
66

NEW
67
void AudioTriggerWidget::setBandFrequencyEdges(const QVector<double> &edges)
×
68
{
NEW
69
    m_bandEdges = edges;
×
NEW
70
    recomputeBarLayout();
×
NEW
71
    update();
×
NEW
72
}
×
73

74
void AudioTriggerWidget::recomputeBarLayout()
2✔
75
{
76
    const int totalW = qMax(0, width() - 10);
2✔
77
    m_volumeBarPixelWidth = (m_barsNumber > 0) ? float(totalW) / float(m_barsNumber + 1) : 0;
2✔
78
    m_spectrumPixelWidth = float(totalW) - m_volumeBarPixelWidth;
2✔
79

80
    m_barXStart.resize(m_barsNumber);
2✔
81
    m_barPixelWidth.resize(m_barsNumber);
2✔
82

83
    if (m_barsNumber <= 0 || m_spectrumPixelWidth <= 0)
2✔
NEW
84
        return;
×
85

86
    if (m_bandEdges.size() != m_barsNumber + 1)
2✔
87
    {
88
        m_barWidth = m_spectrumPixelWidth / float(m_barsNumber);
2✔
89
        for (int i = 0; i < m_barsNumber; ++i)
6✔
90
        {
91
            m_barXStart[i] = 1.0f + float(i) * m_barWidth;
4✔
92
            m_barPixelWidth[i] = m_barWidth - 1.0f;
4✔
93
        }
94
        return;
2✔
95
    }
96

NEW
97
    const double f0 = qMax(1.0, m_bandEdges.first());
×
NEW
98
    const double fN = qMax(f0 + 1.0, m_bandEdges.last());
×
NEW
99
    const double log0 = qLn(f0);
×
NEW
100
    const double logN = qLn(fN);
×
NEW
101
    const double logSpan = logN - log0;
×
102

NEW
103
    if (logSpan <= 0.0)
×
104
    {
NEW
105
        m_barWidth = m_spectrumPixelWidth / float(m_barsNumber);
×
NEW
106
        for (int i = 0; i < m_barsNumber; ++i)
×
107
        {
NEW
108
            m_barXStart[i] = 1.0f + float(i) * m_barWidth;
×
NEW
109
            m_barPixelWidth[i] = m_barWidth - 1.0f;
×
110
        }
NEW
111
        return;
×
112
    }
113

NEW
114
    for (int i = 0; i < m_barsNumber; ++i)
×
115
    {
NEW
116
        const double logLo = qLn(qMax(1.0, m_bandEdges[i]));
×
NEW
117
        const double logHi = qLn(qMax(1.0, m_bandEdges[i + 1]));
×
NEW
118
        const float xLo = 1.0f + float((logLo - log0) / logSpan) * m_spectrumPixelWidth;
×
NEW
119
        const float xHi = 1.0f + float((logHi - log0) / logSpan) * m_spectrumPixelWidth;
×
NEW
120
        m_barXStart[i] = xLo;
×
NEW
121
        m_barPixelWidth[i] = qMax(1.0f, xHi - xLo - 1.0f);
×
122
    }
123
}
124

125
uchar AudioTriggerWidget::getUcharVolume() const
1✔
126
{
127
    return SCALE(float(m_volumeBarHeight), 0.0, float(m_spectrumHeight), 0.0, 255.0);
1✔
128
}
129

130
uchar AudioTriggerWidget::getUcharBand(int idx)
1✔
131
{
132
    if (idx >= 0 && idx < m_barsNumber)
1✔
133
        return SCALE(float(m_spectrumBands[idx]), 0.0, float(m_spectrumHeight), 0.0, 255.0);
1✔
134

135
    return 0;
×
136
}
137

138
void AudioTriggerWidget::displaySpectrum(double *spectrumData, double maxMagnitude, quint32 power)
1✔
139
{
140
    m_volumeBarHeight = (power * m_spectrumHeight) / 0x7FFF;
1✔
141
    for (int i = 0; i < m_barsNumber; i++)
3✔
142
        m_spectrumBands[i] = (m_volumeBarHeight * spectrumData[i]) / maxMagnitude;
2✔
143

144
    update();
1✔
145
}
1✔
146

147
void AudioTriggerWidget::resizeEvent(QResizeEvent *e)
×
148
{
149
    QWidget::resizeEvent(e);
×
NEW
150
    recomputeBarLayout();
×
151
}
×
152

153
void AudioTriggerWidget::paintEvent(QPaintEvent *e)
×
154
{
155
    QWidget::paintEvent(e);
×
156

157
    if (m_barsNumber == 0)
×
158
        return;
×
159

160
    m_spectrumHeight = height() - 20;
×
161

162
    QPainter painter(this);
×
163

UNCOV
164
    painter.setPen(QPen(Qt::darkGray, 2));
×
165
    if (this->isEnabled())
×
166
        painter.setBrush(QBrush(Qt::black));
×
167
    else
168
        painter.setBrush(QBrush(Qt::gray));
×
NEW
169
    painter.drawRect(0, 0, int(m_spectrumPixelWidth), m_spectrumHeight);
×
170

UNCOV
171
    painter.setBrush(QBrush(Qt::lightGray));
×
NEW
172
    painter.drawRect(int(m_spectrumPixelWidth), 0, int(m_volumeBarPixelWidth), m_spectrumHeight);
×
173

UNCOV
174
    painter.setBrush(QBrush(Qt::darkGray));
×
175
    painter.drawRect(0, m_spectrumHeight + 1, width(), 20);
×
176

177
    painter.setBrush(QBrush(Qt::yellow));
×
178

179
    for (int i = 0; i < m_barsNumber; i++)
×
180
    {
NEW
181
        const float xpos = (i < m_barXStart.size()) ? m_barXStart[i] : 1.0f;
×
NEW
182
        const float barW = (i < m_barPixelWidth.size()) ? m_barPixelWidth[i] : m_barWidth;
×
183

184
        painter.setPen(QPen(Qt::NoPen));
×
NEW
185
        painter.drawRect(int(xpos), m_spectrumHeight - int(m_spectrumBands[i]),
×
NEW
186
                         int(barW), int(m_spectrumBands[i]));
×
187
        painter.setPen(QPen(Qt::lightGray, 1));
×
NEW
188
        painter.drawLine(int(xpos + barW + 1), 0, int(xpos + barW + 1), m_spectrumHeight - 2);
×
189
    }
190

NEW
191
    if (m_bandEdges.size() == m_barsNumber + 1 && m_barsNumber > 0)
×
192
    {
NEW
193
        const double f0 = qMax(1.0, m_bandEdges.first());
×
NEW
194
        const double fN = qMax(f0 + 1.0, m_bandEdges.last());
×
NEW
195
        const double log0 = qLn(f0);
×
NEW
196
        const double logN = qLn(fN);
×
NEW
197
        const double logSpan = logN - log0;
×
198

NEW
199
        if (this->isEnabled())
×
NEW
200
            painter.setPen(QPen(Qt::black, 1));
×
201
        else
NEW
202
            painter.setPen(QPen(Qt::gray, 1));
×
203

NEW
204
        if (logSpan > 0.0)
×
205
        {
NEW
206
            const int tickCount = 10;
×
NEW
207
            for (int t = 1; t <= tickCount; ++t)
×
208
            {
NEW
209
                const double logF = log0 + logSpan * (double(t) / double(tickCount));
×
NEW
210
                const double freq = qExp(logF);
×
NEW
211
                const float xpos = 1.0f + float((logF - log0) / logSpan) * m_spectrumPixelWidth;
×
NEW
212
                if (width() >= 500)
×
NEW
213
                    painter.drawText(int(xpos) - 25, height() - 5, QString("%1Hz").arg(int(freq)));
×
NEW
214
                painter.drawLine(int(xpos), m_spectrumHeight + 1, int(xpos), height());
×
215
            }
216
        }
217
    }
218
    else
219
    {
NEW
220
        float freqIncr = m_maxFrequency / 10.0f;
×
NEW
221
        if (this->isEnabled())
×
NEW
222
            painter.setPen(QPen(Qt::black, 1));
×
223
        else
NEW
224
            painter.setPen(QPen(Qt::gray, 1));
×
225

NEW
226
        for (int i = 1; i < 11; i++)
×
227
        {
NEW
228
            float xpos = (m_spectrumPixelWidth / 10.0f * float(i));
×
NEW
229
            if (width() >= 500)
×
NEW
230
                painter.drawText(int(xpos) - 50, height() - 5, QString("%1Hz").arg(int(freqIncr * i)));
×
NEW
231
            painter.drawLine(int(xpos) - 2, m_spectrumHeight + 1, int(xpos), height());
×
232
        }
233
    }
234

UNCOV
235
    painter.setPen(QPen(Qt::NoPen));
×
236
    painter.setBrush(QBrush(Qt::green));
×
NEW
237
    painter.drawRect(int(m_spectrumPixelWidth) + 1, m_spectrumHeight - int(m_volumeBarHeight),
×
NEW
238
                     int(m_volumeBarPixelWidth) - 2, int(m_volumeBarHeight));
×
UNCOV
239
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc