Skip to content

Commit a476af5

Browse files
committed
Added a time ruler above the audio wave
1 parent 821fc0a commit a476af5

12 files changed

+317
-135
lines changed

Signal.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
#include "WAVFormat.h"
2828
#include "LoopableBuffer.h"
2929

30-
Signal::Signal(QObject *parent)
31-
: QObject(parent),
30+
Signal::Signal(QObject *_parent)
31+
: QObject(_parent),
3232
m_duration(1.0),
3333
m_audio(nullptr),
3434
m_samples(nullptr),
@@ -93,11 +93,11 @@ Signal::~Signal()
9393
delete m_samples;
9494
}
9595

96-
void Signal::setVolume(qreal volume)
96+
void Signal::setVolume(qreal _volume)
9797
{
9898
if(m_audio != nullptr)
9999
{
100-
qreal linearVolume = QAudio::convertVolume(volume,
100+
qreal linearVolume = QAudio::convertVolume(_volume,
101101
QAudio::LogarithmicVolumeScale,
102102
QAudio::LinearVolumeScale);
103103
m_audio->setVolume(linearVolume);
@@ -122,11 +122,11 @@ int Signal::sampleCount()
122122
return 0;
123123
}
124124

125-
qint32 Signal::getSample(int index)
125+
qint32 Signal::getSample(int _index)
126126
{
127-
if(m_samples != nullptr && index * 4 >= 0 && index * 4 <= m_samples->size() - 4)
127+
if(m_samples != nullptr && _index * 4 >= 0 && _index * 4 <= m_samples->size() - 4)
128128
{
129-
return qFromLittleEndian<qint32>(m_samples->constData() + (index * 4));
129+
return qFromLittleEndian<qint32>(m_samples->constData() + (_index * 4));
130130
}
131131
return 0;
132132
}
@@ -160,15 +160,15 @@ void Signal::generate()
160160
emit signalChanged();
161161
}
162162

163-
void Signal::exportWAV(QString fileName)
163+
void Signal::exportWAV(QString _fileName)
164164
{
165165
WAVFormat wav(1, 1, m_sampleRate, 32);
166-
wav.writeToFile(fileName, m_samples);
166+
wav.writeToFile(_fileName, m_samples);
167167
}
168168

169-
void Signal::loop(bool enable)
169+
void Signal::loop(bool _enable)
170170
{
171-
m_buffer->setLoop(enable);
171+
m_buffer->setLoop(_enable);
172172
}
173173

174174
void Signal::play()
@@ -213,9 +213,9 @@ void Signal::toEnd()
213213
}
214214

215215

