You can subscribe to this list here.
| 2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(25) |
Jul
(288) |
Aug
(119) |
Sep
(31) |
Oct
(59) |
Nov
(458) |
Dec
(359) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2010 |
Jan
(268) |
Feb
(26) |
Mar
(36) |
Apr
(48) |
May
(119) |
Jun
(37) |
Jul
(173) |
Aug
(429) |
Sep
(137) |
Oct
(156) |
Nov
(59) |
Dec
(45) |
| 2011 |
Jan
(398) |
Feb
(257) |
Mar
(49) |
Apr
(5) |
May
(34) |
Jun
(11) |
Jul
(38) |
Aug
(12) |
Sep
(1) |
Oct
(49) |
Nov
(5) |
Dec
(10) |
| 2012 |
Jan
(21) |
Feb
(32) |
Mar
(20) |
Apr
(1) |
May
(2) |
Jun
|
Jul
(173) |
Aug
|
Sep
(25) |
Oct
(6) |
Nov
(44) |
Dec
|
|
From: Tapio V. <aa...@us...> - 2011-02-23 11:24:11
|
Author: Tapio Vierros <tap...@gm...>
Date: Wed Feb 23 13:22:55 2011 +0200
Fixed Piano-NoteGraph sync.
---
editorapp.cc | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 48edf71..cf9ab5b 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -938,16 +938,17 @@ Piano::Piano(QWidget *parent): QLabel(parent) {}
void Piano::updatePixmap(int noteHeight)
{
- const int notes = 12*6;
+ const int notes = 12 * 4; // Four octaves
QImage image(50, notes * noteHeight, QImage::Format_ARGB32_Premultiplied);
image.fill(qRgba(0, 0, 0, 0));
+ setFixedSize(image.width(), image.height());
{
QPainter painter(&image);
MusicalScale scale;
QPen pen; pen.setWidth(2); pen.setColor(QColor("#c0c0c0"));
painter.setPen(pen);
for (int i = 0; i < notes; ++i) {
- bool sh = scale.isSharp(i);
+ bool sh = scale.isSharp(i); // Sharp notes have black and shorter keys
QColor background(sh ? "#000000" : "#ffffff");
painter.fillRect(0, image.height() - i*noteHeight - noteHeight/2, image.width() * (sh ? 0.8 : 1.0), noteHeight, background);
painter.drawRect(0, image.height() - i*noteHeight - noteHeight/2, image.width(), noteHeight);
|
|
From: Tapio V. <aa...@us...> - 2011-02-23 10:27:53
|
Author: Tapio Vierros <tap...@gm...>
Date: Wed Feb 23 12:26:56 2011 +0200
Shorten black piano keys.
---
editorapp.cc | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index a185764..48edf71 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -947,8 +947,9 @@ void Piano::updatePixmap(int noteHeight)
QPen pen; pen.setWidth(2); pen.setColor(QColor("#c0c0c0"));
painter.setPen(pen);
for (int i = 0; i < notes; ++i) {
- QColor background(scale.isSharp(i) ? "#000000" : "#ffffff");
- painter.fillRect(0, image.height() - i*noteHeight - noteHeight/2, image.width(), noteHeight, background);
+ bool sh = scale.isSharp(i);
+ QColor background(sh ? "#000000" : "#ffffff");
+ painter.fillRect(0, image.height() - i*noteHeight - noteHeight/2, image.width() * (sh ? 0.8 : 1.0), noteHeight, background);
painter.drawRect(0, image.height() - i*noteHeight - noteHeight/2, image.width(), noteHeight);
}
}
|
|
From: Tapio V. <aa...@us...> - 2011-02-22 18:24:52
|
Author: Tapio Vierros <tap...@gm...>
Date: Tue Feb 22 20:24:04 2011 +0200
Detect if user tries to load song file as plain text and ask for action.
---
editorapp.cc | 12 +++++++++++-
songparser.hh | 13 +++++++++----
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 82061d9..a185764 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -18,6 +18,7 @@
#include "editorapp.hh"
#include "notelabel.hh"
#include "notegraphwidget.hh"
+#include "songparser.hh"
#include "songwriter.hh"
#include "textcodecselector.hh"
#include "gettingstarted.hh"
@@ -633,7 +634,16 @@ void EditorApp::on_actionLyricsFromFile_triggered()
return;
QString text = TextCodecSelector::readAllAndHandleEncoding(file, this);
- if (text != "") noteGraph->setLyrics(text);
+ if (text != "") {
+ if (SongParser::looksLikeSongFile(text)
+ && QMessageBox::question(this, tr("Song file detected"),
+ tr("The file you are opening doesn't look like plain text lyrics, but rather an actual song file. Would you like to reload it as such?"),
+ QMessageBox::Yes | QMessageBox::No)
+ == QMessageBox::Yes)
+ {
+ openFile(fileName);
+ } else noteGraph->setLyrics(text);
+ }
}
}
}
diff --git a/songparser.hh b/songparser.hh
index f791923..2aa7cc0 100644
--- a/songparser.hh
+++ b/songparser.hh
@@ -13,6 +13,11 @@ class SongParser {
public:
/// constructor
SongParser(Song& s);
+
+ static bool looksLikeSongFile(QString const& data) {
+ return txtCheck(data) || xmlCheck(data) || iniCheck(data) || smCheck(data);
+ }
+
private:
void finalize();
@@ -24,22 +29,22 @@ class SongParser {
double m_gap;
// UltraStar TXT
- bool txtCheck(QString const& data);
+ static bool txtCheck(QString const& data);
void txtParse();
bool txtParseField(QString const& line);
bool txtParseNote(QString line, VocalTrack &vocal);
// SingStar XML
- bool xmlCheck(QString const& data);
+ static bool xmlCheck(QString const& data);
void xmlParse();
- bool iniCheck(QString const& data);
+ static bool iniCheck(QString const& data);
void iniParse();
void iniParseField(QString const& line);
void midParse();
// FIXME: Dummy funcs
- bool smCheck(QString const& data) { (void)data; return false; }
+ static bool smCheck(QString const& data) { (void)data; return false; }
void smParse() { }
bool smParseField(std::string line) { (void)line; return false; }
Notes smParseNotes(std::string line) { (void)line; return Notes(); }
|
|
From: Tapio V. <aa...@us...> - 2011-02-22 17:37:13
|
Author: Tapio Vierros <tap...@gm...>
Date: Tue Feb 22 19:37:18 2011 +0200
Refactoring piano, still broken.
---
editorapp.cc | 47 ++++++++++++++++++++++++++---------------------
editorapp.hh | 5 ++++-
2 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 77e117c..82061d9 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -99,18 +99,21 @@ EditorApp::EditorApp(QWidget *parent)
connect(player, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(playerStateChanged(Phonon::State,Phonon::State)));
connect(player, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
- // NoteGraph setup down here so that the objects we setup signals are already created
- setupNoteGraph();
- updateNoteInfo(NULL);
-
- song.reset(new Song);
-
// The piano keys
- piano = new Piano(noteGraph, ui.topFrame);
+ piano = new Piano(ui.topFrame);
QHBoxLayout *hl = new QHBoxLayout(ui.topFrame);
hl->addWidget(piano);
hl->addWidget(ui.noteGraphScroller);
ui.topFrame->setLayout(hl);
+ connect(ui.noteGraphScroller->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updatePiano(int)));
+
+ // NoteGraph setup down here so that the objects we setup signals are already created
+ setupNoteGraph();
+ updateNoteInfo(NULL);
+
+ piano->updatePixmap(noteGraph->n2px(0) - noteGraph->n2px(1));
+
+ song.reset(new Song);
// Set status tips to tool tips
handleTips(ui.tabNote);
@@ -882,6 +885,7 @@ void EditorApp::writeSettings()
}
+
AboutDialog::AboutDialog(QWidget* parent)
: QDialog(parent)
{
@@ -914,27 +918,28 @@ AboutDialog::AboutDialog(QWidget* parent)
-Piano::Piano(NoteGraphWidget *ngw, QWidget *parent)
- : QLabel(parent)
+void EditorApp::updatePiano(int y)
+{
+ if (!piano) return;
+ piano->move(piano->x(), ui.noteGraphScroller->y() - y);
+}
+
+Piano::Piano(QWidget *parent): QLabel(parent) {}
+
+void Piano::updatePixmap(int noteHeight)
{
- QImage image(50, 768, QImage::Format_ARGB32_Premultiplied);
+ const int notes = 12*6;
+ QImage image(50, notes * noteHeight, QImage::Format_ARGB32_Premultiplied);
image.fill(qRgba(0, 0, 0, 0));
{
QPainter painter(&image);
- // Piano
MusicalScale scale;
- QColor background;
- int note_height = ngw->n2px(0) - ngw->n2px(1);
QPen pen; pen.setWidth(2); pen.setColor(QColor("#c0c0c0"));
painter.setPen(pen);
- for (int i = 1; i < 12*4; ++i) {
- if(scale.isSharp(i)) {
- background = QColor("#000000");
- } else {
- background = QColor("#ffffff");
- }
- painter.fillRect(0, ngw->n2px(i)-note_height/2, image.width(), note_height, background);
- painter.drawRect(0, ngw->n2px(i)-note_height/2, image.width(), note_height);
+ for (int i = 0; i < notes; ++i) {
+ QColor background(scale.isSharp(i) ? "#000000" : "#ffffff");
+ painter.fillRect(0, image.height() - i*noteHeight - noteHeight/2, image.width(), noteHeight, background);
+ painter.drawRect(0, image.height() - i*noteHeight - noteHeight/2, image.width(), noteHeight);
}
}
setPixmap(QPixmap::fromImage(image));
diff --git a/editorapp.hh b/editorapp.hh
index 7a2bde5..aa01cd9 100644
--- a/editorapp.hh
+++ b/editorapp.hh
@@ -31,7 +31,9 @@ class Piano: public QLabel
{
Q_OBJECT
public:
- Piano(NoteGraphWidget *ngw, QWidget *parent = 0);
+ Piano(QWidget *parent = 0);
+public slots:
+ void updatePixmap(int noteHeight);
};
@@ -68,6 +70,7 @@ public slots:
void audioTick(qint64 time);
void playerStateChanged(Phonon::State newstate, Phonon::State olstate);
void statusBarMessage(const QString& message);
+ void updatePiano(int y);
// Automatic slots
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-22 17:32:29
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Tue Feb 22 17:10:38 2011 +0100 Fix seekhandle rendering on OS X and use a more simplistic look for it. --- notegraphwidget.cc | 17 +---------------- 1 files changed, 1 insertions(+), 16 deletions(-) diff --git a/notegraphwidget.cc b/notegraphwidget.cc index fffdefa..0c8e03d 100644 --- a/notegraphwidget.cc +++ b/notegraphwidget.cc @@ -697,22 +697,7 @@ SeekHandle::SeekHandle(QWidget *parent) : QLabel(parent) { QImage image(16, 768, QImage::Format_ARGB32_Premultiplied); - image.fill(qRgba(0, 0, 0, 0)); - QLinearGradient gradient(0, 0, image.width()-1, 0); - gradient.setColorAt(0.00, QColor(255,255,0,0)); - gradient.setColorAt(0.25, QColor(255,255,0,0)); - gradient.setColorAt(0.50, QColor(255,255,0,200)); - gradient.setColorAt(0.75, QColor(255,255,0,0)); - gradient.setColorAt(1.00, QColor(255,255,0,0)); - - { - QPainter painter(&image); - painter.setRenderHint(QPainter::Antialiasing); - painter.setBrush(gradient); - painter.setPen(Qt::NoPen); - painter.drawRect(QRect(0, 0, image.width(), image.height())); - } - + image.fill(qRgba(128, 128, 128, 128)); setPixmap(QPixmap::fromImage(image)); setMouseTracking(true); setStatusTip(tr("Seek by dragging")); |
|
From: Tapio V. <aa...@us...> - 2011-02-22 16:33:22
|
Author: Tapio Vierros <tap...@gm...> Date: Tue Feb 22 18:33:08 2011 +0200 Moved piano keys outside of note graph; not yet connected (so it's dummy). --- editor.ui | 853 ++++++++++++++++++++++++++-------------------------- editorapp.cc | 36 +++ editorapp.hh | 8 + notegraphwidget.cc | 16 - 4 files changed, 463 insertions(+), 450 deletions(-) |
|
From: Tapio V. <aa...@us...> - 2011-02-22 15:08:15
|
Author: Tapio Vierros <tap...@gm...>
Date: Tue Feb 22 17:07:33 2011 +0200
Use RAII QPainter.
Possibly fixes SeekHandle not showing on Mac (was missing end()).
---
notegraphwidget.cc | 18 ++++++++----------
notelabel.cc | 27 +++++++++++++--------------
pitchvis.cc | 52 ++++++++++++++++++++++++++--------------------------
3 files changed, 47 insertions(+), 50 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 737dc52..c649ba8 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -180,8 +180,7 @@ void NoteGraphWidget::paintEvent(QPaintEvent*) {
int x1, y1, x2, y2;
calcViewport(x1, y1, x2, y2);
- QPainter painter;
- painter.begin(this);
+ QPainter painter(this);
// PitchVis pixmap
if (!m_pixmap.isNull())
@@ -216,8 +215,6 @@ void NoteGraphWidget::paintEvent(QPaintEvent*) {
painter.setPen(pen);
painter.drawRect(QRect(m_mouseHotSpot, mousep));
}
-
- painter.end();
}
void NoteGraphWidget::updatePixmap(const QImage &image, const QPoint &position)
@@ -724,12 +721,13 @@ SeekHandle::SeekHandle(QWidget *parent)
gradient.setColorAt(0.75, QColor(255,255,0,0));
gradient.setColorAt(1.00, QColor(255,255,0,0));
- QPainter painter;
- painter.begin(&image);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setBrush(gradient);
- painter.setPen(Qt::NoPen);
- painter.drawRect(QRect(0, 0, image.width(), image.height()));
+ {
+ QPainter painter(&image);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setBrush(gradient);
+ painter.setPen(Qt::NoPen);
+ painter.drawRect(QRect(0, 0, image.width(), image.height()));
+ }
setPixmap(QPixmap::fromImage(image));
setMouseTracking(true);
diff --git a/notelabel.cc b/notelabel.cc
index ffc0d03..d551e5d 100644
--- a/notelabel.cc
+++ b/notelabel.cc
@@ -55,24 +55,23 @@ void NoteLabel::createPixmap()
gradient.setColorAt(1.0, QColor(100 * ff, 120 * ff, 100 * ff, alpha));
}
- QPainter painter;
- painter.begin(&image);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setPen(isSelected() ? Qt::red : Qt::black); // Hilight selected note
- painter.setBrush(gradient);
- painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1), 8, 8);
-
- painter.setFont(font);
- painter.drawText(QRect(QPoint(text_margin, text_margin), QSize(size.width()-text_margin, size.height()-text_margin)), Qt::AlignCenter, lyric());
-
- // Render sentence end indicator
- if (m_note.lineBreak) {
- painter.setPen(QPen(QBrush(QColor(255, 0, 0)), 4));
- painter.drawLine(2, 0, 2, image.height()-1);
+ {
+ QPainter painter(&image);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setPen(isSelected() ? Qt::red : Qt::black); // Hilight selected note
+ painter.setBrush(gradient);
+ painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1), 8, 8);
+
+ painter.setFont(font);
+ painter.drawText(QRect(QPoint(text_margin, text_margin), QSize(size.width()-text_margin, size.height()-text_margin)), Qt::AlignCenter, lyric());
+
+ // Render sentence end indicator
+ if (m_note.lineBreak) {
+ painter.setPen(QPen(QBrush(QColor(255, 0, 0)), 4));
+ painter.drawLine(2, 0, 2, image.height()-1);
+ }
}
- painter.end();
-
setPixmap(QPixmap::fromImage(image));
setStatusTip(tr("Lyric: ") + lyric());
diff --git a/pitchvis.cc b/pitchvis.cc
index c799967..8383f49 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -144,34 +144,34 @@ void PitchVis::renderer() {
QSettings settings; // Default QSettings parameters given in main()
bool aa = settings.value("anti-aliasing", true).toBool();
- QPainter painter;
- painter.begin(&image);
- if (aa) painter.setRenderHint(QPainter::Antialiasing);
- painter.fillRect(image.rect(), QColor(NoteGraphWidget::BGColor)); // Otherwise the image will have all kinds of carbage
-
- 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) {
- PitchPath::Fragments const& fragments = it->fragments;
- int oldx, oldy;
- // Only render paths in view
- if (widget->s2px(fragments.back().time) < x1) continue;
- else if (widget->s2px(fragments.front().time) > x2) break;
- // Iterate through the path points
- for (PitchPath::Fragments::const_iterator it2 = fragments.begin(), it2end = fragments.end(); it2 != it2end; ++it2) {
- // TODO: Take y-size into account (change also the paint calls in NoteGraphWidget)
- int x = widget->s2px(it2->time) - x1;
- int y = widget->n2px(it2->note);
- pen.setColor(QColor(32 + 64 * it->channel, clamp<int>(127 + it2->level, 32, 255), 32, 128));
- painter.setPen(pen);
- if (it2 != fragments.begin()) painter.drawLine(oldx, oldy, x, y);
- oldx = x; oldy = y;
+ {
+ QPainter painter(&image);
+ if (aa) painter.setRenderHint(QPainter::Antialiasing);
+ painter.fillRect(image.rect(), QColor(NoteGraphWidget::BGColor)); // Otherwise the image will have all kinds of carbage
+
+ 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) {
+ PitchPath::Fragments const& fragments = it->fragments;
+ int oldx, oldy;
+ // Only render paths in view
+ if (widget->s2px(fragments.back().time) < x1) continue;
+ else if (widget->s2px(fragments.front().time) > x2) break;
+ // Iterate through the path points
+ for (PitchPath::Fragments::const_iterator it2 = fragments.begin(), it2end = fragments.end(); it2 != it2end; ++it2) {
+ // TODO: Take y-size into account (change also the paint calls in NoteGraphWidget)
+ int x = widget->s2px(it2->time) - x1;
+ int y = widget->n2px(it2->note);
+ pen.setColor(QColor(32 + 64 * it->channel, clamp<int>(127 + it2->level, 32, 255), 32, 128));
+ painter.setPen(pen);
+ if (it2 != fragments.begin()) painter.drawLine(oldx, oldy, x, y);
+ oldx = x; oldy = y;
+ }
}
}
- painter.end();
// Send the image
// This is actually delivered by the reciever's event loop thread, and not called directly from here
|
|
From: Tapio V. <aa...@us...> - 2011-02-22 13:01:13
|
Author: Tapio Vierros <tap...@gm...>
Date: Tue Feb 22 15:00:26 2011 +0200
Added AboutQt dialog.
---
editor.ui | 8 +++++++-
editorapp.cc | 5 +++++
editorapp.hh | 1 +
3 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/editor.ui b/editor.ui
index 80cab1e..9c2e5b2 100644
--- a/editor.ui
+++ b/editor.ui
@@ -47,7 +47,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>534</width>
+ <width>674</width>
<height>227</height>
</rect>
</property>
@@ -548,6 +548,7 @@
<addaction name="actionGettingStarted"/>
<addaction name="actionWhatsThis"/>
<addaction name="separator"/>
+ <addaction name="actionAboutQt"/>
<addaction name="actionAbout"/>
</widget>
<widget class="QMenu" name="menuInsert">
@@ -806,6 +807,11 @@
<string>F1</string>
</property>
</action>
+ <action name="actionAboutQt">
+ <property name="text">
+ <string>About Qt...</string>
+ </property>
+ </action>
</widget>
<resources/>
<connections>
diff --git a/editorapp.cc b/editorapp.cc
index 1a6a054..4fc7a00 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -683,6 +683,11 @@ void EditorApp::on_actionWhatsThis_triggered()
QWhatsThis::enterWhatsThisMode();
}
+void EditorApp::on_actionAboutQt_triggered()
+{
+ QApplication::aboutQt();
+}
+
void EditorApp::on_actionAbout_triggered()
{
AboutDialog aboutDialog(this);
diff --git a/editorapp.hh b/editorapp.hh
index 99f528e..50174aa 100644
--- a/editorapp.hh
+++ b/editorapp.hh
@@ -101,6 +101,7 @@ public slots:
void on_actionGettingStarted_triggered();
void on_actionWhatsThis_triggered();
void on_actionAbout_triggered();
+ void on_actionAboutQt_triggered();
// Note properties tab
void on_txtTitle_editingFinished();
|
|
From: Tapio V. <aa...@us...> - 2011-02-22 13:01:07
|
Author: Tapio Vierros <tap...@gm...>
Date: Tue Feb 22 14:11:06 2011 +0200
Show time and note of mouse cursor position at status bar.
---
editorapp.cc | 6 ++++++
editorapp.hh | 1 +
notegraphwidget.cc | 2 ++
notegraphwidget.hh | 1 +
4 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 366a290..1a6a054 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -133,6 +133,7 @@ void EditorApp::setupNoteGraph()
// Signals/slots
connect(noteGraph, SIGNAL(operationDone(const Operation&)), this, SLOT(operationDone(const Operation&)));
connect(noteGraph, SIGNAL(updateNoteInfo(NoteLabel*)), this, SLOT(updateNoteInfo(NoteLabel*)));
+ connect(noteGraph, SIGNAL(statusBarMessage(QString)), this, SLOT(statusBarMessage(QString)));
connect(statusbarButton, SIGNAL(clicked()), noteGraph, SLOT(abortPitch()));
connect(ui.noteGraphScroller->horizontalScrollBar(), SIGNAL(valueChanged(int)), noteGraph, SLOT(updatePitch()));
connect(ui.noteGraphScroller->verticalScrollBar(), SIGNAL(valueChanged(int)), noteGraph, SLOT(updatePitch()));
@@ -162,6 +163,11 @@ void EditorApp::operationDone(const Operation &op)
redoStack.clear();
}
+void EditorApp::statusBarMessage(const QString& message)
+{
+ statusBar()->showMessage(message);
+}
+
void EditorApp::doOpStack()
{
BusyDialog busy(this);
diff --git a/editorapp.hh b/editorapp.hh
index 34a063e..99f528e 100644
--- a/editorapp.hh
+++ b/editorapp.hh
@@ -60,6 +60,7 @@ public slots:
void metaDataChanged();
void audioTick(qint64 time);
void playerStateChanged(Phonon::State newstate, Phonon::State olstate);
+ void statusBarMessage(const QString& message);
// Automatic slots
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 039419c..737dc52 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -43,6 +43,7 @@ NoteGraphWidget::NoteGraphWidget(QWidget *parent)
setFocusPolicy(Qt::StrongFocus);
setAcceptDrops(true);
+ setMouseTracking(true);
setWhatsThis(tr("Note graph that displays the song notes and allows you to manipulate them."));
// Context menu
@@ -503,6 +504,7 @@ void NoteGraphWidget::mouseMoveEvent(QMouseEvent *event)
}
}
+ emit statusBarMessage(QString("Time: %1 s, note: %2").arg(px2s(event->x())).arg(round(px2n(event->y()))));
emit updateNoteInfo(selectedNote());
}
diff --git a/notegraphwidget.hh b/notegraphwidget.hh
index a36d690..e587d3a 100644
--- a/notegraphwidget.hh
+++ b/notegraphwidget.hh
@@ -72,6 +72,7 @@ public:
signals:
void updateNoteInfo(NoteLabel*);
void operationDone(const Operation&);
+ void statusBarMessage(QString);
public slots:
void selectNextSyllable(bool backwards = false, bool addToSelection = false);
|
|
From: Yoda-JM <yo...@us...> - 2011-02-22 12:34:19
|
Author: Vincent Le Ligeour <yo...@us...>
Date: Tue Feb 22 13:35:48 2011 +0100
Added piano display
---
notegraphwidget.cc | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index dcf85d0..039419c 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -192,6 +192,22 @@ void NoteGraphWidget::paintEvent(QPaintEvent*) {
for (int i = 1; i < 4; ++i)
painter.drawLine(x1, n2px(i*12), x2, n2px(i*12));
+ // Piano
+ MusicalScale scale;
+ QColor background;
+ int note_width = n2px(0)-n2px(1);
+ pen.setWidth(2); pen.setColor(QColor("#c0c0c0"));
+ painter.setPen(pen);
+ for (int i = 1; i < 12*4; ++i) {
+ if(scale.isSharp(i)) {
+ background = QColor("#000000");
+ } else {
+ background = QColor("#ffffff");
+ }
+ painter.fillRect(x1, n2px(i)-note_width/2, 50, note_width, background);
+ painter.drawRect(x1, n2px(i)-note_width/2, 50, note_width);
+ }
+
// Selection box
if (!m_mouseHotSpot.isNull()) {
QPoint mousep = mapFromGlobal(QCursor::pos());
|
|
From: Tapio V. <aa...@us...> - 2011-02-22 10:10:10
|
Author: Tapio Vierros <tap...@gm...>
Date: Tue Feb 22 12:06:39 2011 +0200
Also use BusyDialog with opening non-plaintext lyrics.
---
busydialog.hh | 5 +----
notegraphwidget.cc | 2 ++
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/busydialog.hh b/busydialog.hh
index f5b8fd2..60ff038 100644
--- a/busydialog.hh
+++ b/busydialog.hh
@@ -22,10 +22,7 @@ public:
QApplication::processEvents();
count = (count + 1) % interval;
// Only show the dialog after certainamount of time
- if (timer.isValid() && timer.elapsed() > 1500) {
- open();
- timer.invalidate();
- }
+ if (isHidden() && timer.elapsed() > 1500) open();
}
protected:
void closeEvent(QCloseEvent* event) { event->ignore(); }
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index b8b1122..dcf85d0 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -88,12 +88,14 @@ void NoteGraphWidget::setLyrics(QString lyrics)
void NoteGraphWidget::setLyrics(const VocalTrack &track)
{
+ BusyDialog busy(this, 10);
doOperation(Operation("CLEAR"));
m_duration = std::max(m_duration, track.endTime + endMarginSeconds);
const Notes ¬es = track.notes;
for (Notes::const_iterator it = notes.begin(); it != notes.end(); ++it) {
if (it->type == Note::SLEEP) continue;
doOperation(opFromNote(*it, m_notes.size(), false));
+ busy();
}
finalizeNewLyrics();
|
|
From: Tapio V. <aa...@us...> - 2011-02-22 09:01:01
|
Author: Tapio Vierros <tap...@gm...>
Date: Tue Feb 22 11:00:26 2011 +0200
Added a busy dialog to give user feedback when an operation takes long.
---
busydialog.hh | 36 ++++++++++++++++++++++++++++++++++++
editorapp.cc | 3 +++
notegraphwidget.cc | 3 +++
3 files changed, 42 insertions(+), 0 deletions(-)
diff --git a/busydialog.hh b/busydialog.hh
new file mode 100644
index 0000000..f5b8fd2
--- /dev/null
+++ b/busydialog.hh
@@ -0,0 +1,36 @@
+#pragma once
+#include <QApplication>
+#include <QDialog>
+#include <QCloseEvent>
+#include <QProgressBar>
+#include <QVBoxLayout>
+#include <QElapsedTimer>
+
+class BusyDialog: public QDialog {
+public:
+ BusyDialog(QWidget *parent = NULL, int eventsInterval = 30): QDialog(parent), timer(), interval(eventsInterval), count() {
+ QProgressBar *progress = new QProgressBar(this);
+ progress->setRange(0,0);
+ setWindowTitle(tr("Working..."));
+ QVBoxLayout *vb = new QVBoxLayout(this);
+ vb->addWidget(progress);
+ setLayout(vb);
+ timer.start();
+ }
+ void operator()() {
+ if (count == 0) // Let's not process events all the time
+ QApplication::processEvents();
+ count = (count + 1) % interval;
+ // Only show the dialog after certainamount of time
+ if (timer.isValid() && timer.elapsed() > 1500) {
+ open();
+ timer.invalidate();
+ }
+ }
+protected:
+ void closeEvent(QCloseEvent* event) { event->ignore(); }
+private:
+ QElapsedTimer timer;
+ int interval;
+ int count;
+};
diff --git a/editorapp.cc b/editorapp.cc
index 162ca82..366a290 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -20,6 +20,7 @@
#include "songwriter.hh"
#include "textcodecselector.hh"
#include "gettingstarted.hh"
+#include "busydialog.hh"
namespace {
static const QString PROJECT_SAVE_FILE_EXTENSION = "songproject"; // FIXME: Nice extension here
@@ -163,11 +164,13 @@ void EditorApp::operationDone(const Operation &op)
void EditorApp::doOpStack()
{
+ BusyDialog busy(this);
noteGraph->clearNotes();
QString newMusic = "";
// Re-apply all operations in the stack
for (OperationStack::const_iterator opit = opStack.begin(); opit != opStack.end(); ++opit) {
//std::cout << "Doing op: " << opit->dump() << std::endl;
+ busy();
try {
if (opit->op() == "META") {
// META ops are handled differently:
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index e0e9e22..b8b1122 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -13,6 +13,7 @@
#include "notegraphwidget.hh"
#include "song.hh"
#include "util.hh"
+#include "busydialog.hh"
namespace {
@@ -56,11 +57,13 @@ NoteGraphWidget::NoteGraphWidget(QWidget *parent)
void NoteGraphWidget::setLyrics(QString lyrics)
{
+ BusyDialog busy(this, 5);
QTextStream ts(&lyrics, QIODevice::ReadOnly);
doOperation(Operation("CLEAR"));
bool firstNote = true;
while (!ts.atEnd()) {
+ busy();
// We want to loop one line at the time to insert line breaks
bool sentenceStart = true;
QString sentence = ts.readLine();
|
|
From: Tapio V. <aa...@us...> - 2011-02-21 19:47:25
|
Author: Tapio Vierros <tap...@gm...>
Date: Mon Feb 21 21:47:09 2011 +0200
Default to always showing getting started window on start-up.
---
gettingstarted.hh | 2 +-
gettingstarted.ui | 3 +++
2 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/gettingstarted.hh b/gettingstarted.hh
index 2bbe1ad..bba71f5 100644
--- a/gettingstarted.hh
+++ b/gettingstarted.hh
@@ -13,7 +13,7 @@ public:
if (!m_editorApp) throw std::runtime_error("Couldn't open help dialog.");
setupUi(this);
QSettings settings;
- chkShowOnStartup->setChecked(settings.value("showhelp", false).toBool());
+ chkShowOnStartup->setChecked(settings.value("showhelp", true).toBool());
}
public slots:
diff --git a/gettingstarted.ui b/gettingstarted.ui
index dc76f57..b881bab 100644
--- a/gettingstarted.ui
+++ b/gettingstarted.ui
@@ -19,6 +19,9 @@
<property name="text">
<string>&Show this window on application start</string>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item row="2" column="1">
|
|
From: Tapio V. <aa...@us...> - 2011-02-21 19:21:56
|
Author: Tapio Vierros <tap...@gm...>
Date: Mon Feb 21 21:20:42 2011 +0200
No more accessing NoteLabels list from two threads (synth fix).
---
editorapp.cc | 21 +++++++++++++++++----
synth.hh | 37 +++++++++++++++++++++++++++----------
2 files changed, 44 insertions(+), 14 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index d4f72c1..162ca82 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -741,8 +741,7 @@ void EditorApp::playButton()
void EditorApp::on_chkSynth_clicked(bool checked)
{
if (checked && player && player->state() == Phonon::PlayingState) {
- synth.reset(new Synth(noteGraph->noteLabels()));
- synth->tick(player->currentTime());
+ synth.reset(new Synth);
} else if (!checked) {
synth.reset();
}
@@ -765,8 +764,22 @@ void EditorApp::audioTick(qint64 time)
{
if (noteGraph && player)
noteGraph->updateMusicPos(time, (player->state() == Phonon::PlayingState ? true : false));
- if (synth)
- synth->tick(time);
+
+ if (noteGraph && synth) {
+ // Here we create some notes for the synthesizer to use.
+ // We don't simply pass the whole list because then there
+ // would be two threads working on the same list.
+ SynthNotes notes;
+ const NoteLabels &nls = noteGraph->noteLabels();
+ int numberOfNotesToPass = 12;
+ for (NoteLabels::const_iterator it = nls.begin(); it != nls.end() && numberOfNotesToPass > 0; ++it) {
+ if ((*it)->note().begin >= time / 1000.0) {
+ notes.push_back(SynthNote((*it)->note()));
+ --numberOfNotesToPass;
+ }
+ }
+ synth->tick(time, notes);
+ }
}
void EditorApp::playerStateChanged(Phonon::State newstate, Phonon::State oldstate)
diff --git a/synth.hh b/synth.hh
index dc9c959..ea568ca 100644
--- a/synth.hh
+++ b/synth.hh
@@ -19,12 +19,23 @@
#define M_PI 3.141592653589793
#endif
+struct SynthNote {
+ SynthNote(): note(24), begin(), length() {}
+ SynthNote(const Note& n): note(n.note), begin(n.begin), length(n.length()) {}
+ bool operator<(const SynthNote& rhs) { return begin < rhs.begin; }
+ int note;
+ double begin;
+ double length;
+};
+
+typedef QList<SynthNote> SynthNotes;
+
+
class Synth: public QThread
{
Q_OBJECT
public:
- //FIXME: Giving notes this way and not guarding access will fail miserably some day
- Synth(NoteLabels& notes, QObject *parent = NULL) : QThread(parent), m_notes(notes), m_delay(), m_pos(), m_noteBegin(), m_curBuffer(), m_quit()
+ Synth(QObject *parent = NULL) : QThread(parent), m_delay(), m_pos(), m_noteBegin(), m_curBuffer(), m_quit()
{
// Apparantly we need to register some types
qRegisterMetaType<Phonon::MediaSource>("MediaSource");
@@ -34,9 +45,11 @@ public:
~Synth() { stop(); wait(); }
/// Updates the synth
- void tick(qint64 pos) {
+ void tick(qint64 pos, const SynthNotes& notes) {
QMutexLocker locker(&m_mutex);
m_pos = pos / 1000.0;
+ m_notes = notes;
+
if (isRunning()) m_condition.wakeOne();
else start();
}
@@ -95,17 +108,21 @@ private:
/// Calculates the next values
void calcNext() {
QElapsedTimer timer; timer.start();
- NoteLabels::const_iterator it = m_notes.begin();
- while (it != m_notes.end() && (*it)->note().begin < m_pos) ++it;
- if (it == m_notes.end()) { m_delay = ULONG_MAX / 1000.0; return; }
- Note n = (*it)->note();
- m_delay = n.begin - m_pos;
+ SynthNote n;
+ {
+ QMutexLocker locker(&m_mutex);
+ SynthNotes::const_iterator it = m_notes.begin();
+ while (it != m_notes.end() && it->begin < m_pos) ++it;
+ if (it == m_notes.end()) { m_delay = ULONG_MAX / 1000.0; return; }
+ n = *it;
+ }
+ m_delay = n.begin - m_pos;
if (n.begin != m_noteBegin) {
// Need to create a new buffer
m_noteBegin = n.begin;
m_player[m_curBuffer]->clear();
- createBuffer(n.note, n.length());
+ createBuffer(n.note, n.length);
m_player[m_curBuffer]->setCurrentSource(m_soundData[m_curBuffer]);
}
// Compensate for the time spent in this function
@@ -162,7 +179,7 @@ private:
static const int sampleRate = 8000; ///< Sample rate
- NoteLabels m_notes; ///< Notes
+ SynthNotes m_notes; ///< Notes to synthesize
double m_delay; ///< How many seconds until the next sound must be played
double m_pos; ///< Position where we are now
double m_noteBegin; ///< Position of the next note
|
|
From: Tapio V. <aa...@us...> - 2011-02-21 16:22:06
|
Author: Tapio Vierros <tap...@gm...>
Date: Mon Feb 21 18:21:08 2011 +0200
Removed Lyrics-tab: Not really useful and may confuse users.
---
editor.ui | 32 +-------------------------------
editorapp.cc | 6 ------
editorapp.hh | 1 -
3 files changed, 1 insertions(+), 38 deletions(-)
diff --git a/editor.ui b/editor.ui
index a6bbe99..80cab1e 100644
--- a/editor.ui
+++ b/editor.ui
@@ -47,7 +47,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>505</width>
+ <width>534</width>
<height>227</height>
</rect>
</property>
@@ -474,36 +474,6 @@
</item>
</layout>
</widget>
- <widget class="QWidget" name="tabLyrics">
- <attribute name="title">
- <string>&Lyrics</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_7">
- <item row="1" column="0">
- <widget class="QPushButton" name="cmdRefreshLyrics">
- <property name="text">
- <string>Refresh</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QTextEdit" name="textEditLyrics">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="autoFillBackground">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
</widget>
</item>
</layout>
diff --git a/editorapp.cc b/editorapp.cc
index 32232be..d4f72c1 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -748,12 +748,6 @@ void EditorApp::on_chkSynth_clicked(bool checked)
}
}
-void EditorApp::on_cmdRefreshLyrics_clicked()
-{
- QString text = noteGraph->dumpLyrics();
- ui.textEditLyrics->setText(text);
-}
-
void EditorApp::on_cmdPlay_clicked()
{
if (player) {
diff --git a/editorapp.hh b/editorapp.hh
index 466e782..34a063e 100644
--- a/editorapp.hh
+++ b/editorapp.hh
@@ -65,7 +65,6 @@ public slots:
void on_cmdPlay_clicked();
void on_chkSynth_clicked(bool checked);
- void on_cmdRefreshLyrics_clicked();
// File menu
void on_actionNew_triggered();
|
|
From: Tapio V. <aa...@us...> - 2011-02-21 16:21:59
|
Author: Tapio Vierros <tap...@gm...>
Date: Mon Feb 21 18:17:27 2011 +0200
Change version to '1.0rc1+'
---
CMakeLists.txt | 2 +-
platform/mingw-cross-env/makeinstaller.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4ae71ba..5e0098f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
project("Composer" CXX C)
cmake_minimum_required(VERSION 2.6)
cmake_policy(VERSION 2.6)
-set(PROJECT_VERSION "rc1")
+set(PROJECT_VERSION "1.0rc1+")
set(EXENAME ${CMAKE_PROJECT_NAME})
if(UNIX)
diff --git a/platform/mingw-cross-env/makeinstaller.py b/platform/mingw-cross-env/makeinstaller.py
index cfb4875..f6cb638 100755
--- a/platform/mingw-cross-env/makeinstaller.py
+++ b/platform/mingw-cross-env/makeinstaller.py
@@ -30,7 +30,7 @@ if not os.path.isdir('dist'):
os.chdir('stage')
app = 'Composer'
-version = 'rc1'
+version = '1.0rc1+'
makensis.stdin.write(r'''!include "MUI2.nsh"
|
|
From: Tapio V. <aa...@us...> - 2011-02-21 16:21:52
|
Author: Tapio Vierros <tap...@gm...> Date: Mon Feb 21 18:13:47 2011 +0200 Re-enable features that were disabled for the release. --- editorapp.cc | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editorapp.cc b/editorapp.cc index 3a232b4..32232be 100644 --- a/editorapp.cc +++ b/editorapp.cc @@ -109,9 +109,9 @@ EditorApp::EditorApp(QWidget *parent) handleTips(ui.tabTools); // FIXME: Remove these after rc release - ui.actionFoFMIDI->setEnabled(false); + //ui.actionFoFMIDI->setEnabled(false); #ifdef WIN32 - ui.chkSynth->setEnabled(false); + //ui.chkSynth->setEnabled(false); #endif //// |
|
From: Tapio V. <aa...@us...> - 2011-02-21 16:21:46
|
Author: Tapio Vierros <tap...@gm...> Date: Mon Feb 21 18:13:08 2011 +0200 Help page tweaks. --- docs/helpindex.html | 9 +++++---- 1 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/helpindex.html b/docs/helpindex.html index f76130e..5bc700f 100644 --- a/docs/helpindex.html +++ b/docs/helpindex.html @@ -14,21 +14,20 @@ <li><a href="#formats">File formats</a></li> </ol> -<p><strong>TODO: Improve this</strong></p> <h2><a name="intro">Introduction</a></h2> <p>This editor is designed to create notes for use in pitch analyzing karaoke games. We attempt to make the process as easy as possible by automating as much as we can. For example, the editor analyzes the song and attempts to automatically place the notes at the correct pitch.</p> <h2><a name="workflow">Basic Workflow</a></h2> -<p>In addition to the sections here, the intended workflow is documented as a handy dialog with clickable items - follow it step-by-step and in the end you have a finished notes for the song. Access it through the main menubar: Help --> Getting started.</p> +<p>In addition to the sections here, the intended workflow is documented as a handy dialog with clickable items - follow it step-by-step and in the end you'll have finished notes for the song. Access it through the main menubar: Help --> Getting started.</p> <h3><a name="initialimport">Importing song lyrics</h3> <p>The first step is to import a music file for analyzation and lyrics text for generating notes. This can be done e.g. through the import menu. Supported song file formats vary depending on the platform, but at least mp3 and ogg should be ok. Lyrics assume that each phrase is on a line of its own - when the text is imported, a note is generated for each word and a sentence marker is placed at the beginning of each line.</p> <h3><a name="timing">Note timing</a></h3> <p>The easiest way to get the notes roughly timed is to switch to the <em>Tools</em> tab and start listening to the music. Each time you hear the phrase that is displayed in the tab, hit the <em>Time phrase</em> button (or its hotkey) and the start of the current phrase is placed to that position.</p> -<p>This way you'll be timing only the beginnings of the phrases - the rest of the notes are divided evenly to the extra space. Obviosly this is not perfect, but it gives a very good starting point for <a href="#tuning">fine tuning</a> and timing each note by itself using this method would require very good reflexes and concentration.</p> +<p>This way you'll be timing only the beginnings of the phrases - the rest of the notes are divided evenly to the extra space. Obviously this is not perfect, but it gives a very good starting point for <a href="#tuning">fine tuning</a> and timing each note by itself using this method would require very good reflexes and concentration.</p> <h3><a name="tuning">Fine tuning</a></h3> <p>Once the notes are roughly timed, it is time to start manually tuning and fixing everything. Each time you correct a note, the neighbouring uncorrected ones adjust themselves accordingly, making your task easier.</p> @@ -42,6 +41,8 @@ <ul> <li>If possible, use vocals-only music tracks for best pitch analyzation (and thus auto-pitch) results.</li> <li>In addition to copy-pasting lyrics from clipboard or loading them from a text file, you can also drag and drop text directly from other applications (like a web browser) to the editor.</li> +<li>Zoom (mouse wheel or ctrl+ -/+) in to get most precise timing or zoom out to get an overview and quickly change to other part of the song.</li> +<li>The actions available through the menu bar and the tabs are also accessible through a context menu that opens up if you right click the note area.</li> </ul> @@ -53,5 +54,5 @@ <li>UltraStar TXT, import/export</li> <li>Frets on Fire INI+MIDI, import</li> <li>LRC karaoke lyrics, export</li> -<li>Plain text, import/export</li> +<li>Plain text lyrics, import/export</li> </ul> |
|
From: Tapio V. <aa...@us...> - 2011-02-09 15:47:10
|
Module: editor
Branch: master
Commit: d63d2ba62ad96468048b41e6c155d9cd59e8167d
Author: Tapio Vierros <tap...@gm...>
Date: Wed Feb 9 17:44:12 2011 +0200
Don't ask confirmation on lyrics pasting if there's no lyrics to overwrite.
---
editorapp.cc | 10 ++++++----
1 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 3d8d6dd..6046a20 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -521,10 +521,12 @@ void EditorApp::on_actionLyricsFromClipboard_triggered()
if (mimeData->hasText() && !mimeData->text().isEmpty()) {
QString text = mimeData->text();
- QMessageBox::StandardButton b = QMessageBox::question(this, tr("Replace lyrics"),
- tr("Pasting lyrics from clipboard will replace the existing ones. Continue?"),
- QMessageBox::Ok | QMessageBox::Cancel);
- if (b == QMessageBox::Ok) {
+ if ((noteGraph && noteGraph->noteLabels().empty())
+ || QMessageBox::question(this, tr("Replace lyrics"),
+ tr("Pasting lyrics from clipboard will replace the existing ones. Continue?"),
+ QMessageBox::Ok | QMessageBox::Cancel)
+ == QMessageBox::Ok)
+ {
noteGraph->setLyrics(text);
}
} else {
|
|
From: Tapio V. <aa...@us...> - 2011-02-09 15:47:05
|
Module: editor Branch: master Commit: 15a6b48ab55229cfd11cb6b94497fc20e65e4ecd Author: Tapio Vierros <tap...@gm...> Date: Wed Feb 9 17:37:19 2011 +0200 Reset unsaved changes state when calling new project. --- editorapp.cc | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/editorapp.cc b/editorapp.cc index f096ba8..3d8d6dd 100644 --- a/editorapp.cc +++ b/editorapp.cc @@ -240,6 +240,7 @@ void EditorApp::on_actionNew_triggered() statusbarProgress->hide(); ui.txtTitle->clear(); ui.txtArtist->clear(); ui.txtGenre->clear(); ui.txtYear->clear(); ui.valMusicFile->clear(); + hasUnsavedChanges = false; } updateMenuStates(); } |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-09 13:04:03
|
Module: editor Branch: master Commit: d5d34308dc48f64726569f0f39525c945e1d9d40 Author: Lasse Karkkainen <tro...@tr...> Date: Wed Feb 9 14:03:50 2011 +0100 A little more detail for pitch detection --- pitch.cc | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/pitch.cc b/pitch.cc index c06d1af..4c965d1 100644 --- a/pitch.cc +++ b/pitch.cc @@ -6,7 +6,7 @@ static const unsigned FFT_P = 12; // FFT size setting, will use 2^FFT_P sample FFT static const std::size_t FFT_N = 1 << FFT_P; // FFT size in samples -static const std::size_t FFT_STEP = 1024; // Step size in samples, should be <= 0.25 * FFT_N. Low values cause high CPU usage. +static const std::size_t FFT_STEP = 512; // Step size in samples, should be <= 0.25 * FFT_N. Low values cause high CPU usage. // Limit the range to avoid noise and useless computation static const double FFT_MINFREQ = 45.0; |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-09 12:34:15
|
Module: web Branch: master Commit: 409a690b91b434184f0852e52dc8116718e0e107 Author: Lasse Karkkainen <tro...@tr...> Date: Wed Feb 9 13:34:00 2011 +0100 The first blog post about the editor --- htdocs-binary/imgs/editor-zoom-thumb.png | Bin 0 -> 34580 bytes htdocs-binary/imgs/editor-zoom.png | Bin 0 -> 76398 bytes htdocs-source/blog.txt | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 0 deletions(-) diff --git a/htdocs-binary/imgs/editor-zoom-thumb.png b/htdocs-binary/imgs/editor-zoom-thumb.png new file mode 100644 index 0000000..d8ae779 Binary files /dev/null and b/htdocs-binary/imgs/editor-zoom-thumb.png differ diff --git a/htdocs-binary/imgs/editor-zoom.png b/htdocs-binary/imgs/editor-zoom.png new file mode 100644 index 0000000..fdaa57c Binary files /dev/null and b/htdocs-binary/imgs/editor-zoom.png differ diff --git a/htdocs-source/blog.txt b/htdocs-source/blog.txt index 67e58e6..c514ddf 100644 --- a/htdocs-source/blog.txt +++ b/htdocs-source/blog.txt @@ -1,5 +1,20 @@ News +:h2:2011-02-09 - A song editor in progress + +An often requested feature for Performous is a song editor, especially considering that there are no editors that could handle vocals, band mode and dance notes together in the same song. We finally got a chance at implementing one once a Swedish company offered to fund the development by hiring two of the Performous core developers to the project. Since there are a few promising open source song editors, we started by reviewing them and seeing if we could build on top of existing software. <a href="http://code.google.com/p/editor-on-fire/" target="_blank">Editor on Fire</a> ended up being the top candidate. Unfortunately it had a very different workflow than what we needed and also the custom UI and being written in C (rather than C++) were major drawbacks. + +Another big question was whether to implement it as a feature of Performous or as a separate application based on Qt. On Performous it could benefit from all the existing infrastructure (OpenGL, audio playback and so on) but also it wouldn't be able to easily use existing widgets and dialogs that are very important for an editor. Also we wanted the editor to be easily usable by everybody instead of tying it down to the game, so we decided to use Qt. Using Qt as the library was a no-brainer as it is the only truely cross-platform solution (native Windows, Gtk and OS X widgets and dialogs) besides wxWidgets and the latter is horribly bad. + +<img src="imgs/editor-zoom-thumb.png" alt="Editor screenshot"/> + +So, now we are on the sixth week of development and all the basic functionality is in place but there is still a lot of work to be done. There is a getting started wizard in the editor to make you familiar with the workflow where a large part of the process is automated via pitch detection and other means, making song creation easy. Only vocals are supported for now but other modes are going to be available at a later time. + +In case you wish to have a sneak peek, you can use the git version, otherwise wait for a release in the coming weeks. + +<code>git clone git://git.performous.org/gitroot/performous/editor</code> + + :h2:2010-12-09 - Website redesigned For long have we attempted to summon a web developer to create us a new site but that never happened. That feeling of deep shame every time I displayed the site to someone finally overcame me and I decided to do the job myself since no-one else was doing it. What you are looking at is just that. The new one boosts a lot of bleeding edge web technology and there could be some bugs as we only tested Chrome, Firefox, Safari, Opera and my mobile phone (all of which worked fine with reasonably high performance). |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-02-09 11:37:19
|
Module: editor
Branch: master
Commit: ca5b9ddf2072715fc715bf2b6f257e8c1a88dc70
Author: Lasse Karkkainen <tro...@tr...>
Date: Wed Feb 9 12:35:38 2011 +0100
Exponential zoom
---
notegraphwidget.hh | 10 +++++-----
notelabelmanager.cc | 26 +++++++++++++++-----------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/notegraphwidget.hh b/notegraphwidget.hh
index 9fa9c2c..8a5d24e 100644
--- a/notegraphwidget.hh
+++ b/notegraphwidget.hh
@@ -72,11 +72,11 @@ public slots:
protected:
- // Pixels Per Second values
- static const double ppsStep = 20.0f;
- static const double ppsMin = 100;
- static const double ppsMax = 300;
- static const double ppsNormal = 200;
+ // Zoom settings
+ static const double zoomStep = 0.5; ///< Mouse wheel steps * zoomStep => double/half zoom factor
+ static const int zoomMin = -12; ///< Number of steps to minimum zoom
+ static const int zoomMax = 6; ///< Number of steps to maximum zoom
+ static const double ppsNormal = 200.0; ///< Pixels per second with default zoom
double m_pixelsPerSecond;
NoteLabels m_notes;
diff --git a/notelabelmanager.cc b/notelabelmanager.cc
index fce9119..68fa7fa 100644
--- a/notelabelmanager.cc
+++ b/notelabelmanager.cc
@@ -246,10 +246,6 @@ void NoteLabelManager::doOperation(const Operation& op, Operation::OperationFlag
}
void NoteLabelManager::zoom(float steps) {
- // Check if we can do anything
- if (m_pixelsPerSecond <= ppsMin && steps < 0) return;
- else if (m_pixelsPerSecond >= ppsMax && steps > 0) return;
-
// Get scrollArea position
QScrollArea *scrollArea = NULL;
double scrollSecs = -1;
@@ -258,13 +254,21 @@ void NoteLabelManager::zoom(float steps) {
if (scrollArea) scrollSecs = px2s(scrollArea->horizontalScrollBar()->value() + scrollArea->width()/2);
}
- // Update zoom factor, NaN means reset
- if (steps != steps) m_pixelsPerSecond = ppsNormal;
- else m_pixelsPerSecond += steps * ppsStep;
- // Limits
- if (m_pixelsPerSecond < ppsMin) m_pixelsPerSecond = ppsMin;
- else if (m_pixelsPerSecond > ppsMax) m_pixelsPerSecond = ppsMax;
-
+ // Update m_pixelsPerSecond
+ {
+ double pps = m_pixelsPerSecond;
+ // Update zoom factor, NaN means reset
+ if (steps != steps) pps = ppsNormal;
+ else {
+ // A little trickier exponential adjustment to avoid accumulating rounding errors
+ double current = std::log(pps / ppsNormal) / std::log(2.0) / zoomStep; // Find the steps for current level
+ int level = clamp(int(round(current + steps)), zoomMin, zoomMax); // New level
+ pps = ppsNormal * std::pow(2.0, level * zoomStep); // Calculate new zoom
+ }
+ if (pps == m_pixelsPerSecond) return; // Nothing changed
+ m_pixelsPerSecond = pps;
+ }
+
// Update scroll bar position
if (scrollArea && scrollSecs >= 0) {
QScrollBar *scrollVer = scrollArea->verticalScrollBar();
|
|
From: Tapio V. <aa...@us...> - 2011-02-09 09:44:32
|
Module: editor
Branch: master
Commit: 48a93fbbd7c0ea5cf1ae3cb61038359c7f41d32d
Author: Tapio Vierros <tap...@gm...>
Date: Wed Feb 9 11:44:02 2011 +0200
Added some comments.
---
notegraphwidget.cc | 3 +++
pitchvis.cc | 8 +++++---
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 141e5d4..bb5a025 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -186,6 +186,7 @@ void NoteGraphWidget::paintEvent(QPaintEvent*) {
void NoteGraphWidget::updatePixmap(const QImage &image, const QPoint &position)
{
// PitchVis sends its renderings here, let's save & draw them
+ // This gets actually called in our own thread by our own event loop (queued connection)
m_pixmap = QPixmap::fromImage(image);
m_pixmapPos = position;
update();
@@ -193,6 +194,8 @@ void NoteGraphWidget::updatePixmap(const QImage &image, const QPoint &position)
void NoteGraphWidget::updatePitch()
{
+ // Called whenever pitch needs updating
+ // Note that the scrollbar change signals are connected here, so no need to call this from everywhere
if (!m_pitch) return;
// Find out the viewport
int x1, y1, x2, y2;
diff --git a/pitchvis.cc b/pitchvis.cc
index 7cd1624..289d132 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -130,12 +130,12 @@ void PitchVis::renderer() {
}
// Rendering
- // TODO: Take y-size into account
+ // QImage allows drawing in non-main/non-GUI thread
QImage image(x2-x1, y2-y1, QImage::Format_RGB32);
- QSettings settings; // Default QSettings parameters given in main()
- bool aa = settings.value("anti-aliasing", true).toBool();
NoteGraphWidget *widget = qobject_cast<NoteGraphWidget*>(parent());
if (!widget) continue;
+ QSettings settings; // Default QSettings parameters given in main()
+ bool aa = settings.value("anti-aliasing", true).toBool();
QPainter painter;
painter.begin(&image);
@@ -155,6 +155,7 @@ void PitchVis::renderer() {
else if (widget->s2px(fragments.front().time) > x2) break;
// Iterate through the path points
for (PitchPath::Fragments::const_iterator it2 = fragments.begin(), it2end = fragments.end(); it2 != it2end; ++it2) {
+ // TODO: Take y-size into account (change also the paint calls in NoteGraphWidget)
int x = widget->s2px(it2->time) - x1;
int y = widget->n2px(it2->note);
pen.setColor(QColor(32 + 64 * it->channel, clamp<int>(127 + it2->level, 32, 255), 32, 128));
@@ -166,6 +167,7 @@ void PitchVis::renderer() {
painter.end();
// Send the image
+ // This is actually delivered by the reciever's event loop thread, and not called directly from here
emit renderedImage(image, QPoint(x1, y1));
mutex.lock();
|
|
From: Tapio V. <aa...@us...> - 2011-02-09 09:44:29
|
Module: editor
Branch: master
Commit: bb68d9647afb4bcb686fc164e7652f3645f789eb
Author: Tapio Vierros <tap...@gm...>
Date: Wed Feb 9 11:28:29 2011 +0200
Fix high "idle" cpu usage in new rendering code.
---
editorapp.cc | 5 +++-
notegraphwidget.cc | 54 +++++++++++++++++++++++++++++++++------------------
notegraphwidget.hh | 3 ++
3 files changed, 42 insertions(+), 20 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 8a5f6cd..f096ba8 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -1,4 +1,5 @@
#include <QProgressBar>
+#include <QScrollBar>
#include <QMessageBox>
#include <QFileDialog>
#include <QDesktopServices>
@@ -85,9 +86,11 @@ void EditorApp::setupNoteGraph()
QList<int> ss; ss.push_back(700); ss.push_back(300); // Proportions, not pixels
ui.splitter->setSizes(ss);
- // Custom signals/slots
+ // Signals/slots
connect(noteGraph, SIGNAL(operationDone(const Operation&)), this, SLOT(operationDone(const Operation&)));
connect(noteGraph, SIGNAL(updateNoteInfo(NoteLabel*)), this, SLOT(updateNoteInfo(NoteLabel*)));
+ connect(ui.noteGraphScroller->horizontalScrollBar(), SIGNAL(valueChanged(int)), noteGraph, SLOT(updatePitch()));
+ connect(ui.noteGraphScroller->verticalScrollBar(), SIGNAL(valueChanged(int)), noteGraph, SLOT(updatePitch()));
connect(ui.cmdTimeSentence, SIGNAL(pressed()), noteGraph, SLOT(timeSentence()));
connect(ui.cmdSkipSentence, SIGNAL(pressed()), noteGraph, SLOT(selectNextSentenceStart()));
connect(ui.chkGrabSeekHandle, SIGNAL(toggled(bool)), noteGraph, SLOT(setSeekHandleWrapToViewport(bool)));
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 8032152..141e5d4 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -112,6 +112,22 @@ void NoteGraphWidget::finalizeNewLyrics()
updateNotes();
}
+void NoteGraphWidget::calcViewport(int &x1, int &y1, int &x2, int &y2) const
+{
+ QScrollArea *scrollArea = NULL;
+ x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+ if (parentWidget())
+ scrollArea = qobject_cast<QScrollArea*>(parentWidget()->parent());
+ if (scrollArea) {
+ if (scrollArea->horizontalScrollBar())
+ x1 = scrollArea->horizontalScrollBar()->value();
+ x2 = x1 + scrollArea->width();
+ if (scrollArea->verticalScrollBar())
+ y1 = scrollArea->verticalScrollBar()->value();
+ y2 = y1 + scrollArea->height();
+ }
+}
+
void NoteGraphWidget::analyzeMusic(QString filepath)
{
m_pitch.reset(new PitchVis(filepath, this));
@@ -129,37 +145,27 @@ void NoteGraphWidget::timerEvent(QTimerEvent* event)
} else if (event->timerId() == m_analyzeTimer && m_pitch) {
// PitchVis stuff
double progress, duration;
- bool needUpdate;
{
QMutexLocker locker(&m_pitch->mutex);
progress = m_pitch->getProgress();
duration = m_pitch->getDuration();
- needUpdate = m_pitch->newDataAvailable() || duration != m_duration;
}
emit analyzeProgress(1000 * progress, 1000); // Update progress bar
- if (needUpdate) {
- m_duration = std::max(m_duration, duration);
- update();
+ m_duration = std::max(m_duration, duration);
+ // Analyzing has ended?
+ if (progress == 1.0) {
+ killTimer(m_analyzeTimer);
+ updatePitch();
}
- if (progress == 1) killTimer(m_analyzeTimer);
}
}
void NoteGraphWidget::paintEvent(QPaintEvent*) {
setFixedSize(s2px(m_duration), height());
- // Find out the horizontal viewport
- QScrollArea *scrollArea = NULL;
- int x1 = 0, x2 = 0;
- if (parentWidget())
- scrollArea = qobject_cast<QScrollArea*>(parentWidget()->parent());
- if (scrollArea && scrollArea->horizontalScrollBar()) {
- x1 = scrollArea->horizontalScrollBar()->value();
- x2 = x1 + scrollArea->width();
- }
-
- // Ask for a new render
- if (m_pitch) m_pitch->paint(x1, 0, x2, height());
+ // Find out the viewport
+ int x1, y1, x2, y2;
+ calcViewport(x1, y1, x2, y2);
QPainter painter;
painter.begin(this);
@@ -185,6 +191,16 @@ void NoteGraphWidget::updatePixmap(const QImage &image, const QPoint &position)
update();
}
+void NoteGraphWidget::updatePitch()
+{
+ if (!m_pitch) return;
+ // Find out the viewport
+ int x1, y1, x2, y2;
+ calcViewport(x1, y1, x2, y2);
+ // Ask for a new render
+ m_pitch->paint(x1, 0, x2, height());
+}
+
void NoteGraphWidget::updateNotes(bool leftToRight)
{
// Here happens the magic that adjusts the floating
@@ -245,7 +261,7 @@ void NoteGraphWidget::updateMusicPos(qint64 time, bool smoothing)
killTimer(m_playbackTimer);
m_seekHandle.move(x, 0);
if (smoothing)
- m_playbackTimer = startTimer(17); // Hope for 60 fps
+ m_playbackTimer = startTimer(20); // Hope for 50 fps
m_playbackInterval.restart();
}
diff --git a/notegraphwidget.hh b/notegraphwidget.hh
index 8c22ca6..9fa9c2c 100644
--- a/notegraphwidget.hh
+++ b/notegraphwidget.hh
@@ -116,6 +116,7 @@ public slots:
void timeSentence();
void setSeekHandleWrapToViewport(bool state) { m_seekHandle.wrapToViewport = state; }
void updatePixmap(const QImage &image, const QPoint &position);
+ void updatePitch();
signals:
void analyzeProgress(int, int);
@@ -130,10 +131,12 @@ protected:
void keyPressEvent(QKeyEvent *event);
void timerEvent(QTimerEvent *event);
void paintEvent(QPaintEvent*);
+ void resizeEvent(QResizeEvent *) { updatePitch(); }
private:
void finalizeNewLyrics();
void timeCurrent();
+ void calcViewport(int &x1, int &y1, int &x2, int &y2) const;
QPoint m_panHotSpot;
bool m_seeking;
|