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-01-19 17:08:41
|
Module: editor
Branch: master
Commit: b3550b7f277ef4eabf4d6f303293c677d777c429
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 19:07:43 2011 +0200
Tweaks to playback controls.
---
editorapp.cc | 22 +++++++++++++++-------
1 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index b41b60c..f2c7ecb 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -46,7 +46,7 @@ EditorApp::EditorApp(QWidget *parent): QMainWindow(parent), projectFileName(), h
song.reset(new Song);
- // Some icons to menus to make them prettier
+ // Some icons to make menus etc prettier
ui.actionNew->setIcon(QIcon::fromTheme("document-new"));
ui.actionOpen->setIcon(QIcon::fromTheme("document-open"));
ui.actionSave->setIcon(QIcon::fromTheme("document-save"));
@@ -59,6 +59,8 @@ EditorApp::EditorApp(QWidget *parent): QMainWindow(parent), projectFileName(), h
ui.actionLyricsFromFile->setIcon(QIcon::fromTheme("insert-text"));
ui.actionLyricsFromClipboard->setIcon(QIcon::fromTheme("insert-text"));
ui.actionAbout->setIcon(QIcon::fromTheme("help-about"));
+ ui.cmdPlay->setIcon(QIcon::fromTheme("media-playback-start"));
+ ui.cmdStop->setIcon(QIcon::fromTheme("media-playback-stop"));
hasUnsavedChanges = false;
updateMenuStates();
@@ -354,7 +356,6 @@ void EditorApp::on_actionMusicFile_triggered()
if (!fileName.isNull()) {
ui.valMusicFile->setText(fileName);
- ui.tabWidget->setCurrentIndex(1); // Switch to song properties tab
// Metadata is updated when it becomes available (signal)
player->setCurrentSource(Phonon::MediaSource(QUrl::fromLocalFile(fileName)));
// Fire up analyzer
@@ -463,12 +464,19 @@ void EditorApp::audioTick(qint64 time)
void EditorApp::on_cmdPlay_toggled(bool checked)
{
if (player) {
- if (checked) {
- player->play();
- ui.cmdPlay->setText(tr("Playing..."));
+ if (player->currentSource().type() == Phonon::MediaSource::Empty
+ || player->currentSource().type() == Phonon::MediaSource::Invalid) {
+ on_actionMusicFile_triggered();
} else {
- player->pause();
- ui.cmdPlay->setText(tr("Play"));
+ if (checked) {
+ player->play();
+ ui.cmdPlay->setText(tr("Playing..."));
+ ui.cmdPlay->setIcon(QIcon::fromTheme("media-playback-pause"));
+ } else {
+ player->pause();
+ ui.cmdPlay->setText(tr("Play"));
+ ui.cmdPlay->setIcon(QIcon::fromTheme("media-playback-start"));
+ }
}
}
}
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 16:42:54
|
Module: editor
Branch: master
Commit: 535176228fd02f6f8728145e5900811823bf1087
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 18:41:41 2011 +0200
Added playback controls (UI not final).
---
editor.ui | 33 ++++++++++++++++++++++++++++++---
editorapp.cc | 22 +++++++++++++++++++---
editorapp.hh | 3 +++
3 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/editor.ui b/editor.ui
index 3706b5c..43fb2ea 100644
--- a/editor.ui
+++ b/editor.ui
@@ -76,9 +76,6 @@
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
- <property name="currentIndex">
- <number>1</number>
- </property>
<widget class="QWidget" name="tabNote">
<attribute name="title">
<string>&Note properties</string>
@@ -318,6 +315,36 @@
</item>
</layout>
</widget>
+ <widget class="QWidget" name="tabTools">
+ <attribute name="title">
+ <string>&Tools</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="0" column="0">
+ <widget class="QPushButton" name="cmdPlay">
+ <property name="toolTip">
+ <string>Start playing the music file.</string>
+ </property>
+ <property name="text">
+ <string>Play</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="cmdStop">
+ <property name="toolTip">
+ <string>Stop the music.</string>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</widget>
</item>
</layout>
diff --git a/editorapp.cc b/editorapp.cc
index 512bc2c..b41b60c 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -357,9 +357,6 @@ void EditorApp::on_actionMusicFile_triggered()
ui.tabWidget->setCurrentIndex(1); // Switch to song properties tab
// Metadata is updated when it becomes available (signal)
player->setCurrentSource(Phonon::MediaSource(QUrl::fromLocalFile(fileName)));
- // FIXME: Temporary test
- audioOutput->setVolume(50);
- player->play();
// Fire up analyzer
noteGraph->analyzeMusic(fileName);
}
@@ -463,6 +460,25 @@ void EditorApp::audioTick(qint64 time)
if (noteGraph) noteGraph->updateMusicPos(time);
}
+void EditorApp::on_cmdPlay_toggled(bool checked)
+{
+ if (player) {
+ if (checked) {
+ player->play();
+ ui.cmdPlay->setText(tr("Playing..."));
+ } else {
+ player->pause();
+ ui.cmdPlay->setText(tr("Play"));
+ }
+ }
+}
+
+void EditorApp::on_cmdStop_clicked()
+{
+ if (player) player->stop();
+ if (noteGraph) noteGraph->updateMusicPos(0);
+ ui.cmdPlay->setChecked(false);
+}
void EditorApp::on_txtTitle_editingFinished() { updateSongMeta(); }
void EditorApp::on_txtArtist_editingFinished() { updateSongMeta(); }
diff --git a/editorapp.hh b/editorapp.hh
index 9e64861..276030f 100644
--- a/editorapp.hh
+++ b/editorapp.hh
@@ -34,6 +34,9 @@ public slots:
// Automatic slots
+ void on_cmdPlay_toggled(bool checked);
+ void on_cmdStop_clicked();
+
// File menu
void on_actionNew_triggered();
void on_actionOpen_triggered();
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 16:42:54
|
Module: editor
Branch: master
Commit: 6baa717e233b84b5aa639bd34b056ebae9aeecf4
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 18:11:15 2011 +0200
Add a push button for changing the music file.
---
editor.ui | 12 +++++++++++-
editorapp.cc | 2 ++
2 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/editor.ui b/editor.ui
index 1b76db0..3706b5c 100644
--- a/editor.ui
+++ b/editor.ui
@@ -77,7 +77,7 @@
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
- <number>0</number>
+ <number>1</number>
</property>
<widget class="QWidget" name="tabNote">
<attribute name="title">
@@ -306,6 +306,16 @@
</property>
</spacer>
</item>
+ <item row="2" column="3">
+ <widget class="QPushButton" name="cmdMusicFile">
+ <property name="toolTip">
+ <string>Change music file...</string>
+ </property>
+ <property name="text">
+ <string>Load music...</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</widget>
diff --git a/editorapp.cc b/editorapp.cc
index 073049e..512bc2c 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -29,6 +29,8 @@ EditorApp::EditorApp(QWidget *parent): QMainWindow(parent), projectFileName(), h
// Custom signals/slots
connect(noteGraph, SIGNAL(operationDone(const Operation&)), this, SLOT(operationDone(const Operation&)));
connect(noteGraph, SIGNAL(updateNoteInfo(NoteLabel*)), this, SLOT(updateNoteInfo(NoteLabel*)));
+ // Duplicate/reused signals/slots
+ connect(ui.cmdMusicFile, SIGNAL(clicked()), this, SLOT(on_actionMusicFile_triggered()));
// We must set the initial lyrics here, because constructor doesn't have
// signals yet ready, which leads to empty undo stack
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 16:42:49
|
Module: editor
Branch: master
Commit: 1cf0bf5cdf278ca03393d3b627e1bf3e0b0e014e
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 18:01:27 2011 +0200
Minimum size for window - counter shrinking into uselessness when un-maximizing.
---
editor.ui | 12 ++++++++++++
1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/editor.ui b/editor.ui
index a9f09c1..1b76db0 100644
--- a/editor.ui
+++ b/editor.ui
@@ -10,6 +10,18 @@
<height>600</height>
</rect>
</property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>600</width>
+ <height>500</height>
+ </size>
+ </property>
<property name="windowTitle">
<string>Editor</string>
</property>
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 14:53:41
|
Module: editor
Branch: master
Commit: 7cadc19fa5f8e51ef397dbe744cff1c6e812f693
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 16:52:38 2011 +0200
NoteLabel alpha channel fun.
---
notelabel.cc | 23 ++++++++++++-----------
1 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/notelabel.cc b/notelabel.cc
index 599722e..48e652b 100644
--- a/notelabel.cc
+++ b/notelabel.cc
@@ -40,20 +40,21 @@ void NoteLabel::createPixmap(QSize size)
image.fill(qRgba(0, 0, 0, 0));
QLinearGradient gradient(0, 0, 0, image.height()-1);
- gradient.setColorAt(0.0, Qt::white);
- float ff = m_floating ? 1.0f : 0.5f;
+ float ff = m_floating ? 1.0f : 0.6f;
+ int alpha = m_floating ? 160 : ( m_selected ? 80 : 220 );
+ gradient.setColorAt(0.0, m_floating ? QColor(255, 255, 255, alpha) : QColor(50, 50, 50, alpha));
if (m_note.type == Note::NORMAL) {
- gradient.setColorAt(0.2, QColor(100 * ff, 100 * ff, 255 * ff));
- gradient.setColorAt(0.8, QColor(100 * ff, 100 * ff, 255 * ff));
- gradient.setColorAt(1.0, QColor(100 * ff, 100 * ff, 200 * ff));
+ gradient.setColorAt(0.2, QColor(100 * ff, 100 * ff, 255 * ff, alpha));
+ gradient.setColorAt(0.8, QColor(100 * ff, 100 * ff, 255 * ff, alpha));
+ gradient.setColorAt(1.0, QColor(100 * ff, 100 * ff, 200 * ff, alpha));
} else if (m_note.type == Note::GOLDEN) {
- gradient.setColorAt(0.2, QColor(255 * ff, 255 * ff, 100 * ff));
- gradient.setColorAt(0.8, QColor(255 * ff, 255 * ff, 100 * ff));
- gradient.setColorAt(1.0, QColor(160 * ff, 160 * ff, 100 * ff));
+ gradient.setColorAt(0.2, QColor(255 * ff, 255 * ff, 100 * ff, alpha));
+ gradient.setColorAt(0.8, QColor(255 * ff, 255 * ff, 100 * ff, alpha));
+ gradient.setColorAt(1.0, QColor(160 * ff, 160 * ff, 100 * ff, alpha));
} else if (m_note.type == Note::FREESTYLE) {
- gradient.setColorAt(0.2, QColor(100 * ff, 180 * ff, 100 * ff));
- gradient.setColorAt(0.8, QColor(100 * ff, 180 * ff, 100 * ff));
- gradient.setColorAt(1.0, QColor(100 * ff, 120 * ff, 100 * ff));
+ gradient.setColorAt(0.2, QColor(100 * ff, 180 * ff, 100 * ff, alpha));
+ gradient.setColorAt(0.8, QColor(100 * ff, 180 * ff, 100 * ff, alpha));
+ gradient.setColorAt(1.0, QColor(100 * ff, 120 * ff, 100 * ff, alpha));
}
QPainter painter;
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 14:35:17
|
Module: editor
Branch: master
Commit: 9583a36d256423687624e1cc250f75eeb093b583
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 16:31:44 2011 +0200
Initial implementation of music position indicator / seeking handle.
Seeking works, but position needs interpolation for this to be usable.
(Time updates are signaled too slowly, at about 500ms at minimum.)
---
editorapp.cc | 6 ++--
notegraphwidget.cc | 68 ++++++++++++++++++++++++++++++++++++++++++++++-----
notegraphwidget.hh | 18 ++++++++++++-
notelabel.cc | 2 +-
4 files changed, 81 insertions(+), 13 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 6c1bae9..073049e 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -64,11 +64,12 @@ EditorApp::EditorApp(QWidget *parent): QMainWindow(parent), projectFileName(), h
// Audio stuff
player = new Phonon::MediaObject(this);
audioOutput = new Phonon::AudioOutput(this);
- player->setTickInterval(500);
+ player->setTickInterval(100);
Phonon::createPath(player, audioOutput);
// Audio signals
connect(player, SIGNAL(tick(qint64)), this, SLOT(audioTick(qint64)));
connect(player, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
+ connect(noteGraph, SIGNAL(seek(qint64)), player, SLOT(seek(qint64)));
}
void EditorApp::operationDone(const Operation &op)
@@ -457,8 +458,7 @@ void EditorApp::metaDataChanged()
void EditorApp::audioTick(qint64 time)
{
- // TODO: Update a cursor in NoteGraphWidget
- // (the cursor needs to be first implemented)
+ if (noteGraph) noteGraph->updateMusicPos(time);
}
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 6f0ddb4..4c189ec 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -16,7 +16,7 @@ namespace {
}
NoteGraphWidget::NoteGraphWidget(QWidget *parent)
- : QLabel(parent), m_panHotSpot(), m_selectedNote(), m_selectedAction(NONE), m_actionHappened(), m_pitch()
+ : QLabel(parent), m_panHotSpot(), m_selectedNote(), m_selectedAction(NONE), m_seeking(), m_actionHappened(), m_pitch(), m_seekHandle(this)
{
// FIXME: Temporary hack to make testing quicker
analyzeMusic("music.ogg");
@@ -186,16 +186,30 @@ void NoteGraphWidget::updateNotes()
}
}
+void NoteGraphWidget::updateMusicPos(qint64 time)
+{
+ int x = s2px(time / 1000.0) - m_seekHandle.width() / 2;
+ m_seekHandle.move(x, 0);
+}
+
void NoteGraphWidget::mousePressEvent(QMouseEvent *event)
{
NoteLabel *child = qobject_cast<NoteLabel*>(childAt(event->pos()));
if (!child) {
- // Left click empty area = pan
- if (event->button() == Qt::LeftButton)
+ SeekHandle *seekh = qobject_cast<SeekHandle*>(childAt(event->pos()));
+ if (!seekh) {
+ // Left click empty area = pan
+ if (event->button() == Qt::LeftButton)
+ m_panHotSpot = event->pos();
+ // Right click empty area = deselect
+ if (event->button() == Qt::RightButton)
+ selectNote(NULL);
+ } else {
+ // Seeking
+ m_seeking = true;
m_panHotSpot = event->pos();
- // Right click empty area = deselect
- if (event->button() == Qt::RightButton)
- selectNote(NULL);
+ setCursor(QCursor(Qt::SizeHorCursor));
+ }
return;
}
if (child->isHidden()) return;
@@ -263,6 +277,7 @@ void NoteGraphWidget::mouseReleaseEvent(QMouseEvent *event)
}
m_actionHappened = false;
m_panHotSpot = QPoint();
+ m_seeking = false;
setCursor(QCursor());
updateNotes();
}
@@ -302,8 +317,15 @@ void NoteGraphWidget::mouseMoveEvent(QMouseEvent *event)
}
}
+ // Seeking
+ if (m_seeking) {
+ QPoint diff = event->pos() - m_panHotSpot;
+ std::cout << diff.x() << " " << event->pos().x() << " " << m_panHotSpot.x() << std::endl;
+ m_seekHandle.move(m_panHotSpot.x() + diff.x(), 0);
+ emit seek(1000 * px2s(m_seekHandle.x()));
+ }
// Pan
- if (!m_panHotSpot.isNull()) {
+ else if (!m_panHotSpot.isNull()) {
setCursor(QCursor(Qt::ClosedHandCursor));
QScrollArea *scrollArea = NULL;
if (parentWidget())
@@ -422,6 +444,38 @@ double NoteGraphWidget::px2s(int px) const { return m_pitch->px2time(px); }
int NoteGraphWidget::n2px(int note) const { return m_pitch->note2px(note); }
int NoteGraphWidget::px2n(int px) const { return m_pitch->px2note(px); }
+
+
+SeekHandle::SeekHandle(QWidget *parent)
+ : QLabel(parent)
+{
+ // FIXME: height from notegraph
+ QImage image(8, 768, QImage::Format_ARGB32_Premultiplied);
+ image.fill(qRgba(0, 0, 0, 0));
+ QLinearGradient gradient(0, 0, image.width()-1, 0);
+ gradient.setColorAt(0.0, QColor(255,255,0,50));
+ gradient.setColorAt(0.5, QColor(255,255,0,200));
+ gradient.setColorAt(1.0, QColor(255,255,0,50));
+
+ QPainter painter;
+ painter.begin(&image);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setBrush(gradient);
+ painter.drawRect(QRect(0, 0, image.width(), image.height()));
+
+ setPixmap(QPixmap::fromImage(image));
+
+ setStatusTip(tr("Seek by dragging"));
+}
+
+void SeekHandle::mouseMoveEvent(QMouseEvent *event)
+{
+ setCursor(QCursor(Qt::SizeHorCursor));
+ event->ignore();
+}
+
+
+
void FloatingGap::addNote(NoteLabel* n)
{
notes.push_back(n);
diff --git a/notegraphwidget.hh b/notegraphwidget.hh
index ffc2f92..e4ab11a 100644
--- a/notegraphwidget.hh
+++ b/notegraphwidget.hh
@@ -10,6 +10,16 @@
class NoteLabel;
typedef QList<NoteLabel*> NoteLabels;
+
+class SeekHandle: public QLabel
+{
+ Q_OBJECT
+public:
+ SeekHandle(QWidget *parent = 0);
+ void mouseMoveEvent(QMouseEvent *event);
+};
+
+
class NoteGraphWidget: public QLabel
{
Q_OBJECT
@@ -23,8 +33,9 @@ public:
void setLyrics(const VocalTrack &track);
void analyzeMusic(QString filepath);
void updateNotes();
+ void updateMusicPos(qint64 time);
- void selectNote(NoteLabel* note);
+ void selectNote(NoteLabel *note);
NoteLabel* selectedNote() const { return m_selectedNote; }
int getNoteLabelId(NoteLabel* note) const;
@@ -41,12 +52,13 @@ public:
signals:
void updateNoteInfo(NoteLabel*);
void operationDone(const Operation&);
+ void seek(qint64 time);
protected:
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
- void mouseDoubleClickEvent(QMouseEvent * event);
+ void mouseDoubleClickEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent * event);
void keyPressEvent(QKeyEvent *event);
@@ -57,9 +69,11 @@ private:
QPoint m_panHotSpot;
NoteLabel* m_selectedNote;
enum NoteAction { NONE, RESIZE, MOVE } m_selectedAction;
+ bool m_seeking;
bool m_actionHappened;
NoteLabels m_notes;
QScopedPointer<PitchVis> m_pitch;
+ SeekHandle m_seekHandle;
};
diff --git a/notelabel.cc b/notelabel.cc
index 7a7ff05..599722e 100644
--- a/notelabel.cc
+++ b/notelabel.cc
@@ -77,7 +77,7 @@ void NoteLabel::createPixmap(QSize size)
setPixmap(QPixmap::fromImage(image));
- setStatusTip(QString("Lyric: ") + lyric());
+ setStatusTip(tr("Lyric: ") + lyric());
updateNote();
}
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 13:28:41
|
Module: editor
Branch: master
Commit: 12732218441d457e6794711b6ae1a2b8acf57aec
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 15:26:48 2011 +0200
Notes become non-floating as soon as started to move/resize.
Fixes issue with not being able to resize floating note.
---
notegraphwidget.cc | 19 +++++++++----------
1 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 5636842..6f0ddb4 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -252,19 +252,11 @@ void NoteGraphWidget::mouseReleaseEvent(QMouseEvent *event)
m_selectedNote->move(m_selectedNote->pos().x(), n2px(px2n(m_selectedNote->pos().y())));
if (m_actionHappened) {
// Operation for undo stack & saving
- int notelabelid = getNoteLabelId(m_selectedNote);
Operation op("SETGEOM");
- op << notelabelid
+ op << getNoteLabelId(m_selectedNote)
<< m_selectedNote->x() << m_selectedNote->y()
<< m_selectedNote->width() << m_selectedNote->height();
doOperation(op, Operation::NO_EXEC);
- // See if it needs to be unfloated
- if (m_selectedNote->isFloating()) {
- Operation fop("FLOATING"); fop << notelabelid << false;
- Operation combiner("COMBINER"); combiner << 2;
- doOperation(fop);
- doOperation(combiner);
- }
}
}
m_selectedAction = NONE;
@@ -301,7 +293,14 @@ void NoteGraphWidget::mouseDoubleClickEvent(QMouseEvent *event)
void NoteGraphWidget::mouseMoveEvent(QMouseEvent *event)
{
- m_actionHappened = true; // We have movement, so resize/move can be accepted
+ if (!m_actionHappened) {
+ m_actionHappened = true; // We have movement, so resize/move can be accepted
+ // See if the note needs to be unfloated
+ if (m_selectedAction != NONE && m_selectedNote && m_selectedNote->isFloating()) {
+ Operation op("FLOATING"); op << getNoteLabelId(m_selectedNote) << false;
+ doOperation(op);
+ }
+ }
// Pan
if (!m_panHotSpot.isNull()) {
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 13:17:39
|
Module: editor
Branch: master
Commit: 8c1304798c090dd4d765eb94acb64cdf18d90668
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 15:16:19 2011 +0200
Notes imported from US TXT are set to non-floating.
---
notegraphwidget.cc | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 32e6cb5..5636842 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -79,14 +79,12 @@ void NoteGraphWidget::setLyrics(const VocalTrack &track)
setFixedSize(s2px(track.endTime), h());
- bool first = true;
const Notes ¬es = track.notes;
for (Notes::const_iterator it = notes.begin(); it != notes.end(); ++it) {
if (it->type == Note::NORMAL || it->type == Note::GOLDEN || it->type == Note::FREESTYLE) {
m_notes.push_back(new NoteLabel(*it, this, QPoint(s2px(it->begin), n2px(it->note)),
- QSize(s2px(it->length()), 0), !first));
+ QSize(s2px(it->length()), 0), false));
doOperation(opFromNote(*m_notes.back(), m_notes.size()-1), Operation::NO_EXEC);
- first = false;
}
}
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 11:25:11
|
Module: editor
Branch: master
Commit: 2636231fa65aff8f08a213e1b840ce93f8ad5793
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 13:23:42 2011 +0200
Note height adjust from keyboard is now undoable.
Info is also now updated.
---
notegraphwidget.cc | 14 +++++++++++---
1 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index e755f1e..32e6cb5 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -344,12 +344,18 @@ void NoteGraphWidget::keyPressEvent(QKeyEvent *event)
break;
case Qt::Key_Up: // Move note up
if (m_selectedNote) {
- m_selectedNote->move(m_selectedNote->x(), m_pitch->note2px(m_pitch->px2note(m_selectedNote->y()) + 1));
+ Operation op("MOVE");
+ op << getNoteLabelId(m_selectedNote);
+ op << m_selectedNote->x() << n2px(px2n(m_selectedNote->y()) + 1);
+ doOperation(op);
}
break;
case Qt::Key_Down: // Move note down
if (m_selectedNote) {
- m_selectedNote->move(m_selectedNote->x(), m_pitch->note2px(m_pitch->px2note(m_selectedNote->y()) - 1));
+ Operation op("MOVE");
+ op << getNoteLabelId(m_selectedNote);
+ op << m_selectedNote->x() << n2px(px2n(m_selectedNote->y()) - 1);
+ doOperation(op);
}
break;
case Qt::Key_Delete: // Delete selected note
@@ -407,8 +413,10 @@ void NoteGraphWidget::doOperation(const Operation& op, Operation::OperationFlags
}
updateNotes();
}
- if (!(flags & Operation::NO_EMIT))
+ if (!(flags & Operation::NO_EMIT)) {
emit operationDone(op);
+ emit updateNoteInfo(m_selectedNote);
+ }
}
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 10:49:41
|
Module: editor
Branch: master
Commit: f23d5da0c32863ecdb209e10051d9034166bc260
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 12:48:39 2011 +0200
Audio decoding & analyzing happens small buffer at a time.
---
pitchvis.cc | 47 ++++++++++++++++++++---------------------------
1 files changed, 20 insertions(+), 27 deletions(-)
diff --git a/pitchvis.cc b/pitchvis.cc
index 230ee0d..5bcca18 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -22,42 +22,35 @@ template <typename T> void readVec(std::string const& filename, std::vector<T>&
}
PitchVis::PitchVis(std::string const& filename, QWidget *parent): QWidget(parent), step(512), height(768) {
-#if 0 // Change this to 1 to use old raw loading
- std::vector<float> data;
- try { readVec("music.raw", data); } catch(std::exception& e) { std::cerr << e.what() << std::endl; return; }
-#else
- // FIXME: This is rather horrible and needs clean-up
- const unsigned buffersize = 512;
- std::vector<float> datas(buffersize);
+ QProgressDialog progress(tr("Initializing audio decoding..."), tr("&Abort"), 0, 1, this);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setValue(0);
+
+ // Initialize FFmpeg decoding
FFmpeg mpeg(false, true, filename, 44100);
- uint64_t pos = 0;
while(std::isnan(mpeg.duration())); // Wait for ffmpeg to be ready
- usleep(3000000); // Wait some more
- while (!mpeg.audioQueue.eof(pos)) {
- // TODO: Should probably analyze buffer-by-buffer
- mpeg.audioQueue(&datas[pos], &*datas.end(), pos);
- datas.resize(datas.size() + buffersize);
- pos += buffersize;
- usleep(10); // HACKHACK
- }
- // We want only one channel
- std::vector<float> data;
- data.reserve(datas.size()/2);
- for (uint64_t i = 0; i < datas.size() / 2; ++i) {
- data.push_back(datas[i*2]);
- }
-#endif
- unsigned width = data.size() / step;
+ usleep(1000000); // Wait some more
+
+ unsigned width = mpeg.duration() * 44100 / step;
img.resize(width * height);
Analyzer analyzer(44100, "");
- QProgressDialog progress(tr("Analyzing pitch data..."), tr("&Abort"), 0, width, this);
- progress.setWindowModality(Qt::WindowModal);
+ progress.setMaximum(width);
+ progress.setLabelText(tr("Analyzing pitch data..."));
for (unsigned x = 0; x < width; ++x) {
progress.setValue(x);
if (progress.wasCanceled()) return;
- analyzer.input(data.begin() + x * step, data.begin() + (x + 1) * step);
+ // Get decoded samples from ffmpeg
+ std::vector<float> data(step*2);
+ mpeg.audioQueue(&*data.begin(), &*data.end(), x * step * 2);
+
+ // Sample iterators for getting only one channel
+ da::step_iterator<float> beginIt(&*data.begin(), 2);
+ da::step_iterator<float> endIt(&*data.end(), 2);
+
+ // Analyze
+ analyzer.input(beginIt, endIt);
analyzer.process();
Analyzer::Peaks peaks = analyzer.getPeaks();
for (unsigned i = 0; i < peaks.size(); ++i) {
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-19 09:48:54
|
Module: editor
Branch: master
Commit: fb73607c6a53df8e63d482a3c4c0cc597bc0feb7
Author: Lasse Karkkainen <tro...@tr...>
Date: Wed Jan 19 10:48:41 2011 +0100
Improve pitch analysis response speed
---
pitch.cc | 6 +++---
pitch.hh | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/pitch.cc b/pitch.cc
index 566ba72..a5a6c3b 100644
--- a/pitch.cc
+++ b/pitch.cc
@@ -194,13 +194,13 @@ void Analyzer::mergeWithOld(Tones& tones) const {
if (it != tones.end() && *it == *oldit) {
// Merge the old tone into the new tone
it->age = oldit->age + 1;
- it->levelSlow = 0.8 * oldit->levelSlow + 0.2 * it->level;
- it->freqSlow = 0.8 * oldit->freqSlow + 0.2 * it->freq;
+ it->levelSlow = 0.5 * oldit->levelSlow + 0.5 * it->level;
+ it->freqSlow = 0.5 * oldit->freqSlow + 0.5 * it->freq;
} else if (oldit->levelSlow > 1e-8) {
// Insert a decayed version of the old tone into new tones
Tone& t = *tones.insert(it, *oldit);
t.level = 0.0;
- t.levelSlow *= 0.8;
+ t.levelSlow *= 0.01;
}
}
}
diff --git a/pitch.hh b/pitch.hh
index 6061bfc..bfe6240 100644
--- a/pitch.hh
+++ b/pitch.hh
@@ -13,7 +13,7 @@ static inline double dB2level(double db) { return std::pow(10.0, db / 20.0); }
/// A tone is a collection of a base frequency (freq) and all its harmonics
struct Tone {
static const std::size_t MAXHARM = 16; ///< The maximum number of harmonics tracked
- static const std::size_t MINAGE = 6; ///< The minimum age required for a tone to be output
+ static const std::size_t MINAGE = 2; ///< The minimum age required for a tone to be output
double freq; ///< Frequency (Hz)
double freqSlow; ///< Stable frequency
double level; ///< Level (linear)
|
|
From: Yoda-JM <yo...@us...> - 2011-01-19 09:32:08
|
Module: editor
Branch: master
Commit: 1ae340bdbb7a0b2d9802bea189860f440198fafc
Author: Vincent Le Ligeour <yo...@us...>
Date: Wed Jan 19 10:31:31 2011 +0100
Added standalone Phonon detection
---
CMakeLists.txt | 15 ++++++++++++---
cmake/FindPhonon.cmake | 28 ++++++++++++++++++++++++++++
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 860dd55..714c48e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,9 +28,18 @@ find_package(Boost 1.36 REQUIRED COMPONENTS thread regex filesystem system)
include_directories(${Boost_INCLUDE_DIRS})
list(APPEND LIBS ${Boost_LIBRARIES})
-find_package(Qt4 COMPONENTS QtCore QtGui QtXml Phonon REQUIRED)
-include(${QT_USE_FILE})
-list(APPEND LIBS ${QT_LIBRARIES})
+find_package(Phonon)
+if(Phonon_FOUND)
+ include_directories(${Phonon_INCLUDE_DIRS})
+ list(APPEND LIBS ${Phonon_LIBRARIES})
+ find_package(Qt4 COMPONENTS QtCore QtGui QtXml REQUIRED)
+ include(${QT_USE_FILE})
+ list(APPEND LIBS ${QT_LIBRARIES})
+else(Phonon_FOUND)
+ find_package(Qt4 COMPONENTS QtCore QtGui QtXml Phonon REQUIRED)
+ include(${QT_USE_FILE})
+ list(APPEND LIBS ${QT_LIBRARIES})
+endif(Phonon_FOUND)
# Headers that need MOC need to be defined separately
file(GLOB MOC_HEADER_FILES "editorapp.hh" "notelabel.hh" "notegraphwidget.hh")
diff --git a/cmake/FindPhonon.cmake b/cmake/FindPhonon.cmake
new file mode 100644
index 0000000..c3da1a6
--- /dev/null
+++ b/cmake/FindPhonon.cmake
@@ -0,0 +1,28 @@
+# - Try to find Phonon
+# Once done, this will define
+#
+# Phonon_FOUND - system has Phonon
+# Phonon_INCLUDE_DIRS - the Phonon include directories
+# Phonon_LIBRARIES - link these to use Phonon
+#
+# See documentation on how to write CMake scripts at
+# http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries
+
+include(LibFindMacros)
+
+libfind_pkg_check_modules(Phonon_PKGCONF phonon)
+
+find_path(Phonon_INCLUDE_DIR
+ NAMES phonon/Global
+ PATHS ${Phonon_PKGCONF_INCLUDE_DIRS}
+)
+
+find_library(Phonon_LIBRARY
+ NAMES phonon
+ PATHS ${Phonon_PKGCONF_LIBRARY_DIRS}
+)
+
+set(Phonon_PROCESS_INCLUDES Phonon_INCLUDE_DIR)
+set(Phonon_PROCESS_LIBS Phonon_LIBRARY)
+libfind_process(Phonon)
+
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 09:07:09
|
Module: editor
Branch: master
Commit: 4d129f596854bc7b170f57fd024af893eae75f25
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 11:03:53 2011 +0200
Importing a music file now starts the analyzer on it.
PitchVis is now stored as a pointer. Pixel access was changed from
operator() to pixel() to make it prettier with pointer type.
---
editorapp.cc | 7 +++--
notegraphwidget.cc | 65 ++++++++++++++++++++++++++++-----------------------
notegraphwidget.hh | 8 ++++--
pitchvis.cc | 10 ++++----
pitchvis.hh | 4 +-
5 files changed, 52 insertions(+), 42 deletions(-)
diff --git a/editorapp.cc b/editorapp.cc
index 614ff9c..6c1bae9 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -82,7 +82,7 @@ void EditorApp::operationDone(const Operation &op)
void EditorApp::doOpStack()
{
- noteGraph->clear();
+ noteGraph->clearNotes();
// Re-apply all operations in the stack
// FIXME: This technique cannot work quickly enough, since analyzing would also be started from scratch
for (OperationStack::const_iterator opit = opStack.begin(); opit != opStack.end(); ++opit) {
@@ -143,7 +143,7 @@ void EditorApp::updateNoteInfo(NoteLabel *note)
void EditorApp::on_actionNew_triggered()
{
if (promptSaving()) {
- noteGraph->clear();
+ noteGraph->clearNotes();
projectFileName = "";
}
updateMenuStates();
@@ -357,7 +357,8 @@ void EditorApp::on_actionMusicFile_triggered()
// FIXME: Temporary test
audioOutput->setVolume(50);
player->play();
- // TODO: Extract the raw data and fire up an analyzer
+ // Fire up analyzer
+ noteGraph->analyzeMusic(fileName);
}
}
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index c2e2760..e755f1e 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -16,27 +16,11 @@ namespace {
}
NoteGraphWidget::NoteGraphWidget(QWidget *parent)
- : QLabel(parent), m_pitch("music.ogg"), m_panHotSpot(), m_selectedNote(), m_selectedAction(NONE), m_actionHappened()
+ : QLabel(parent), m_panHotSpot(), m_selectedNote(), m_selectedAction(NONE), m_actionHappened(), m_pitch()
{
- unsigned width = m_pitch.width(), height = m_pitch.height;
- QProgressDialog progress(tr("Rendering pitch data..."), tr("&Abort"), 0, width, this);
- progress.setWindowModality(Qt::WindowModal);
-
- QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
- unsigned* rgba = reinterpret_cast<unsigned*>(image.bits());
- for (unsigned x = 0; x < width; ++x) {
- progress.setValue(x);
- if (progress.wasCanceled()) break;
+ // FIXME: Temporary hack to make testing quicker
+ analyzeMusic("music.ogg");
- for (unsigned y = 0; y < height; ++y) {
- rgba[y * width + x] = m_pitch(x, y).rgba();
- }
- }
- setPixmap(QPixmap::fromImage(image));
- progress.setValue(width);
-
- // FIXME: Width should come from song length * pixPerSec
- setFixedSize(std::max(width, (unsigned)1024), h());
setFocusPolicy(Qt::StrongFocus);
setWhatsThis(tr("Note graph that displays the song notes and allows you to manipulate them."));
@@ -58,7 +42,7 @@ void NoteGraphWidget::selectNote(NoteLabel* note)
emit updateNoteInfo(m_selectedNote);
}
-void NoteGraphWidget::clear()
+void NoteGraphWidget::clearNotes()
{
selectNote(NULL);
// Clear NoteLabels
@@ -74,13 +58,13 @@ void NoteGraphWidget::setLyrics(QString lyrics)
{
QTextStream ts(&lyrics, QIODevice::ReadOnly);
- clear();
+ clearNotes();
bool first = true;
while (!ts.atEnd()) {
QString word;
ts >> word;
if (!word.isEmpty()) {
- m_notes.push_back(new NoteLabel(Note(word.toStdString()), this, QPoint(0, m_pitch.note2px(24)), QSize(), !first));
+ m_notes.push_back(new NoteLabel(Note(word.toStdString()), this, QPoint(0, m_pitch->note2px(24)), QSize(), !first));
doOperation(opFromNote(*m_notes.back(), m_notes.size()-1), Operation::NO_EXEC);
first = false;
}
@@ -91,7 +75,7 @@ void NoteGraphWidget::setLyrics(QString lyrics)
void NoteGraphWidget::setLyrics(const VocalTrack &track)
{
- clear();
+ clearNotes();
setFixedSize(s2px(track.endTime), h());
@@ -125,6 +109,29 @@ void NoteGraphWidget::finalizeNewLyrics()
updateNotes();
}
+void NoteGraphWidget::analyzeMusic(QString filepath)
+{
+ m_pitch.reset(new PitchVis(filepath.toStdString(), this));
+ unsigned width = m_pitch->width(), height = m_pitch->height;
+ QProgressDialog progress(tr("Rendering pitch data..."), tr("&Abort"), 0, width, this);
+ progress.setWindowModality(Qt::WindowModal);
+
+ QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
+ unsigned* rgba = reinterpret_cast<unsigned*>(image.bits());
+ for (unsigned x = 0; x < width; ++x) {
+ progress.setValue(x);
+ if (progress.wasCanceled()) break;
+
+ for (unsigned y = 0; y < height; ++y) {
+ rgba[y * width + x] = m_pitch->pixel(x, y).rgba();
+ }
+ }
+ setPixmap(QPixmap::fromImage(image));
+ progress.setValue(width);
+
+ setFixedSize(width, height);
+}
+
int NoteGraphWidget::getNoteLabelId(NoteLabel* note) const
{
for (int i = 0; i < m_notes.size(); ++i)
@@ -337,12 +344,12 @@ void NoteGraphWidget::keyPressEvent(QKeyEvent *event)
break;
case Qt::Key_Up: // Move note up
if (m_selectedNote) {
- m_selectedNote->move(m_selectedNote->x(), m_pitch.note2px(m_pitch.px2note(m_selectedNote->y()) + 1));
+ m_selectedNote->move(m_selectedNote->x(), m_pitch->note2px(m_pitch->px2note(m_selectedNote->y()) + 1));
}
break;
case Qt::Key_Down: // Move note down
if (m_selectedNote) {
- m_selectedNote->move(m_selectedNote->x(), m_pitch.note2px(m_pitch.px2note(m_selectedNote->y()) - 1));
+ m_selectedNote->move(m_selectedNote->x(), m_pitch->note2px(m_pitch->px2note(m_selectedNote->y()) - 1));
}
break;
case Qt::Key_Delete: // Delete selected note
@@ -405,10 +412,10 @@ void NoteGraphWidget::doOperation(const Operation& op, Operation::OperationFlags
}
-int NoteGraphWidget::s2px(double sec) const { return m_pitch.time2px(sec); }
-double NoteGraphWidget::px2s(int px) const { return m_pitch.px2time(px); }
-int NoteGraphWidget::n2px(int note) const { return m_pitch.note2px(note); }
-int NoteGraphWidget::px2n(int px) const { return m_pitch.px2note(px); }
+int NoteGraphWidget::s2px(double sec) const { return m_pitch->time2px(sec); }
+double NoteGraphWidget::px2s(int px) const { return m_pitch->px2time(px); }
+int NoteGraphWidget::n2px(int note) const { return m_pitch->note2px(note); }
+int NoteGraphWidget::px2n(int px) const { return m_pitch->px2note(px); }
void FloatingGap::addNote(NoteLabel* n)
{
diff --git a/notegraphwidget.hh b/notegraphwidget.hh
index 15bfc7a..ffc2f92 100644
--- a/notegraphwidget.hh
+++ b/notegraphwidget.hh
@@ -5,6 +5,7 @@
#include "operation.hh"
#include <QLabel>
#include <QList>
+#include <QScopedPointer>
class NoteLabel;
typedef QList<NoteLabel*> NoteLabels;
@@ -17,9 +18,10 @@ public:
NoteGraphWidget(QWidget *parent = 0);
- void clear();
+ void clearNotes();
void setLyrics(QString lyrics);
void setLyrics(const VocalTrack &track);
+ void analyzeMusic(QString filepath);
void updateNotes();
void selectNote(NoteLabel* note);
@@ -33,7 +35,7 @@ public:
double px2s(int px) const;
int n2px(int note) const;
int px2n(int px) const;
- int h() const { return m_pitch.height; }
+ int h() const { return m_pitch->height; }
signals:
@@ -57,7 +59,7 @@ private:
enum NoteAction { NONE, RESIZE, MOVE } m_selectedAction;
bool m_actionHappened;
NoteLabels m_notes;
- PitchVis m_pitch;
+ QScopedPointer<PitchVis> m_pitch;
};
diff --git a/pitchvis.cc b/pitchvis.cc
index 698a8e1..230ee0d 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -21,7 +21,7 @@ template <typename T> void readVec(std::string const& filename, std::vector<T>&
if (!f) throw std::runtime_error("Error reading " + filename);
}
-PitchVis::PitchVis(std::string const& filename): step(512), height(768) {
+PitchVis::PitchVis(std::string const& filename, QWidget *parent): QWidget(parent), step(512), height(768) {
#if 0 // Change this to 1 to use old raw loading
std::vector<float> data;
try { readVec("music.raw", data); } catch(std::exception& e) { std::cerr << e.what() << std::endl; return; }
@@ -66,12 +66,12 @@ PitchVis::PitchVis(std::string const& filename): step(512), height(768) {
float value = 0.003 * (level2dB(peaks[i].level) + 80.0);
if (value <= 0.0) continue;
Pixel p(0.0f, 0.0f, value);
- (*this)(x, y) += p;
+ pixel(x, y) += p;
p.r *= 0.5;
p.g *= 0.5;
p.b *= 0.5;
- (*this)(x, y + 1) += p;
- (*this)(x, y - 1) += p;
+ pixel(x, y + 1) += p;
+ pixel(x, y - 1) += p;
}
Analyzer::Tones tones = analyzer.getTones();
unsigned int i = 0;
@@ -82,7 +82,7 @@ PitchVis::PitchVis(std::string const& filename): step(512), height(768) {
float value = 0.003 * (level2dB(it->levelSlow) + 80.0);
if (value <= 0.0) continue;
Pixel p(0.0f, value, 0.0f);
- for (int j = int(y) - 2; j <= int(y) + 2; ++j) (*this)(x, j) += p;
+ for (int j = int(y) - 2; j <= int(y) + 2; ++j) pixel(x, j) += p;
}
}
progress.setValue(width);
diff --git a/pitchvis.hh b/pitchvis.hh
index 1275a1a..62f7f03 100644
--- a/pitchvis.hh
+++ b/pitchvis.hh
@@ -37,9 +37,9 @@ class PitchVis: public QWidget {
MusicalScale scale;
public:
const std::size_t height;
- Pixel& operator()(std::size_t x, std::size_t y) { return img[x * height + y]; }
+ Pixel& pixel(std::size_t x, std::size_t y) { return img[x * height + y]; }
std::size_t width() const { return img.size() / height; }
- PitchVis(std::string const& filename);
+ PitchVis(std::string const& filename, QWidget *parent = NULL);
unsigned freq2px(double freq) const;
unsigned note2px(double note) const;
double px2note(unsigned px) const;
|
|
From: Tapio V. <aa...@us...> - 2011-01-19 08:31:33
|
Module: editor
Branch: master
Commit: 5cc517e4dd6f0f082a6e021c03a4630b54ae9ab1
Author: Tapio Vierros <tap...@gm...>
Date: Wed Jan 19 10:30:14 2011 +0200
Use Phonon instead of QtMultimediaKit.
For both playback & metadata resolving.
---
CMakeLists.txt | 8 +-------
editorapp.cc | 47 +++++++++++++++++++++++++++++------------------
editorapp.hh | 13 ++++++++-----
3 files changed, 38 insertions(+), 30 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 013c270..860dd55 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,16 +28,10 @@ find_package(Boost 1.36 REQUIRED COMPONENTS thread regex filesystem system)
include_directories(${Boost_INCLUDE_DIRS})
list(APPEND LIBS ${Boost_LIBRARIES})
-find_package(Qt4 COMPONENTS QtCore QtGui QtXml QtNetwork QtMultimediaKit REQUIRED)
+find_package(Qt4 COMPONENTS QtCore QtGui QtXml Phonon REQUIRED)
include(${QT_USE_FILE})
list(APPEND LIBS ${QT_LIBRARIES})
-# Not used directly but is a dep of QtMultimediaKit
-if(${CMAKE_SYSTEM_NAME} MATCHES Linux)
- find_package(LibPulse REQUIRED)
- list(APPEND LIBS ${LibPulse_LIBRARIES})
-endif()
-
# Headers that need MOC need to be defined separately
file(GLOB MOC_HEADER_FILES "editorapp.hh" "notelabel.hh" "notegraphwidget.hh")
diff --git a/editorapp.cc b/editorapp.cc
index c3042f3..614ff9c 100644
--- a/editorapp.cc
+++ b/editorapp.cc
@@ -1,4 +1,6 @@
#include <QtGui>
+#include <phonon/MediaObject>
+#include <phonon/AudioOutput>
#include <iostream>
#include "editorapp.hh"
#include "notelabel.hh"
@@ -41,8 +43,6 @@ EditorApp::EditorApp(QWidget *parent): QMainWindow(parent), projectFileName(), h
updateNoteInfo(NULL);
song.reset(new Song);
- player.reset(new QMediaPlayer);
- connect(player.data(), SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
// Some icons to menus to make them prettier
ui.actionNew->setIcon(QIcon::fromTheme("document-new"));
@@ -60,6 +60,15 @@ EditorApp::EditorApp(QWidget *parent): QMainWindow(parent), projectFileName(), h
hasUnsavedChanges = false;
updateMenuStates();
+
+ // Audio stuff
+ player = new Phonon::MediaObject(this);
+ audioOutput = new Phonon::AudioOutput(this);
+ player->setTickInterval(500);
+ Phonon::createPath(player, audioOutput);
+ // Audio signals
+ connect(player, SIGNAL(tick(qint64)), this, SLOT(audioTick(qint64)));
+ connect(player, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
}
void EditorApp::operationDone(const Operation &op)
@@ -344,14 +353,9 @@ void EditorApp::on_actionMusicFile_triggered()
ui.valMusicFile->setText(fileName);
ui.tabWidget->setCurrentIndex(1); // Switch to song properties tab
// Metadata is updated when it becomes available (signal)
- QBuffer stream;
- stream.open(QBuffer::ReadWrite);
- player->setMedia(QUrl::fromLocalFile(fileName), &stream);
- std::cout << stream.size() << std::endl;
- char lol;
- stream.getChar(&lol);
+ player->setCurrentSource(Phonon::MediaSource(QUrl::fromLocalFile(fileName)));
// FIXME: Temporary test
- player->setVolume(50);
+ audioOutput->setVolume(50);
player->play();
// TODO: Extract the raw data and fire up an analyzer
}
@@ -437,19 +441,26 @@ void EditorApp::updateSongMeta(bool readFromSongToUI)
void EditorApp::metaDataChanged()
{
- if (!player.isNull()) {
- if (player->metaData(QtMultimediaKit::Title).isValid())
- song->title = player->metaData(QtMultimediaKit::Title).toString().toStdString();
- if (player->metaData(QtMultimediaKit::AlbumArtist).isValid())
- song->artist = player->metaData(QtMultimediaKit::AlbumArtist).toString().toStdString();
- if (player->metaData(QtMultimediaKit::Genre).isValid())
- song->genre = player->metaData(QtMultimediaKit::Genre).toString().toStdString();
- if (player->metaData(QtMultimediaKit::Year).isValid())
- song->year = player->metaData(QtMultimediaKit::Year).toString().toStdString();
+ if (player) {
+ if (!player->metaData(Phonon::TitleMetaData).isEmpty())
+ song->title = player->metaData(Phonon::TitleMetaData).join(", ").toStdString();
+ if (!player->metaData(Phonon::ArtistMetaData).isEmpty())
+ song->artist = player->metaData(Phonon::ArtistMetaData).join(", ").toStdString();
+ if (!player->metaData(Phonon::GenreMetaData).isEmpty())
+ song->genre = player->metaData(Phonon::GenreMetaData).join(", ").toStdString();
+ if (!player->metaData("DATE").isEmpty())
+ song->year = player->metaData("DATE").join(", ").toStdString();
updateSongMeta(true);
}
}
+void EditorApp::audioTick(qint64 time)
+{
+ // TODO: Update a cursor in NoteGraphWidget
+ // (the cursor needs to be first implemented)
+}
+
+
void EditorApp::on_txtTitle_editingFinished() { updateSongMeta(); }
void EditorApp::on_txtArtist_editingFinished() { updateSongMeta(); }
void EditorApp::on_txtGenre_editingFinished() { updateSongMeta(); }
diff --git a/editorapp.hh b/editorapp.hh
index 4f72627..9e64861 100644
--- a/editorapp.hh
+++ b/editorapp.hh
@@ -1,14 +1,15 @@
#pragma once
-#include <QtMultimediaKit/QMediaPlayer>
-#include <QMessageBox>
#include "ui_editor.h"
#include "operation.hh"
#include "song.hh"
class NoteLabel;
class NoteGraphWidget;
-class QMediaPlayer;
+namespace Phonon {
+ class MediaObject;
+ class AudioOutput;
+}
class EditorApp: public QMainWindow
{
@@ -29,6 +30,7 @@ public slots:
void operationDone(const Operation &op);
void updateNoteInfo(NoteLabel *note);
void metaDataChanged();
+ void audioTick(qint64 time);
// Automatic slots
@@ -65,11 +67,12 @@ public slots:
private:
Ui::EditorApp ui;
- NoteGraphWidget* noteGraph;
+ NoteGraphWidget *noteGraph;
OperationStack opStack;
OperationStack redoStack;
QScopedPointer<Song> song;
- QScopedPointer<QMediaPlayer> player;
+ Phonon::MediaObject *player;
+ Phonon::AudioOutput *audioOutput;
QString projectFileName;
bool hasUnsavedChanges;
};
|
|
From: Tapio V. <aa...@us...> - 2011-01-18 17:15:35
|
Module: editor
Branch: master
Commit: bb282719ded81ba0d87cb91bf46bdb9759b4c624
Author: Tapio Vierros <tap...@gm...>
Date: Tue Jan 18 19:13:04 2011 +0200
Provide easy access to old .raw loader.
In case the current ffmpeg loader state is unsatisfactory.
---
pitchvis.cc | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/pitchvis.cc b/pitchvis.cc
index 8439855..698a8e1 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -22,9 +22,10 @@ template <typename T> void readVec(std::string const& filename, std::vector<T>&
}
PitchVis::PitchVis(std::string const& filename): step(512), height(768) {
-// std::vector<float> data;
-// try { readVec(filename, data); } catch(std::exception& e) { std::cerr << e.what() << std::endl; return; }
-
+#if 0 // Change this to 1 to use old raw loading
+ std::vector<float> data;
+ try { readVec("music.raw", data); } catch(std::exception& e) { std::cerr << e.what() << std::endl; return; }
+#else
// FIXME: This is rather horrible and needs clean-up
const unsigned buffersize = 512;
std::vector<float> datas(buffersize);
@@ -45,7 +46,7 @@ PitchVis::PitchVis(std::string const& filename): step(512), height(768) {
for (uint64_t i = 0; i < datas.size() / 2; ++i) {
data.push_back(datas[i*2]);
}
-
+#endif
unsigned width = data.size() / step;
img.resize(width * height);
Analyzer analyzer(44100, "");
|
|
From: Tapio V. <aa...@us...> - 2011-01-18 17:15:33
|
Module: editor Branch: master Commit: 5f5838049618ba6612f2557e866a6ac1968ec9da Author: Tapio Vierros <tap...@gm...> Date: Tue Jan 18 19:09:27 2011 +0200 Hack in FFmpeg audio decoding. Needs a lot of work. --- CMakeLists.txt | 13 ++- cmake/FindAVCodec.cmake | 48 ++++++++ cmake/FindAVFormat.cmake | 46 +++++++ cmake/FindAVUtil.cmake | 44 +++++++ cmake/FindSWScale.cmake | 46 +++++++ config.cmake.hh | 18 +++ editorapp.cc | 2 +- ffmpeg.cc | 294 ++++++++++++++++++++++++++++++++++++++++++++++ ffmpeg.hh | 244 ++++++++++++++++++++++++++++++++++++++ notegraphwidget.cc | 4 +- notegraphwidget.hh | 2 - pitchvis.cc | 25 ++++- xtime.hh | 29 +++++ 13 files changed, 807 insertions(+), 8 deletions(-) |
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-18 16:47:57
|
Module: editor
Branch: master
Commit: a0a9a97fdf9e97f1924ee6d7e8e0f8671334634a
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Jan 18 17:47:45 2011 +0100
Back to level-based rendering (instead of different colors by index).
---
pitchvis.cc | 22 +++-------------------
1 files changed, 3 insertions(+), 19 deletions(-)
diff --git a/pitchvis.cc b/pitchvis.cc
index be9ab74..97a3034 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -20,7 +20,7 @@ template <typename T> void readVec(std::string const& filename, std::vector<T>&
if (!f) throw std::runtime_error("Error reading " + filename);
}
-PitchVis::PitchVis(std::string const& filename): step(1024), height(768) {
+PitchVis::PitchVis(std::string const& filename): step(512), height(768) {
std::vector<float> data;
try { readVec(filename, data); } catch(std::exception& e) { std::cerr << e.what() << std::endl; return; }
unsigned width = data.size() / step;
@@ -57,24 +57,8 @@ PitchVis::PitchVis(std::string const& filename): step(1024), height(768) {
if (y == 0 || y >= height - 1) continue;
float value = 0.003 * (level2dB(it->levelSlow) + 80.0);
if (value <= 0.0) continue;
- Pixel p(value, value, value);
- switch(i) {
- case 0:
- p.r = 0.0; p.g = 1.0; p.b = 0.0;
- break;
- case 1:
- p.r = 0.15; p.g = 0.15; p.b = 0.0;
- break;
- case 2:
- p.r = 0.05; p.g = 0.05; p.b = 0.0;
- break;
- }
- (*this)(x, y) = p;
- for(int j = -3 ; j < 4 ; ++j) {
- if(y + j < 0 || y + j >= height) continue;
- (*this)(x, y + j) = p;
- }
- ++i;
+ Pixel p(0.0f, value, 0.0f);
+ for (int j = int(y) - 2; j <= int(y) + 2; ++j) (*this)(x, j) += p;
}
}
progress.setValue(width);
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-18 15:45:12
|
Module: editor
Branch: master
Commit: f68120c8ee79edbd536354845702b559b5f13622
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Jan 18 16:44:57 2011 +0100
Use middle C as note default position
---
notegraphwidget.cc | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 6c909b1..62080b6 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -81,7 +81,7 @@ void NoteGraphWidget::setLyrics(QString lyrics)
QString word;
ts >> word;
if (!word.isEmpty()) {
- m_notes.push_back(new NoteLabel(Note(word.toStdString()), this, QPoint(0, m_pitch.note2px(6)), QSize(), !first));
+ m_notes.push_back(new NoteLabel(Note(word.toStdString()), this, QPoint(0, m_pitch.note2px(24)), QSize(), !first));
doOperation(opFromNote(*m_notes.back(), m_notes.size()-1), Operation::NO_EXEC);
first = false;
}
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-18 15:41:20
|
Module: editor
Branch: master
Commit: 3bd7d31bd4d0c784bc722a838b2f18ceaf4b33b7
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Jan 18 16:41:10 2011 +0100
Remove the fixme comment too...
---
notegraphwidget.cc | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 7d911f3..6c909b1 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -324,7 +324,6 @@ void NoteGraphWidget::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Left: // Select note on the left
- // FIXME!
if (m_selectedNote && m_notes.size() > 1 && m_selectedNote != m_notes.front()) {
for (NoteLabels::iterator it = m_notes.begin(); it != m_notes.end(); ++it) {
if (m_selectedNote == *it) { selectNote(*(--it)); break; }
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-18 15:33:18
|
Module: editor
Branch: master
Commit: bf7d33001c7734922af2ea3d26e5ce7fc4c2d9c7
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Jan 18 16:33:06 2011 +0100
Fix moving leftwards in notes
---
notegraphwidget.cc | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 48cc7d1..7d911f3 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -325,11 +325,11 @@ void NoteGraphWidget::keyPressEvent(QKeyEvent *event)
switch (event->key()) {
case Qt::Key_Left: // Select note on the left
// FIXME!
-/* if (m_selectedNote && m_notes.size() > 1 && m_selectedNote != m_notes.front()) {
- for (NoteLabels::reverse_iterator it = m_notes.rbegin(); it != m_notes.rend(); ++it) {
- if (m_selectedNote == *it) { selectNote(*(++it)); break; }
+ if (m_selectedNote && m_notes.size() > 1 && m_selectedNote != m_notes.front()) {
+ for (NoteLabels::iterator it = m_notes.begin(); it != m_notes.end(); ++it) {
+ if (m_selectedNote == *it) { selectNote(*(--it)); break; }
}
- }*/
+ }
break;
case Qt::Key_Right: // Select note on the right
if (m_selectedNote && m_notes.size() > 1 && m_selectedNote != m_notes.back()) {
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-18 15:29:48
|
Module: editor
Branch: master
Commit: 69359edadb203d83029122b00223f8159c05131e
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Jan 18 16:29:34 2011 +0100
Make pitchvis do all pix<->time/note mapping so that notes are synchronized with pitchvis
---
notegraphwidget.cc | 42 +++++++++---------------------------------
notegraphwidget.hh | 7 +------
pitchvis.cc | 15 +++++++++------
pitchvis.hh | 8 ++++++++
4 files changed, 27 insertions(+), 45 deletions(-)
diff --git a/notegraphwidget.cc b/notegraphwidget.cc
index 5f0c8f2..48cc7d1 100644
--- a/notegraphwidget.cc
+++ b/notegraphwidget.cc
@@ -15,12 +15,8 @@ namespace {
}
}
-
-const int NoteGraphWidget::noteYStep = 28;
-
NoteGraphWidget::NoteGraphWidget(QWidget *parent)
- : QLabel(parent), m_pixPerSec(200), m_lowestNote(MusicalScale::getBaseId()), m_octaves(3),
- m_panHotSpot(), m_selectedNote(), m_selectedAction(NONE), m_actionHappened(), m_pitch("music.raw")
+ : QLabel(parent), m_pitch("music.raw"), m_panHotSpot(), m_selectedNote(), m_selectedAction(NONE), m_actionHappened()
{
unsigned width = m_pitch.width(), height = m_pitch.height;
QProgressDialog progress(tr("Rendering pitch data..."), tr("&Abort"), 0, width, this);
@@ -85,7 +81,7 @@ void NoteGraphWidget::setLyrics(QString lyrics)
QString word;
ts >> word;
if (!word.isEmpty()) {
- m_notes.push_back(new NoteLabel(Note(word.toStdString()), this, QPoint(0, n2px(m_lowestNote + 12 * m_octaves - 6)), QSize(), !first));
+ m_notes.push_back(new NoteLabel(Note(word.toStdString()), this, QPoint(0, m_pitch.note2px(6)), QSize(), !first));
doOperation(opFromNote(*m_notes.back(), m_notes.size()-1), Operation::NO_EXEC);
first = false;
}
@@ -98,12 +94,7 @@ void NoteGraphWidget::setLyrics(const VocalTrack &track)
{
clear();
- // Determine how many octaves are needed and what is the base line
- int diff = track.noteMax - track.noteMin;
- m_octaves = std::ceil(diff / 12.0f) + 1;
- m_lowestNote = clamp(track.noteMin - 6, 0, 1000);
- //std::cout << "Octaves: " << m_octaves << " Lowest:" << m_lowestNote << std::endl;
- setFixedSize(s2px(track.endTime), ndiff2px(m_octaves*12));
+ setFixedSize(s2px(track.endTime), h());
bool first = true;
const Notes ¬es = track.notes;
@@ -349,12 +340,12 @@ void NoteGraphWidget::keyPressEvent(QKeyEvent *event)
break;
case Qt::Key_Up: // Move note up
if (m_selectedNote) {
- m_selectedNote->move(m_selectedNote->x(), m_selectedNote->y() - noteYStep);
+ m_selectedNote->move(m_selectedNote->x(), m_pitch.note2px(m_pitch.px2note(m_selectedNote->y()) + 1));
}
break;
case Qt::Key_Down: // Move note down
if (m_selectedNote) {
- m_selectedNote->move(m_selectedNote->x(), m_selectedNote->y() + noteYStep);
+ m_selectedNote->move(m_selectedNote->x(), m_pitch.note2px(m_pitch.px2note(m_selectedNote->y()) - 1));
}
break;
case Qt::Key_Delete: // Delete selected note
@@ -417,25 +408,10 @@ void NoteGraphWidget::doOperation(const Operation& op, Operation::OperationFlags
}
-int NoteGraphWidget::s2px(double sec) const {
- return sec * m_pixPerSec;
-}
-double NoteGraphWidget::px2s(int px) const {
- return px / m_pixPerSec;
-}
-int NoteGraphWidget::ndiff2px(int dn) const {
- return dn * noteYStep;
-}
-int NoteGraphWidget::n2px(int note) const {
- int highestNote = m_lowestNote + 12 * m_octaves;
- return (highestNote - note) * noteYStep;
-}
-int NoteGraphWidget::px2n(int px) const {
- int highestNote = m_lowestNote + 12 * m_octaves;
- return highestNote - round(px / (float)noteYStep);
-}
-
-
+int NoteGraphWidget::s2px(double sec) const { return m_pitch.time2px(sec); }
+double NoteGraphWidget::px2s(int px) const { return m_pitch.px2time(px); }
+int NoteGraphWidget::n2px(int note) const { return m_pitch.note2px(note); }
+int NoteGraphWidget::px2n(int px) const { return m_pitch.px2note(px); }
void FloatingGap::addNote(NoteLabel* n)
{
diff --git a/notegraphwidget.hh b/notegraphwidget.hh
index 4d40379..7d1deea 100644
--- a/notegraphwidget.hh
+++ b/notegraphwidget.hh
@@ -33,10 +33,9 @@ public:
int s2px(double sec) const;
double px2s(int px) const;
- int ndiff2px(int dn) const;
int n2px(int note) const;
int px2n(int px) const;
- int h() const { return 12 * m_octaves * noteYStep; }
+ int h() const { return m_pitch.height; }
signals:
@@ -54,10 +53,6 @@ protected:
private:
void finalizeNewLyrics();
- double m_pixPerSec; ///< Pixels per second
- int m_lowestNote; ///< Note id / midi pitch of the lowest note in the view
- int m_octaves; ///< How many octaves are displayed in the view
-
int m_requiredWidth;
QPoint m_panHotSpot;
NoteLabel* m_selectedNote;
diff --git a/pitchvis.cc b/pitchvis.cc
index 271e50d..be9ab74 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -1,6 +1,5 @@
#include "pitchvis.hh"
-#include "notes.hh"
#include "pitch.hh"
#include <fstream>
#include <iostream>
@@ -21,14 +20,12 @@ template <typename T> void readVec(std::string const& filename, std::vector<T>&
if (!f) throw std::runtime_error("Error reading " + filename);
}
-PitchVis::PitchVis(std::string const& filename): height(1024) {
+PitchVis::PitchVis(std::string const& filename): step(1024), height(768) {
std::vector<float> data;
try { readVec(filename, data); } catch(std::exception& e) { std::cerr << e.what() << std::endl; return; }
- unsigned step = 1024;
unsigned width = data.size() / step;
img.resize(width * height);
Analyzer analyzer(44100, "");
- MusicalScale scale;
QProgressDialog progress(tr("Analyzing pitch data..."), tr("&Abort"), 0, width, this);
progress.setWindowModality(Qt::WindowModal);
@@ -40,7 +37,7 @@ PitchVis::PitchVis(std::string const& filename): height(1024) {
analyzer.process();
Analyzer::Peaks peaks = analyzer.getPeaks();
for (unsigned i = 0; i < peaks.size(); ++i) {
- unsigned y = height - static_cast<unsigned>(16.0 * scale.getNote(peaks[i].freq));
+ unsigned y = freq2px(peaks[i].freq);
if (y == 0 || y >= height - 1) continue;
float value = 0.003 * (level2dB(peaks[i].level) + 80.0);
if (value <= 0.0) continue;
@@ -56,7 +53,7 @@ PitchVis::PitchVis(std::string const& filename): height(1024) {
unsigned int i = 0;
for (Analyzer::Tones::const_iterator it = tones.begin(), itend = tones.end(); it != itend && i < 3; ++it) {
if (it->age < Tone::MINAGE) continue;
- unsigned y = height - static_cast<unsigned>(16.0 * scale.getNote(it->freqSlow));
+ unsigned y = freq2px(it->freqSlow);
if (y == 0 || y >= height - 1) continue;
float value = 0.003 * (level2dB(it->levelSlow) + 80.0);
if (value <= 0.0) continue;
@@ -83,3 +80,9 @@ PitchVis::PitchVis(std::string const& filename): height(1024) {
progress.setValue(width);
}
+unsigned PitchVis::freq2px(double freq) const { return note2px(scale.getNote(freq)); }
+unsigned PitchVis::note2px(double note) const { return height - static_cast<unsigned>(16.0 * note); }
+double PitchVis::px2note(unsigned px) const { return (height - px) / 16.0; }
+unsigned PitchVis::time2px(double t) const { return t * 44100.0 / step; }
+double PitchVis::px2time(double px) const { return px / 44100.0 * step; }
+
diff --git a/pitchvis.hh b/pitchvis.hh
index a85a2ff..1275a1a 100644
--- a/pitchvis.hh
+++ b/pitchvis.hh
@@ -1,5 +1,6 @@
#pragma once
+#include "notes.hh"
#include "util.hh"
#include <QWidget>
#include <cmath>
@@ -31,11 +32,18 @@ struct Pixel {
};
class PitchVis: public QWidget {
+ unsigned step;
std::vector<Pixel> img;
+ MusicalScale scale;
public:
const std::size_t height;
Pixel& operator()(std::size_t x, std::size_t y) { return img[x * height + y]; }
std::size_t width() const { return img.size() / height; }
PitchVis(std::string const& filename);
+ unsigned freq2px(double freq) const;
+ unsigned note2px(double note) const;
+ double px2note(unsigned px) const;
+ unsigned time2px(double t) const;
+ double px2time(double px) const;
};
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-18 14:41:57
|
Module: editor
Branch: master
Commit: df0744aecaec8697594d084b9d0d2835adf8c7b7
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Jan 18 15:29:13 2011 +0100
Make pitchvis use the new pitch.* code.
---
pitchvis.cc | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/pitchvis.cc b/pitchvis.cc
index e4b29f9..271e50d 100644
--- a/pitchvis.cc
+++ b/pitchvis.cc
@@ -42,7 +42,7 @@ PitchVis::PitchVis(std::string const& filename): height(1024) {
for (unsigned i = 0; i < peaks.size(); ++i) {
unsigned y = height - static_cast<unsigned>(16.0 * scale.getNote(peaks[i].freq));
if (y == 0 || y >= height - 1) continue;
- float value = 0.003 * (magn2dB(peaks[i].magnitude) + 80.0);
+ float value = 0.003 * (level2dB(peaks[i].level) + 80.0);
if (value <= 0.0) continue;
Pixel p(0.0f, 0.0f, value);
(*this)(x, y) += p;
@@ -56,9 +56,9 @@ PitchVis::PitchVis(std::string const& filename): height(1024) {
unsigned int i = 0;
for (Analyzer::Tones::const_iterator it = tones.begin(), itend = tones.end(); it != itend && i < 3; ++it) {
if (it->age < Tone::MINAGE) continue;
- unsigned y = height - static_cast<unsigned>(16.0 * scale.getNote(it->freq));
+ unsigned y = height - static_cast<unsigned>(16.0 * scale.getNote(it->freqSlow));
if (y == 0 || y >= height - 1) continue;
- float value = 0.003 * (it->db + 80.0);
+ float value = 0.003 * (level2dB(it->levelSlow) + 80.0);
if (value <= 0.0) continue;
Pixel p(value, value, value);
switch(i) {
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-18 14:41:55
|
Module: editor
Branch: master
Commit: e520fd815ea1326b1ce353832f4bcfd00d67496d
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Jan 18 15:28:48 2011 +0100
Use linear levels everywhere instead of dB, simplify terminology, track slow (time-averaged), instaneous freq/level separately, minor changes to the operation of various parts due to the mentioned changes.
---
pitch.cc | 68 ++++++++++++++++++++++++++++++-------------------------------
pitch.hh | 15 +++++++------
2 files changed, 41 insertions(+), 42 deletions(-)
diff --git a/pitch.cc b/pitch.cc
index b3da8a1..566ba72 100644
--- a/pitch.cc
+++ b/pitch.cc
@@ -15,18 +15,18 @@ static const double FFT_MINFREQ = 45.0;
static const double FFT_MAXFREQ = 3000.0;
Tone::Tone():
- freq(0.0),
- db(-getInf()),
- stabledb(-getInf()),
+ freq(),
+ freqSlow(),
+ level(),
+ levelSlow(),
age()
{
- for (std::size_t i = 0; i < MAXHARM; ++i)
- harmonics[i] = 0.0;
+ for (std::size_t i = 0; i < MAXHARM; ++i) harmonics[i] = 0.0;
}
void Tone::print() const {
if (age < Tone::MINAGE) return;
- std::cout << std::fixed << std::setprecision(1) << freq << " Hz, age " << age << ", " << db << " dB:";
+ std::cout << std::fixed << std::setprecision(1) << freq << " Hz, age " << age << ", " << level2dB(level) << " dB:";
for (std::size_t i = 0; i < 8; ++i) std::cout << " " << harmonics[i];
std::cout << std::endl;
}
@@ -78,15 +78,15 @@ namespace {
}
struct Combo {
double freq;
- double magnitude;
- Combo(): freq(), magnitude() {}
+ double level;
+ Combo(): freq(), level() {}
void combine(Peak const& p) {
- freq += p.magnitude * p.freq; // Multiplication for weighted average
- magnitude += p.magnitude;
+ freq += p.level * p.freq; // Multiplication for weighted average
+ level += p.level;
}
bool match(double freqOther) const { return matchFreq(freq, freqOther); }
};
- bool operator<(Combo const& a, Combo const& b) { return a.magnitude < b.magnitude; }
+ bool operator<(Combo const& a, Combo const& b) { return a.level < b.level; }
}
void Analyzer::calcTones() {
@@ -98,10 +98,10 @@ void Analyzer::calcTones() {
const size_t kMin = std::max(size_t(3), size_t(FFT_MINFREQ / freqPerBin));
const size_t kMax = std::min(FFT_N / 2, size_t(FFT_MAXFREQ / freqPerBin));
m_peaks.resize(kMax);
- std::vector<double> magnitudes;
+ std::vector<double> levels;
// Process FFT into peaks
for (size_t k = 1; k < kMax; ++k) {
- double magnitude = normCoeff * std::abs(m_fft[k]);
+ double level = normCoeff * std::abs(m_fft[k]);
double phase = std::arg(m_fft[k]);
// Use the reassignment method for calculating precise frequencies
double delta = phase - m_fftLastPhase[k];
@@ -111,25 +111,25 @@ void Analyzer::calcTones() {
delta /= phaseStep; // Calculate how much difference that makes during a step
m_peaks[k].freqFFT = k * freqPerBin; // Calculate the simple FFT frequency
m_peaks[k].freq = (k + delta) * freqPerBin; // Calculate the true frequency
- m_peaks[k].magnitude = magnitude;
- magnitudes.push_back(magnitude);
+ m_peaks[k].level = level;
+ levels.push_back(level);
}
- std::sort(magnitudes.begin(), magnitudes.end());
- double m80 = magnitudes[magnitudes.size() * 80 / 100]; // m80 > 80 % peaks
- double mmax = magnitudes.back(); // Max magnitude of peaks (note: combos will combine peaks and go higher)
+ std::sort(levels.begin(), levels.end());
+ double l80 = levels[levels.size() * 80 / 100]; // l80 > 80 % peaks
+ double lmax = levels.back(); // Max level of peaks (note: combos will combine peaks and go higher)
// Filter peaks and combine adjacent peaks pointing at the same frequency into one
typedef std::vector<Combo> Combos;
Combos combos;
for (size_t k = kMin; k < kMax; ++k) {
Peak const& p = m_peaks[k];
- if (p.magnitude < m80) continue;
+ if (p.level < l80) continue;
if (p.freq < FFT_MINFREQ || p.freq > FFT_MAXFREQ) continue;
// Do we need to add a new Combo (rather than using the last one)?
if (combos.empty() || !combos.back().match(p.freq)) combos.push_back(Combo());
combos.back().combine(p);
}
// Convert sum frequencies into averages
- for (Combos::iterator it = combos.begin(), end = combos.end(); it != end; ++it) it->freq /= it->magnitude;
+ for (Combos::iterator it = combos.begin(), end = combos.end(); it != end; ++it) it->freq /= it->level;
// Strongest first
std::sort(combos.rbegin(), combos.rend());
// Keep only a reasonable amount of strongest frequencies.
@@ -137,7 +137,7 @@ void Analyzer::calcTones() {
// Try to combine combos into tones (collections of harmonics)
Tones tones;
for (Combos::const_iterator it = combos.begin(), end = combos.end(); it != end; ++it) {
- if (it->magnitude < 0.1 * mmax) break;
+ if (it->level < 0.1 * lmax) break;
Tone bestTone;
int bestScore = 0;
for (std::size_t div = 1; div <= Tone::MAXHARM; ++div) {
@@ -146,7 +146,7 @@ void Analyzer::calcTones() {
double freq = it->freq / div; // Assumed fundamental frequency
int score = 0;
size_t misses = 0;
- t.db = 0.0; // Using db field for magnitude
+ t.level = 0.0;
for (std::size_t n = 1; n <= Tone::MAXHARM && misses < 3; ++n) {
double hfreq = n * freq;
bool miss = true;
@@ -154,22 +154,20 @@ void Analyzer::calcTones() {
if (!harm->match(hfreq)) continue; // Skip current combo if it doesn't match
// Possible matching harmonic found, update t and scoring
score += (n == 1 ? 3 : 2); // Score for finding a harmonic (extra for fundamental)
- double m = harm->magnitude;
- t.harmonics[n - 1] += m;
- t.db += m; // Magnitudes can be accumulated (decibels cannot)
- t.freq += m * harm->freq / n; // The sum of all harmonics' fundies (weighted by m)
+ double l = harm->level;
+ t.harmonics[n - 1] += l;
+ t.level += l;
+ t.freq += l * harm->freq / n; // The sum of all harmonics' fundies (weighted by m)
}
if (miss) ++misses;
}
- t.freq /= t.db; // Average instead of sum
- t.stabledb = t.db = magn2dB(t.db); // Convert into actual dB
+ t.freqSlow = t.freq /= t.level; // Average instead of sum
if (score > bestScore) {
bestScore = score;
bestTone = t;
}
}
tones.push_back(bestTone);
-// std::cerr << "[" << it->freq << " Hz -> best: " << bestTone.freq << " Hz, " << bestScore << " points, " << bestTone.db << " dB]";
}
// Clean harmonics misdetected as fundamental
tones.sort();
@@ -196,13 +194,13 @@ void Analyzer::mergeWithOld(Tones& tones) const {
if (it != tones.end() && *it == *oldit) {
// Merge the old tone into the new tone
it->age = oldit->age + 1;
- it->stabledb = 0.8 * oldit->stabledb + 0.2 * it->db;
- it->freq = 0.5 * oldit->freq + 0.5 * it->freq;
- } else if (oldit->db > -80.0) {
+ it->levelSlow = 0.8 * oldit->levelSlow + 0.2 * it->level;
+ it->freqSlow = 0.8 * oldit->freqSlow + 0.2 * it->freq;
+ } else if (oldit->levelSlow > 1e-8) {
// Insert a decayed version of the old tone into new tones
Tone& t = *tones.insert(it, *oldit);
- t.db -= 5.0;
- t.stabledb -= 0.5;
+ t.level = 0.0;
+ t.levelSlow *= 0.8;
}
}
}
@@ -217,7 +215,7 @@ Tone const* Analyzer::findTone(double minfreq, double maxfreq) const {
double bestscore = -getInf();
for (Tones::const_iterator it = m_tones.begin(); it != m_tones.end(); ++it) {
if (it->freq > maxfreq || it->freq < minfreq || it->age < Tone::MINAGE) continue;
- double score = it->stabledb - std::max(180.0, std::abs(it->freq - 300.0)) / 10.0;
+ double score = it->levelSlow * (400.0 - std::max(180.0, std::abs(it->freq - 300.0)));
if (m_oldfreq != 0.0 && std::abs(it->freq/m_oldfreq - 1.0) < 0.10) score += 30.0; // Stability is a really good thing
if (it->harmonics[0] > 0.0) score += 10.0; // fundamental is always good to have
if (it->harmonics[2] > 0.0) score += 10.0; // third harmonic is nearly always detected for vocals
diff --git a/pitch.hh b/pitch.hh
index 642d8c6..6061bfc 100644
--- a/pitch.hh
+++ b/pitch.hh
@@ -7,31 +7,32 @@
#include <algorithm>
#include <cmath>
-static inline double magn2dB(double magnitude) { return 20.0 * std::log10(magnitude); }
-static inline double dB2magn(double db) { return std::pow(10.0, db / 20.0); }
+static inline double level2dB(double level) { return 20.0 * std::log10(level); }
+static inline double dB2level(double db) { return std::pow(10.0, db / 20.0); }
/// A tone is a collection of a base frequency (freq) and all its harmonics
struct Tone {
static const std::size_t MAXHARM = 16; ///< The maximum number of harmonics tracked
static const std::size_t MINAGE = 6; ///< The minimum age required for a tone to be output
double freq; ///< Frequency (Hz)
- double db; ///< Level (dB)
- double stabledb; ///< Stable level, useful for graphics rendering
+ double freqSlow; ///< Stable frequency
+ double level; ///< Level (linear)
+ double levelSlow; ///< Stable level, useful for graphics rendering
double harmonics[MAXHARM]; ///< Harmonics' levels
std::size_t age; ///< How many times the tone has been detected in row
Tone();
void print() const; ///< Prints Tone to std::cout
bool operator==(double f) const; ///< Compare for rough frequency match
/// Less-than compare by levels (instead of frequencies like operator< does)
- static bool dbCompare(Tone const& l, Tone const& r) { return l.stabledb < r.stabledb; }
+ static bool levelCompare(Tone const& l, Tone const& r) { return l.levelSlow < r.levelSlow; }
};
/// A peak contains information about a single frequency
struct Peak {
double freqFFT;
double freq;
- double magnitude;
- Peak(): freqFFT(), freq(), magnitude() {}
+ double level;
+ Peak(): freqFFT(), freq(), level() {}
};
static inline bool operator==(Tone const& lhs, Tone const& rhs) { return lhs == rhs.freq; }
|
|
From: Lasse Kärkkäi. <tr...@us...> - 2011-01-18 13:01:26
|
Module: editor
Branch: master
Commit: 10799b6fdb4de826adac5fb6ba96318099f0df07
Author: Lasse Karkkainen <tro...@tr...>
Date: Tue Jan 18 13:59:45 2011 +0100
Modify Analyzer::input so that it accepts int16 samples too.
---
pitch.hh | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/pitch.hh b/pitch.hh
index cff8cf3..642d8c6 100644
--- a/pitch.hh
+++ b/pitch.hh
@@ -60,6 +60,7 @@ public:
bool overflow = false;
while (begin != end) {
float s = *begin++; // Read input sample
+ if (sizeof(*begin) == 2) s /= 32767.0; // Integer samples (normalize)
// Peak level calculation
float p = s * s;
if (p > m_level) m_level = p; else m_level *= 0.9995;
|
|
From: Tapio V. <aa...@us...> - 2011-01-18 12:37:01
|
Module: editor
Branch: master
Commit: 49ddf67539a46909f9bee315a2f491efd6882ee6
Author: Tapio Vierros <tap...@gm...>
Date: Tue Jan 18 13:18:27 2011 +0200
Further XML export timing tweaks - still doesn't look right.
---
songwriter-xml.cc | 4 ++--
songwriter.hh | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/songwriter-xml.cc b/songwriter-xml.cc
index 0fddb1d..557ceee 100644
--- a/songwriter-xml.cc
+++ b/songwriter-xml.cc
@@ -13,7 +13,7 @@ void SingStarXMLWriter::writeXML() {
root.setAttribute("Version", "1");
root.setAttribute("Tempo", QString::number(tempo));
root.setAttribute("FixedTempo", "Yes");
- root.setAttribute("Resolution", "Semiquaver"); // Demisemiquaver = 2x tempo
+ root.setAttribute("Resolution", "Demisemiquaver"); // Demisemiquaver = 2x tempo of Semiquaver
root.setAttribute("Genre", QString::fromStdString(s.genre));
root.setAttribute("Year", QString::fromStdString(s.year));
root.setAttribute("xsi:schemaLocation", "http://www.singstargame.com http://15GMS-SINGSQL/xml_schema/melody.xsd");
@@ -90,5 +90,5 @@ void SingStarXMLWriter::writeXML() {
}
int SingStarXMLWriter::sec2dur(double sec) {
- return round(tempo / 60.0 * sec * 4);
+ return round(tempo / 60.0 * sec * 8); // 8 for Demisemiquaver
}
diff --git a/songwriter.hh b/songwriter.hh
index a8e11db..13016fb 100644
--- a/songwriter.hh
+++ b/songwriter.hh
@@ -13,7 +13,7 @@ struct SongWriter
struct SingStarXMLWriter: public SongWriter
{
SingStarXMLWriter(const Song& s_, const QString& path_)
- : SongWriter(s_, path_), tempo(240) { writeXML(); }
+ : SongWriter(s_, path_), tempo(160) { writeXML(); }
private:
void writeXML();
int sec2dur(double sec);
|