|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-27 16:38:03
|
Module: editor
Branch: master
Commit: c04ac90c5b36ff67f14f12c16b41e535ec28047b
Author: Lasse Karkkainen <tro...@tr...>
Date: Thu Jan 27 17:36:20 2011 +0100
Keep pitch data in usable and scalable format instead of QPainterPath, do render with proper level, cleanup, do the rendering in PitchVis where it belongs. Timing moved from PitchVis to Analyzer::Moments as it should be.
---
notegraphwidget.cc | 19 +----------------
pitch.cc | 2 +-
pitchvis.cc | 53 +++++++++++++++++++++++++++++++++++++--------------
pitchvis.hh | 31 ++++++-----------------------
4 files changed, 48 insertions(+), 57 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 1287f82..f5bb331 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -30,7 +30,7 @@ NoteGraphWidget::NoteGraphWidget(QWidget *parent)
templabel.close();
setProperty("darkBackground", true);
- setStyleSheet("QLabel[darkBackground=\"true\"] { background: #222; }");
+ setStyleSheet("QLabel[darkBackground=\"true\"] { background: rgb(32, 32, 32); }");
// Initially expanding horizontally to fill the space
QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -145,22 +145,7 @@ void NoteGraphWidget::timerEvent(QTimerEvent*)
}
void NoteGraphWidget::paintEvent(QPaintEvent*) {
- if (m_pitch) {
- QMutexLocker locker(&m_pitch->mutex);
- QPainter painter;
- painter.begin(this);
- painter.setRenderHint(QPainter::Antialiasing);
- QPen pen;
- pen.setColor(Qt::gray);
- pen.setWidth(8);
- pen.setCapStyle(Qt::RoundCap);
- painter.setPen(pen);
- PitchVis::Paths const& paths = m_pitch->getPaths();
- for (PitchVis::Paths::const_iterator it = paths.begin(), itend = paths.end(); it != itend; ++it) {
- painter.drawPath(*it);
- }
- painter.end();
- }
+ if (m_pitch) m_pitch->paint(this);
}
int NoteGraphWidget::getNoteLabelId(NoteLabel* note) const
diff --git a/pitch.cc b/pitch.cc
index 4b81537..cf1a3a8 100644
--- a/pitch.cc
+++ b/pitch.cc
@@ -179,7 +179,7 @@ void Analyzer::temporalMerge(Tones& tones) {
}
}
}
- m_moments.push_back(Moment(0.0));
+ m_moments.push_back(Moment(m_moments.size() * FFT_STEP / m_rate));
m_moments.back().stealTones(tones); // No pointers are invalidated
}
diff --git a/pitchvis.cc b/pitchvis.cc
index 7d5766d..5100d0f 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -5,6 +5,7 @@
#include <fstream>
#include <iostream>
#include <stdexcept>
+#include <QPainter>
#include <QProgressDialog>
#include <QLabel>
@@ -23,15 +24,15 @@ void PitchVis::setWidth(std::size_t w) {
void PitchVis::run()
{
try {
- unsigned scale = 8;
- unsigned int rate = 44100;
+ unsigned pixScale = 8;
+ unsigned rate = 44100;
Analyzer analyzer(rate, "");
{
unsigned step = 1024;
- pixelsPerSecond = scale * rate / step;
+ pixelsPerSecond = pixScale * rate / step;
// Initialize FFmpeg decoding
FFmpeg mpeg(fileName.toStdString(), rate);
- setWidth(scale * mpeg.duration() * rate / step); // Estimation
+ setWidth(pixScale * mpeg.duration() * rate / step); // Estimation
curX = 0;
for (std::vector<float> data(step*2); mpeg.audioQueue(&*data.begin(), &*data.end(), curX * step * 2); ++curX) {
// Mix stereo into mono
@@ -46,7 +47,7 @@ void PitchVis::run()
// Filter the analyzer output data into QPainterPaths.
Analyzer::Moments const& moments = analyzer.getMoments();
curX = 0;
- setWidth(scale * moments.size());
+ setWidth(pixScale * moments.size());
for (Analyzer::Moments::const_iterator it = moments.begin(), itend = moments.end(); it != itend && curX < width(); ++it, ++curX) {
moreAvailable = true;
Moment::Tones const& tones = it->m_tones;
@@ -54,17 +55,16 @@ void PitchVis::run()
if (it2->prev) continue; // The tone doesn't begin at this moment, skip
// Copy the linked list into vector for easier access and calculate max level
std::vector<Tone const*> tones;
- double lmax = 0.0;
- for (Tone const* n = &*it2; n; n = n->next) { tones.push_back(n); lmax = std::max(lmax, n->level); }
- if (tones.size() < 8) continue; // Too short or weak tone, ignored
- QPainterPath path;
- path.lineTo(20, 30);
+ for (Tone const* n = &*it2; n; n = n->next) { tones.push_back(n); }
+ if (tones.size() < 5) continue; // Too short or weak tone, ignored
+ PitchPath path;
+ Analyzer::Moments::const_iterator momit = it;
// Render
- for (unsigned i = 0; i < tones.size(); ++i) {
- //float value = 0.006 * (level2dB(tones[i]->level) + 60.0);
- unsigned x = scale * (curX + i);
- unsigned y = freq2px(tones[i]->freq);
- if (i == 0) path.moveTo(x, y); else path.lineTo(x, y);
+ for (unsigned i = 0; i < tones.size(); ++i, ++momit) {
+ float t = momit->m_time;
+ float n = scale.getNote(tones[i]->freq);
+ float level = level2dB(tones[i]->level);
+ path.push_back(PitchFragment(t, n, level));
}
QMutexLocker locker(&mutex);
paths.push_back(path);
@@ -80,6 +80,29 @@ void PitchVis::run()
curX = width();
}
+void PitchVis::paint(QPaintDevice* widget) {
+ QMutexLocker locker(&mutex);
+ QPainter painter;
+ painter.begin(widget);
+ painter.setRenderHint(QPainter::Antialiasing);
+ QPen pen;
+ pen.setWidth(8);
+ pen.setCapStyle(Qt::RoundCap);
+ PitchVis::Paths const& paths = getPaths();
+ for (PitchVis::Paths::const_iterator it = paths.begin(), itend = paths.end(); it != itend; ++it) {
+ int oldx, oldy;
+ for (PitchPath::const_iterator it2 = it->begin(), it2end = it->end(); it2 != it2end; ++it2) {
+ int x = time2px(it2->time);
+ int y = note2px(it2->note);
+ pen.setColor(QColor(32, clamp<int>(127 + it2->level, 32, 255), 32));
+ painter.setPen(pen);
+ if (it2 != it->begin()) painter.drawLine(oldx, oldy, x, y);
+ oldx = x; oldy = y;
+ }
+ }
+ painter.end();
+}
+
unsigned PitchVis::freq2px(double freq) const { return note2px(scale.getNote(freq)); }
/* static */ unsigned PitchVis::note2px(double tone) { return height - static_cast<unsigned>(16.0 * tone); }
/* static */ double PitchVis::px2note(unsigned px) { return (height - px) / 16.0; }
diff --git a/pitchvis.hh b/pitchvis.hh
index 11fe13f..c317da6 100644
--- a/pitchvis.hh
+++ b/pitchvis.hh
@@ -10,34 +10,17 @@
#include <string>
#include <vector>
-struct Pixel {
- float r,g,b,a;
- Pixel(float r, float g, float b, float a = 1.0f): r(r), g(g), b(b), a(a) {}
- Pixel(): r(), g(), b(), a(1.0f) {}
- static unsigned char conv(float c, float a) {
- return static_cast<unsigned char>(0.5 + 255.0 * clamp(a * std::sqrt(c))); // sqrt(c) is gamma correction
- }
- unsigned rgba() const {
- unsigned char red = conv(r, a);
- unsigned char green = conv(g, a);
- unsigned char blue = conv(b, a);
- unsigned char alpha = conv(1.0f, a);
- return alpha << 24 | red << 16 | green << 8 | blue;
- }
- float& operator[](unsigned idx) { return (&r)[idx]; }
- Pixel& operator+=(Pixel const& pix) {
- r += pix.r;
- g += pix.g;
- b += pix.b;
- a += pix.a;
- return *this;
- }
+struct PitchFragment {
+ float time, note, level; // seconds, MIDI note, dB
+ PitchFragment(float time, float note, float level): time(time), note(note), level(level) {}
};
+typedef std::vector<PitchFragment> PitchPath;
+
class PitchVis: public QWidget, public QThread {
public:
static const std::size_t height = 768;
- typedef std::vector<QPainterPath> Paths;
+ typedef std::vector<PitchPath> Paths;
QMutex mutex;
PitchVis(QString const& filename, QWidget *parent = NULL);
@@ -45,7 +28,7 @@ public:
void run(); // Thread runs here
void stop() { cancelled = true; }
-
+ void paint(QPaintDevice* widget);
Paths const& getPaths() { moreAvailable = false; return paths; }
bool newDataAvailable() const { return moreAvailable; }
int getXValue() const { return curX; }
|