216-
void Signal::handleStateChanged(QAudio::State newState)
216+
void Signal::handleStateChanged(QAudio::State _newState)
217217
{
218-
switch (newState) {
218+
switch (_newState) {
219219
case QAudio::IdleState:
220220
// Finished playing (no more data)
221221
m_audio->stop();

Signal.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,17 @@ class Signal : public QObject
3737
Signal(QObject* parent = nullptr);
3838
virtual ~Signal() override;
3939

40-
void setComponent(Component* component) { m_component = component; }
41-
Component* component() { return m_component; }
40+
inline void setComponent(Component* _component) { m_component = _component; }
41+
inline Component* component() { return m_component; }
4242

43-
void setVolume(qreal volume);
43+
void setVolume(qreal _volume);
4444
qreal volume();
4545

46-
void setDuration(qreal duration) { m_duration = duration; }
47-
qreal duration() { return m_duration; }
46+
inline void setDuration(qreal _duration) { m_duration = _duration; }
47+
inline qreal duration() { return m_duration; }
4848
int sampleCount();
49-
qint32 getSample(int index);
49+
qint32 getSample(int _index);
50+
inline int getSampleRate() { return m_sampleRate; }
5051

5152
signals:
5253
void signalChanged();
@@ -59,11 +60,11 @@ public slots:
5960
void toStart();
6061
void toEnd();
6162
void generate();
62-
void exportWAV(QString fileName);
63-
void loop(bool enable);
63+
void exportWAV(QString _fileName);
64+
void loop(bool _enable);
6465

6566
private slots:
66-
void handleStateChanged(QAudio::State newState);
67+
void handleStateChanged(QAudio::State _newState);
6768

6869
private:
6970
int m_sampleRate;

SoundGenerator.pro

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ SOURCES += \
6666
UI/WaveFormChunk.cpp \
6767
UI/WaveFormView.cpp \
6868
LoopableBuffer.cpp \
69-
ActionCycle.cpp
69+
ActionCycle.cpp \
70+
UI/TimeRuler.cpp
7071

7172
HEADERS += \
7273
Components/AddComponent.h \
@@ -108,7 +109,8 @@ HEADERS += \
108109
UI/WaveFormChunk.h \
109110
UI/WaveFormView.h \
110111
LoopableBuffer.h \
111-
ActionCycle.h
112+
ActionCycle.h \
113+
UI/TimeRuler.h
112114

113115
FORMS += \
114116
UI/mainwindow.ui

UI/MainWindow.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ MainWindow::MainWindow(QWidget* _parent)
5757

5858

5959
ui->buttonLayout->setAlignment(Qt::Alignment(Qt::AlignTop | Qt::AlignHCenter));
60-
ui->waveFormView->setScrollBar(ui->waveFormScrollBar);
6160
ui->waveFormView->setSignal(&m_signal);
61+
connect(ui->waveFormView, &WaveFormView::zoomChanged, this, &MainWindow::onWaveFormViewZoomChanged);
62+
connect(ui->waveFormScrollBar, &QScrollBar::valueChanged, this, &MainWindow::onScrollbarValueChanged);
6263

6364
m_scene = new NodalScene(ui->nodalView);
6465
m_scene->setBackgroundBrush(Qt::black);
@@ -540,6 +541,36 @@ void MainWindow::onOutputChanged()
540541
}
541542
}
542543

544+
void MainWindow::onWaveFormViewZoomChanged()
545+
{
546+
int total = ui->waveFormView->getNbTotalSample();
547+
int offset = ui->waveFormView->getSampleOffset();
548+
int viewSize = ui->waveFormView->getNbSampleViewed();
549+
550+
// update scrollbar
551+
ui->waveFormScrollBar->setMinimum(0);
552+
ui->waveFormScrollBar->setMaximum(qMax(0, total - viewSize));
553+
ui->waveFormScrollBar->setPageStep(viewSize);
554+
ui->waveFormScrollBar->setValue((viewSize < total) ? offset : 0);
555+
556+
// update time ruler
557+
qreal sampleDuration = 1.0 / static_cast<qreal>(m_signal.getSampleRate());
558+
ui->timeRuler->setTimeWindow(offset * sampleDuration, (offset + viewSize) * sampleDuration);
559+
}
560+
561+
void MainWindow::onScrollbarValueChanged()
562+
{
563+
int offset = ui->waveFormScrollBar->value();
564+
int viewSize = ui->waveFormScrollBar->pageStep();
565+
566+
// update wave form view
567+
ui->waveFormView->setSampleOffset(offset);
568+
569+
// update time ruler
570+
qreal sampleDuration = 1.0 / static_cast<qreal>(m_signal.getSampleRate());
571+
ui->timeRuler->setTimeWindow(offset * sampleDuration, (offset + viewSize) * sampleDuration);
572+
}
573+
543574
void MainWindow::createActions()
544575
{
545576

UI/MainWindow.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ public slots:
7777
void changeAutoGenerate(QAction* _action);
7878
void onOutputChanged();
7979

80+
void onWaveFormViewZoomChanged();
81+
void onScrollbarValueChanged();
82+
8083
private:
8184
void createActions();
8285
void createMenus();

UI/TimeRuler.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2020 Benoit Pelletier
3+
*
4+
* This file is part of Sound Generator.
5+
*
6+
* Sound Generator is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Sound Generator is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Sound Generator. If not, see <https://www.gnu.org/licenses/>.
18+
*
19+
*/
20+
21+
#include "TimeRuler.h"
22+
#include <QPainter>
23+
#include <qmath.h>
24+
#include "Utils.h"
25+
#include <math.h>
26+
27+
TimeRuler::TimeRuler(QWidget *_parent)
28+
: QWidget(_parent)
29+
{
30+
m_startTime = 0.0;
31+
m_endTime = 1.0;
32+
}
33+
34+
void TimeRuler::setTimeWindow(qreal _startTime, qreal _endTime)
35+
{
36+
m_startTime = _startTime;
37+
m_endTime = _endTime;
38+
39+
update();
40+
}
41+
42+
void TimeRuler::paintEvent(QPaintEvent *_event)
43+
{
44+
Q_UNUSED(_event);
45+
46+
QPen pen;
47+
pen.setColor(QColor(200, 200, 200));
48+
pen.setStyle(Qt::PenStyle::SolidLine);
49+
50+
QPainter painter(this);
51+
painter.setPen(pen);
52+
53+
qreal pixelPerSecond = size().width() / (m_endTime - m_startTime);
54+
qreal minDistBetweenTimecodes = 200.0;
55+
qreal timeInterval = 1.0 / qPow(2, qFloor(log2(pixelPerSecond / minDistBetweenTimecodes)));
56+
qreal pixelInterval = timeInterval * pixelPerSecond;
57+
qreal firstTimeCode = qFloor(m_startTime / timeInterval) * timeInterval;
58+
int pixelOffset = qRound((firstTimeCode - m_startTime) * pixelPerSecond);
59+
int nbLine = qCeil(size().width() / pixelInterval) + 1;
60+
int nbSubdiv = 10;
61+
62+
QVector<QLineF> lines(nbLine * nbSubdiv);
63+
QRectF textRect;
64+
QPointF pos;
65+
qreal minY = 15.0;
66+
qreal maxY = static_cast<qreal>(size().height());
67+
68+
// main divisions with timecodes
69+
for(int i = 0; i < nbLine; ++i)
70+
{
71+
pos.setX(static_cast<qreal>(qRound(i * pixelInterval + pixelOffset)));
72+
textRect.moveCenter(pos);
73+
painter.drawText(textRect, Qt::AlignHCenter | Qt::AlignTop | Qt::TextDontClip
74+
, Utils::FormatTimeCode(firstTimeCode + i * timeInterval));
75+
76+
lines[i * nbSubdiv].setLine(pos.x(), minY, pos.x(), maxY);
77+
}
78+
79+
// sub division (N subdivisions, so N-1 lines in each division)
80+
for(int i = 0; i < nbLine * nbSubdiv; ++i)
81+
{
82+
if(i % nbSubdiv != 0)
83+
{
84+
qreal x = static_cast<qreal>(qRound(i * pixelInterval / static_cast<qreal>(nbSubdiv) + pixelOffset));
85+
lines[i].setLine(x, minY + 5, x, maxY);
86+
}
87+
}
88+
89+
painter.drawLines(lines);
90+
}

UI/TimeRuler.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2020 Benoit Pelletier
3+
*
4+
* This file is part of Sound Generator.
5+
*
6+
* Sound Generator is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Sound Generator is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Sound Generator. If not, see <https://www.gnu.org/licenses/>.
18+
*
19+
*/
20+
21+
#ifndef TIMERULER_H
22+
#define TIMERULER_H
23+
24+
#include <QWidget>
25+
26+
class TimeRuler : public QWidget
27+
{
28+
Q_OBJECT
29+
30+
public:
31+
TimeRuler(QWidget* _parent = nullptr);
32+
33+
void setTimeWindow(qreal _startTime, qreal _endTime);
34+
inline qreal getStartTime() { return m_startTime; }
35+
inline qreal getEndTime() { return m_endTime; }
36+
37+
public slots:
38+
virtual void paintEvent(QPaintEvent* _event) override;
39+
40+
signals:
41+
void onTimeSelected(qreal _time);
42+
43+
private:
44+
qreal m_startTime = 0.0;
45+
qreal m_endTime = 0.0;
46+
};
47+
48+
#endif // TIMERULER_H

0 commit comments

Comments
 (0)