Merge lp:~max-linke/mixxx/chromaprint into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Max Linke
Status: Merged
Merged at revision: 3397
Proposed branch: lp:~max-linke/mixxx/chromaprint
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 2462 lines (+2023/-87)
24 files modified
mixxx/build/depends.py (+23/-1)
mixxx/build/qtcreator/mixxx.pro (+1/-0)
mixxx/src/dlgtagfetcher.cpp (+159/-0)
mixxx/src/dlgtagfetcher.h (+57/-0)
mixxx/src/dlgtagfetcher.ui (+441/-0)
mixxx/src/dlgtrackinfo.cpp (+22/-9)
mixxx/src/dlgtrackinfo.h (+5/-2)
mixxx/src/dlgtrackinfo.ui (+78/-71)
mixxx/src/musicbrainz/acoustidclient.cpp (+109/-0)
mixxx/src/musicbrainz/acoustidclient.h (+67/-0)
mixxx/src/musicbrainz/chromaprinter.cpp (+88/-0)
mixxx/src/musicbrainz/chromaprinter.h (+25/-0)
mixxx/src/musicbrainz/crc.c (+133/-0)
mixxx/src/musicbrainz/crc.h (+83/-0)
mixxx/src/musicbrainz/gzip.cpp (+49/-0)
mixxx/src/musicbrainz/gzip.h (+12/-0)
mixxx/src/musicbrainz/musicbrainzclient.cpp (+180/-0)
mixxx/src/musicbrainz/musicbrainzclient.h (+130/-0)
mixxx/src/musicbrainz/tagfetcher.cpp (+121/-0)
mixxx/src/musicbrainz/tagfetcher.h (+58/-0)
mixxx/src/network.cpp (+73/-0)
mixxx/src/network.h (+49/-0)
mixxx/src/widget/wtracktableview.cpp (+53/-4)
mixxx/src/widget/wtracktableview.h (+7/-0)
To merge this branch: bzr merge lp:~max-linke/mixxx/chromaprint
Reviewer Review Type Date Requested Status
RJ Skerry-Ryan Approve
Review via email: mp+163406@code.launchpad.net

Description of the change

Hi

I decided to split up my library_features branch in smaller branches. In here are all changes relevant to fingerprinting and retreiving tags from MusicBrainz

To post a comment you must log in.
lp:~max-linke/mixxx/chromaprint updated
3375. By Max Linke <email address hidden>

merge with trunk

3376. By Max Linke <email address hidden>

merge with trunk

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Looks good to me on my first skim. I need to take a look src/chromaprint more closely but for now I'm assuming it works as expected.

* void WTrackTableView::showDlgTagFetcher(QModelIndex index)

What about when multiple tracks are selected?

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Doesn't MB have more data than artist/title/album/track/year?

When you set them, what about checking whether the existing data is blank or the replacement data is blank and keeping the old one if the new one is blank?

Revision history for this message
Max Linke (max-linke) wrote :

'reload from MB' is grayed out if more then one song is selected. The tagfetcher class is able to handle to fetch tags from several songs at once but I haven't found a good way to display several tracks+results to the user and by this I follow the same logic as 'properties' does.

I didn't check to keep the old one if the new data is blank but I honestly also never encountered that with any track I own. I sure I can write some code that checks for this but I don't know how I'm supposed to test if it works.

lp:~max-linke/mixxx/chromaprint updated
3377. By Max Linke <email address hidden>

removed musicbrainz url that is not used

3378. By Max Linke <email address hidden>

merge with trunk

3379. By Max Linke <email address hidden>

fixed signal connection

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Looks good -- thanks for the feature Max!

review: Approve
lp:~max-linke/mixxx/chromaprint updated
3380. By Max Linke <email address hidden>

trackpointer->year/tracknumber can also be 0 check for that as well

3381. By Max Linke <email address hidden>

merge with trunk

3382. By Max Linke <email address hidden>

merge with trunk and small changes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'mixxx/build/depends.py'
--- mixxx/build/depends.py 2013-05-21 03:02:42 +0000
+++ mixxx/build/depends.py 2013-05-22 07:42:27 +0000
@@ -400,6 +400,17 @@
400 if build.platform_is_windows and build.static_dependencies:400 if build.platform_is_windows and build.static_dependencies:
401 build.env.Append(CPPDEFINES = 'TAGLIB_STATIC')401 build.env.Append(CPPDEFINES = 'TAGLIB_STATIC')
402402
403class Chromaprint(Dependence):
404 def configure(self, build, conf):
405 if not conf.CheckLib(['chromaprint', 'libchromaprint', 'chromaprint_p', 'libchromaprint_p']):
406 raise Exception("Could not find libchromaprint or its development headers.")
407 if build.platform_is_windows and build.static_dependencies:
408 build.env.Append(CPPDEFINES = 'CHROMAPRINT_NODLL')
409
410 # On Windows, we link chromaprint with FFTW3.
411 if not conf.CheckLib(['fftw', 'libfftw', 'fftw3', 'libfftw3']):
412 raise Exception("Could not find fftw3 or its development headers.")
413
403class ProtoBuf(Dependence):414class ProtoBuf(Dependence):
404 def configure(self, build, conf):415 def configure(self, build, conf):
405 libs = ['libprotobuf-lite', 'protobuf-lite', 'libprotobuf', 'protobuf']416 libs = ['libprotobuf-lite', 'protobuf-lite', 'libprotobuf', 'protobuf']
@@ -447,6 +458,7 @@
447 "dlgabout.cpp",458 "dlgabout.cpp",
448 "dlgprefeq.cpp",459 "dlgprefeq.cpp",
449 "dlgprefcrossfader.cpp",460 "dlgprefcrossfader.cpp",
461 "dlgtagfetcher.cpp",
450 "dlgtrackinfo.cpp",462 "dlgtrackinfo.cpp",
451 "dlgprepare.cpp",463 "dlgprepare.cpp",
452 "dlgautodj.cpp",464 "dlgautodj.cpp",
@@ -545,6 +557,14 @@
545557
546 "mathstuff.cpp",558 "mathstuff.cpp",
547559
560 "network.cpp",
561 "musicbrainz/tagfetcher.cpp",
562 "musicbrainz/gzip.cpp",
563 "musicbrainz/crc.c",
564 "musicbrainz/acoustidclient.cpp",
565 "musicbrainz/chromaprinter.cpp",
566 "musicbrainz/musicbrainzclient.cpp",
567
548 "rotary.cpp",568 "rotary.cpp",
549 "widget/wtracktableview.cpp",569 "widget/wtracktableview.cpp",
550 "widget/wtracktableviewheader.cpp",570 "widget/wtracktableviewheader.cpp",
@@ -756,6 +776,7 @@
756 build.env.Uic4('dlgprefnovinyldlg.ui')776 build.env.Uic4('dlgprefnovinyldlg.ui')
757 build.env.Uic4('dlgprefrecorddlg.ui')777 build.env.Uic4('dlgprefrecorddlg.ui')
758 build.env.Uic4('dlgaboutdlg.ui')778 build.env.Uic4('dlgaboutdlg.ui')
779 build.env.Uic4('dlgtagfetcher.ui')
759 build.env.Uic4('dlgtrackinfo.ui')780 build.env.Uic4('dlgtrackinfo.ui')
760 build.env.Uic4('dlgprepare.ui')781 build.env.Uic4('dlgprepare.ui')
761 build.env.Uic4('dlgautodj.ui')782 build.env.Uic4('dlgautodj.ui')
@@ -910,7 +931,8 @@
910931
911 def depends(self, build):932 def depends(self, build):
912 return [SoundTouch, ReplayGain, PortAudio, PortMIDI, Qt,933 return [SoundTouch, ReplayGain, PortAudio, PortMIDI, Qt,
913 FidLib, SndFile, FLAC, OggVorbis, OpenGL, TagLib, ProtoBuf]934 FidLib, SndFile, FLAC, OggVorbis, OpenGL, TagLib, ProtoBuf,
935 Chromaprint]
914936
915 def post_dependency_check_configure(self, build, conf):937 def post_dependency_check_configure(self, build, conf):
916 """Sets up additional things in the Environment that must happen938 """Sets up additional things in the Environment that must happen
917939
=== modified file 'mixxx/build/qtcreator/mixxx.pro'
--- mixxx/build/qtcreator/mixxx.pro 2013-05-20 02:18:33 +0000
+++ mixxx/build/qtcreator/mixxx.pro 2013-05-22 07:42:27 +0000
@@ -105,6 +105,7 @@
105 $$UI_DIR/ui_dlgpreferencesdlg.h \105 $$UI_DIR/ui_dlgpreferencesdlg.h \
106 $$UI_DIR/ui_dlgprefmidibindingsdlg.h \106 $$UI_DIR/ui_dlgprefmidibindingsdlg.h \
107 $$UI_DIR/ui_dlgprefplaylistdlg.h \107 $$UI_DIR/ui_dlgprefplaylistdlg.h \
108 $$UI_DIR/ui_dlgtagfetcher.h \
108 $$UI_DIR/ui_dlgprefrecorddlg.h \109 $$UI_DIR/ui_dlgprefrecorddlg.h \
109 $$UI_DIR/ui_dlgprefsounddlg.h \110 $$UI_DIR/ui_dlgprefsounddlg.h \
110 $$UI_DIR/ui_dlgprefvinyldlg.h \111 $$UI_DIR/ui_dlgprefvinyldlg.h \
111112
=== added file 'mixxx/src/dlgtagfetcher.cpp'
--- mixxx/src/dlgtagfetcher.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/dlgtagfetcher.cpp 2013-05-22 07:42:27 +0000
@@ -0,0 +1,159 @@
1#include <QTreeWidget>
2#include <QDebug>
3#include <QDesktopServices>
4#include <QUrl>
5
6#include "dlgtagfetcher.h"
7
8DlgTagFetcher::DlgTagFetcher(QWidget *parent)
9 : QWidget(parent),
10 m_track(NULL),
11 m_TagFetcher(parent) {
12 setupUi(this);
13
14 connect(btnApply, SIGNAL(clicked()),
15 this, SLOT(apply()));
16 connect(btnQuit, SIGNAL(clicked()),
17 this, SLOT(quit()));
18 connect(btnPrev, SIGNAL(clicked()),
19 this, SIGNAL(previous()));
20 connect(btnNext, SIGNAL(clicked()),
21 this, SIGNAL(next()));
22 connect(results, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
23 this, SLOT(resultSelected()));
24
25 connect(&m_TagFetcher, SIGNAL(resultAvailable(const TrackPointer,const QList<TrackPointer>&)),
26 this, SLOT(fetchTagFinished(const TrackPointer,const QList<TrackPointer>&)));
27 connect(&m_TagFetcher, SIGNAL(fetchProgress(QString)),
28 this, SLOT(fetchTagProgress(QString)));
29
30 // Resize columns, this can't be set in the ui file
31 results->setColumnWidth(0, 50); // Track column
32 results->setColumnWidth(1, 50); // Year column
33 results->setColumnWidth(2, 160); // Title column
34 results->setColumnWidth(3, 160); // Artist column
35 results->setColumnWidth(4, 160); // Album column
36
37 progressLabel->setText("");
38}
39
40DlgTagFetcher::~DlgTagFetcher() {
41}
42
43void DlgTagFetcher::init(const TrackPointer track) {
44 results->clear();
45 m_track = track;
46 m_data = Data();
47 m_TagFetcher.startFetch(m_track);
48 updateStack();
49}
50
51void DlgTagFetcher::apply() {
52 int resultIndex = m_data.m_selectedResult;
53 if (resultIndex > -1) {
54 if (!m_data.m_results[resultIndex]->getAlbum().isEmpty()) {
55 m_track->setAlbum(m_data.m_results[resultIndex]->getAlbum());
56 }
57 if (!m_data.m_results[resultIndex]->getArtist().isEmpty()) {
58 m_track->setArtist(m_data.m_results[resultIndex]->getArtist());
59 }
60 if (!m_data.m_results[resultIndex]->getTitle().isEmpty()) {
61 m_track->setTitle(m_data.m_results[resultIndex]->getTitle());
62 }
63 if (!m_data.m_results[resultIndex]->getYear().isEmpty() &&
64 m_data.m_results[resultIndex]->getYear() != "0") {
65 m_track->setYear(m_data.m_results[resultIndex]->getYear());
66 }
67 if (!m_data.m_results[resultIndex]->getTrackNumber().isEmpty() &&
68 m_data.m_results[resultIndex]->getTrackNumber() != "0") {
69 m_track->setTrackNumber(m_data.m_results[resultIndex]->getTrackNumber());
70 }
71 }
72}
73
74void DlgTagFetcher::quit() {
75 m_TagFetcher.cancel();
76 close();
77}
78
79void DlgTagFetcher::fetchTagProgress(QString text) {
80 loading->setText(text);
81}
82
83void DlgTagFetcher::fetchTagFinished(const TrackPointer track,
84 const QList<TrackPointer>& tracks) {
85 // check if the answer is for this track
86 if (m_track->getLocation() != track->getLocation()) {
87 return;
88 }
89
90 m_data.m_pending = false;
91 m_data.m_results = tracks;
92 // qDebug() << "number of results = " << tracks.size();
93 updateStack();
94}
95
96void DlgTagFetcher::updateStack() {
97 if (m_data.m_pending) {
98 stack->setCurrentWidget(loading_page);
99 return;
100 } else if (m_data.m_results.isEmpty()) {
101 stack->setCurrentWidget(error_page);
102 return;
103 }
104 btnApply->setEnabled(true);
105 stack->setCurrentWidget(results_page);
106
107 results->clear();
108
109 addDivider(tr("Original tags"), results);
110 addTrack(m_track, -1, results);
111
112 addDivider(tr("Suggested tags"), results);
113
114 int trackIndex = 0;
115 foreach (const TrackPointer track, m_data.m_results) {
116 addTrack(track, trackIndex++, results);
117 }
118
119 // Find the item that was selected last time
120 for (int i=0 ; i<results->model()->rowCount() ; ++i) {
121 const QModelIndex index = results->model()->index(i, 0);
122 const QVariant id = index.data(Qt::UserRole);
123 if (!id.isNull() && id.toInt() == m_data.m_selectedResult) {
124 results->setCurrentIndex(index);
125 break;
126 }
127 }
128}
129
130void DlgTagFetcher::addTrack(const TrackPointer track, int resultIndex,
131 QTreeWidget* parent) const {
132 QStringList values;
133 values << track->getTrackNumber() << track->getYear() << track->getTitle()
134 << track->getArtist() << track->getAlbum();
135
136 QTreeWidgetItem* item = new QTreeWidgetItem(parent, values);
137 item->setData(0, Qt::UserRole, resultIndex);
138 item->setData(0, Qt::TextAlignmentRole, Qt::AlignRight);
139}
140
141void DlgTagFetcher::addDivider(const QString& text, QTreeWidget* parent) const {
142 QTreeWidgetItem* item = new QTreeWidgetItem(parent);
143 item->setFirstColumnSpanned(true);
144 item->setText(0, text);
145 item->setFlags(Qt::NoItemFlags);
146 item->setForeground(0, palette().color(QPalette::Disabled, QPalette::Text));
147
148 QFont bold_font(font());
149 bold_font.setBold(true);
150 item->setFont(0, bold_font);
151}
152
153void DlgTagFetcher::resultSelected() {
154 if (!results->currentItem())
155 return;
156
157 const int resultIndex = results->currentItem()->data(0, Qt::UserRole).toInt();
158 m_data.m_selectedResult = resultIndex;
159}
0160
=== added file 'mixxx/src/dlgtagfetcher.h'
--- mixxx/src/dlgtagfetcher.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/dlgtagfetcher.h 2013-05-22 07:42:27 +0000
@@ -0,0 +1,57 @@
1#ifndef DLGTAGFETCHER_H
2#define DLGTAGFETCHER_H
3
4#include <QWidget>
5#include "ui_dlgtagfetcher.h"
6#include "trackinfoobject.h"
7#include "musicbrainz/tagfetcher.h"
8
9
10class QTreeWidget;
11
12
13class DlgTagFetcher : public QWidget, public Ui::DlgTagFetcher {
14 Q_OBJECT
15
16 public:
17 DlgTagFetcher(QWidget *parent);
18 virtual ~DlgTagFetcher();
19
20 void init(const TrackPointer track);
21
22 signals:
23 void next();
24 void previous();
25
26 public slots:
27 void fetchTagFinished(const TrackPointer,const QList<TrackPointer>& tracks);
28 void resultSelected();
29 void fetchTagProgress(QString);
30
31 private slots:
32 void apply();
33 void quit();
34
35 private:
36
37 void updateStack();
38 void addDivider(const QString& text, QTreeWidget* parent) const;
39 void addTrack(const TrackPointer track, int resultIndex,
40 QTreeWidget* parent) const;
41
42 private:
43 struct Data {
44 Data() : m_pending(true), m_selectedResult(0) {}
45
46 bool m_pending;
47 int m_selectedResult;
48 QList<TrackPointer> m_results;
49 };
50
51 TrackPointer m_track;
52 Data m_data;
53 QString m_progress;
54 TagFetcher m_TagFetcher;
55};
56
57#endif // DLGTAGFETCHER_H
058
=== added file 'mixxx/src/dlgtagfetcher.ui'
--- mixxx/src/dlgtagfetcher.ui 1970-01-01 00:00:00 +0000
+++ mixxx/src/dlgtagfetcher.ui 2013-05-22 07:42:27 +0000
@@ -0,0 +1,441 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>DlgTagFetcher</class>
4 <widget class="QWidget" name="DlgTagFetcher">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>810</width>
10 <height>393</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Dialog</string>
15 </property>
16 <widget class="QWidget" name="verticalLayoutWidget_3">
17 <property name="geometry">
18 <rect>
19 <x>10</x>
20 <y>10</y>
21 <width>791</width>
22 <height>371</height>
23 </rect>
24 </property>
25 <layout class="QVBoxLayout" name="verticalLayout_3">
26 <item>
27 <widget class="QStackedWidget" name="stack">
28 <property name="currentIndex">
29 <number>1</number>
30 </property>
31 <widget class="QWidget" name="results_page">
32 <widget class="QWidget" name="verticalLayoutWidget_2">
33 <property name="geometry">
34 <rect>
35 <x>0</x>
36 <y>0</y>
37 <width>791</width>
38 <height>331</height>
39 </rect>
40 </property>
41 <layout class="QVBoxLayout" name="verticalLayout_2">
42 <item>
43 <widget class="QLabel" name="label_3">
44 <property name="styleSheet">
45 <string notr="true">QLabel { font-weight: bold; }</string>
46 </property>
47 <property name="text">
48 <string>Select best possible match</string>
49 </property>
50 </widget>
51 </item>
52 <item>
53 <widget class="QTreeWidget" name="results">
54 <property name="editTriggers">
55 <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
56 </property>
57 <property name="rootIsDecorated">
58 <bool>false</bool>
59 </property>
60 <attribute name="headerDefaultSectionSize">
61 <number>150</number>
62 </attribute>
63 <attribute name="headerMinimumSectionSize">
64 <number>50</number>
65 </attribute>
66 <column>
67 <property name="text">
68 <string>Track</string>
69 </property>
70 </column>
71 <column>
72 <property name="text">
73 <string>Year</string>
74 </property>
75 </column>
76 <column>
77 <property name="text">
78 <string>Title</string>
79 </property>
80 </column>
81 <column>
82 <property name="text">
83 <string>Artist</string>
84 </property>
85 </column>
86 <column>
87 <property name="text">
88 <string>Album</string>
89 </property>
90 </column>
91 </widget>
92 </item>
93 </layout>
94 </widget>
95 </widget>
96 <widget class="QWidget" name="loading_page">
97 <widget class="QWidget" name="verticalLayoutWidget_4">
98 <property name="geometry">
99 <rect>
100 <x>-1</x>
101 <y>-1</y>
102 <width>791</width>
103 <height>341</height>
104 </rect>
105 </property>
106 <layout class="QVBoxLayout" name="verticalLayout_4">
107 <item>
108 <spacer name="verticalSpacer_3">
109 <property name="orientation">
110 <enum>Qt::Vertical</enum>
111 </property>
112 <property name="sizeHint" stdset="0">
113 <size>
114 <width>20</width>
115 <height>40</height>
116 </size>
117 </property>
118 </spacer>
119 </item>
120 <item>
121 <layout class="QHBoxLayout" name="horizontalLayout_3">
122 <item>
123 <spacer name="horizontalSpacer_4">
124 <property name="orientation">
125 <enum>Qt::Horizontal</enum>
126 </property>
127 <property name="sizeHint" stdset="0">
128 <size>
129 <width>40</width>
130 <height>20</height>
131 </size>
132 </property>
133 </spacer>
134 </item>
135 <item>
136 <widget class="QLabel" name="message1">
137 <property name="text">
138 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Fetching track data from MusicBrainz&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
139 </property>
140 </widget>
141 </item>
142 <item>
143 <spacer name="horizontalSpacer_5">
144 <property name="orientation">
145 <enum>Qt::Horizontal</enum>
146 </property>
147 <property name="sizeHint" stdset="0">
148 <size>
149 <width>40</width>
150 <height>20</height>
151 </size>
152 </property>
153 </spacer>
154 </item>
155 </layout>
156 </item>
157 <item>
158 <layout class="QHBoxLayout" name="horizontalLayout_4">
159 <item>
160 <spacer name="horizontalSpacer_6">
161 <property name="orientation">
162 <enum>Qt::Horizontal</enum>
163 </property>
164 <property name="sizeHint" stdset="0">
165 <size>
166 <width>40</width>
167 <height>20</height>
168 </size>
169 </property>
170 </spacer>
171 </item>
172 <item>
173 <widget class="QLabel" name="status">
174 <property name="text">
175 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Status:&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
176 </property>
177 </widget>
178 </item>
179 <item>
180 <widget class="QLabel" name="loading">
181 <property name="text">
182 <string/>
183 </property>
184 </widget>
185 </item>
186 <item>
187 <spacer name="horizontalSpacer_7">
188 <property name="orientation">
189 <enum>Qt::Horizontal</enum>
190 </property>
191 <property name="sizeHint" stdset="0">
192 <size>
193 <width>40</width>
194 <height>20</height>
195 </size>
196 </property>
197 </spacer>
198 </item>
199 </layout>
200 </item>
201 <item>
202 <spacer name="verticalSpacer_4">
203 <property name="orientation">
204 <enum>Qt::Vertical</enum>
205 </property>
206 <property name="sizeHint" stdset="0">
207 <size>
208 <width>20</width>
209 <height>40</height>
210 </size>
211 </property>
212 </spacer>
213 </item>
214 </layout>
215 </widget>
216 </widget>
217 <widget class="QWidget" name="error_page">
218 <widget class="QWidget" name="verticalLayoutWidget">
219 <property name="geometry">
220 <rect>
221 <x>0</x>
222 <y>0</y>
223 <width>791</width>
224 <height>331</height>
225 </rect>
226 </property>
227 <layout class="QVBoxLayout" name="verticalLayout">
228 <item>
229 <spacer name="verticalSpacer">
230 <property name="orientation">
231 <enum>Qt::Vertical</enum>
232 </property>
233 <property name="sizeHint" stdset="0">
234 <size>
235 <width>20</width>
236 <height>40</height>
237 </size>
238 </property>
239 </spacer>
240 </item>
241 <item>
242 <widget class="QLabel" name="label">
243 <property name="text">
244 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt; font-weight:600;&quot;&gt;Sorry&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
245 </property>
246 </widget>
247 </item>
248 <item>
249 <widget class="QLabel" name="label_2">
250 <property name="text">
251 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Mixxx could not find this song&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
252 </property>
253 </widget>
254 </item>
255 <item>
256 <spacer name="verticalSpacer_2">
257 <property name="orientation">
258 <enum>Qt::Vertical</enum>
259 </property>
260 <property name="sizeHint" stdset="0">
261 <size>
262 <width>20</width>
263 <height>40</height>
264 </size>
265 </property>
266 </spacer>
267 </item>
268 </layout>
269 </widget>
270 </widget>
271 <widget class="QWidget" name="submit_page">
272 <widget class="QWidget" name="verticalLayoutWidget_5">
273 <property name="geometry">
274 <rect>
275 <x>1</x>
276 <y>0</y>
277 <width>791</width>
278 <height>331</height>
279 </rect>
280 </property>
281 <layout class="QVBoxLayout" name="verticalLayout_5">
282 <item>
283 <layout class="QHBoxLayout" name="horizontalLayout_2">
284 <item>
285 <widget class="QLineEdit" name="apikey">
286 <property name="text">
287 <string/>
288 </property>
289 </widget>
290 </item>
291 <item>
292 <widget class="QPushButton" name="btnApikey">
293 <property name="text">
294 <string>get API-Key</string>
295 </property>
296 </widget>
297 </item>
298 <item>
299 <spacer name="horizontalSpacer_3">
300 <property name="orientation">
301 <enum>Qt::Horizontal</enum>
302 </property>
303 <property name="sizeHint" stdset="0">
304 <size>
305 <width>40</width>
306 <height>20</height>
307 </size>
308 </property>
309 </spacer>
310 </item>
311 <item>
312 <spacer name="horizontalSpacer_2">
313 <property name="orientation">
314 <enum>Qt::Horizontal</enum>
315 </property>
316 <property name="sizeHint" stdset="0">
317 <size>
318 <width>40</width>
319 <height>20</height>
320 </size>
321 </property>
322 </spacer>
323 </item>
324 <item>
325 <widget class="QPushButton" name="btnSubmit">
326 <property name="text">
327 <string>Submit</string>
328 </property>
329 </widget>
330 </item>
331 </layout>
332 </item>
333 <item>
334 <widget class="QTreeWidget" name="submit_tree">
335 <column>
336 <property name="text">
337 <string>Track</string>
338 </property>
339 </column>
340 <column>
341 <property name="text">
342 <string>Year</string>
343 </property>
344 </column>
345 <column>
346 <property name="text">
347 <string>New Column</string>
348 </property>
349 </column>
350 <column>
351 <property name="text">
352 <string>Artist</string>
353 </property>
354 </column>
355 <column>
356 <property name="text">
357 <string>Album</string>
358 </property>
359 </column>
360 <item>
361 <property name="text">
362 <string>New Item</string>
363 </property>
364 <property name="text">
365 <string/>
366 </property>
367 <property name="text">
368 <string/>
369 </property>
370 <property name="text">
371 <string/>
372 </property>
373 <property name="text">
374 <string/>
375 </property>
376 </item>
377 </widget>
378 </item>
379 <item>
380 <widget class="QLabel" name="progressLabel">
381 <property name="text">
382 <string>TextLabel</string>
383 </property>
384 </widget>
385 </item>
386 </layout>
387 </widget>
388 </widget>
389 </widget>
390 </item>
391 <item>
392 <layout class="QHBoxLayout" name="horizontalLayout">
393 <item>
394 <widget class="QPushButton" name="btnPrev">
395 <property name="text">
396 <string>Prev</string>
397 </property>
398 </widget>
399 </item>
400 <item>
401 <widget class="QPushButton" name="btnNext">
402 <property name="text">
403 <string>Next</string>
404 </property>
405 </widget>
406 </item>
407 <item>
408 <spacer name="horizontalSpacer">
409 <property name="orientation">
410 <enum>Qt::Horizontal</enum>
411 </property>
412 <property name="sizeHint" stdset="0">
413 <size>
414 <width>40</width>
415 <height>20</height>
416 </size>
417 </property>
418 </spacer>
419 </item>
420 <item>
421 <widget class="QPushButton" name="btnApply">
422 <property name="text">
423 <string>Apply</string>
424 </property>
425 </widget>
426 </item>
427 <item>
428 <widget class="QPushButton" name="btnQuit">
429 <property name="text">
430 <string>Close</string>
431 </property>
432 </widget>
433 </item>
434 </layout>
435 </item>
436 </layout>
437 </widget>
438 </widget>
439 <resources/>
440 <connections/>
441</ui>
0442
=== modified file 'mixxx/src/dlgtrackinfo.cpp'
--- mixxx/src/dlgtrackinfo.cpp 2012-05-08 16:10:02 +0000
+++ mixxx/src/dlgtrackinfo.cpp 2013-05-22 07:42:27 +0000
@@ -7,10 +7,20 @@
7#include "library/dao/cue.h"7#include "library/dao/cue.h"
8#include "trackinfoobject.h"8#include "trackinfoobject.h"
99
10DlgTrackInfo::DlgTrackInfo(QWidget* parent) :10DlgTrackInfo::DlgTrackInfo(QWidget* parent,
11 QDialog(parent),11 DlgTagFetcher& DlgTagFetcher)
12 m_pLoadedTrack() {12 : QDialog(parent),
1313 m_pLoadedTrack(NULL),
14 m_DlgTagFetcher(DlgTagFetcher) {
15 init();
16}
17
18DlgTrackInfo::~DlgTrackInfo() {
19 unloadTrack(false);
20 qDebug() << "~DlgTrackInfo()";
21}
22
23void DlgTrackInfo::init(){
14 setupUi(this);24 setupUi(this);
1525
16 cueTable->hideColumn(0);26 cueTable->hideColumn(0);
@@ -24,6 +34,9 @@
24 connect(btnCancel, SIGNAL(clicked()),34 connect(btnCancel, SIGNAL(clicked()),
25 this, SLOT(cancel()));35 this, SLOT(cancel()));
2636
37 connect(btnFetchTag, SIGNAL(clicked()),
38 this, SLOT(fetchTag()));
39
27 connect(bpmDouble, SIGNAL(clicked()),40 connect(bpmDouble, SIGNAL(clicked()),
28 this, SLOT(slotBpmDouble()));41 this, SLOT(slotBpmDouble()));
29 connect(bpmHalve, SIGNAL(clicked()),42 connect(bpmHalve, SIGNAL(clicked()),
@@ -43,11 +56,6 @@
43 }56 }
44}57}
4558
46DlgTrackInfo::~DlgTrackInfo() {
47 unloadTrack(false);
48 qDebug() << "~DlgTrackInfo()";
49}
50
51void DlgTrackInfo::apply() {59void DlgTrackInfo::apply() {
52 unloadTrack(true);60 unloadTrack(true);
53 accept();61 accept();
@@ -320,3 +328,8 @@
320 populateFields(pTrack);328 populateFields(pTrack);
321 }329 }
322}330}
331
332void DlgTrackInfo::fetchTag() {
333 m_DlgTagFetcher.init(m_pLoadedTrack);
334 m_DlgTagFetcher.show();
335}
323336
=== modified file 'mixxx/src/dlgtrackinfo.h'
--- mixxx/src/dlgtrackinfo.h 2013-05-14 18:09:35 +0000
+++ mixxx/src/dlgtrackinfo.h 2013-05-22 07:42:27 +0000
@@ -8,8 +8,8 @@
8#include <QList>8#include <QList>
99
10#include "ui_dlgtrackinfo.h"10#include "ui_dlgtrackinfo.h"
11
12#include "trackinfoobject.h"11#include "trackinfoobject.h"
12#include "dlgtagfetcher.h"
1313
14/** Minimum allowed Beat per minute (BPM) */14/** Minimum allowed Beat per minute (BPM) */
15const int minBPM = 30;15const int minBPM = 30;
@@ -25,7 +25,7 @@
25class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo {25class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo {
26 Q_OBJECT26 Q_OBJECT
27 public:27 public:
28 DlgTrackInfo(QWidget* parent);28 DlgTrackInfo(QWidget* parent, DlgTagFetcher& DlgTagFetcher);
29 virtual ~DlgTrackInfo();29 virtual ~DlgTrackInfo();
3030
31 public slots:31 public slots:
@@ -43,6 +43,7 @@
43 void apply();43 void apply();
44 void cancel();44 void cancel();
45 void trackUpdated();45 void trackUpdated();
46 void fetchTag();
4647
47 void cueActivate();48 void cueActivate();
48 void cueDelete();49 void cueDelete();
@@ -58,6 +59,7 @@
58 void populateCues(TrackPointer pTrack);59 void populateCues(TrackPointer pTrack);
59 void unloadTrack(bool save);60 void unloadTrack(bool save);
60 void clear();61 void clear();
62 void init();
6163
62 QHash<int, Cue*> m_cueMap;64 QHash<int, Cue*> m_cueMap;
63 TrackPointer m_pLoadedTrack;65 TrackPointer m_pLoadedTrack;
@@ -66,6 +68,7 @@
66 QTime m_bpmTapTimer;68 QTime m_bpmTapTimer;
6769
68 QMutex m_mutex;70 QMutex m_mutex;
71 DlgTagFetcher& m_DlgTagFetcher;
6972
70};73};
7174
7275
=== modified file 'mixxx/src/dlgtrackinfo.ui'
--- mixxx/src/dlgtrackinfo.ui 2012-05-08 16:10:02 +0000
+++ mixxx/src/dlgtrackinfo.ui 2013-05-22 07:42:27 +0000
@@ -61,6 +61,13 @@
61 <item row="1" column="1" colspan="3">61 <item row="1" column="1" colspan="3">
62 <widget class="QLineEdit" name="txtArtist"/>62 <widget class="QLineEdit" name="txtArtist"/>
63 </item>63 </item>
64 <item row="8" column="0">
65 <widget class="QLabel" name="label_7">
66 <property name="text">
67 <string>Duration:</string>
68 </property>
69 </widget>
70 </item>
64 <item row="2" column="0">71 <item row="2" column="0">
65 <widget class="QLabel" name="label_6">72 <widget class="QLabel" name="label_6">
66 <property name="text">73 <property name="text">
@@ -68,25 +75,12 @@
68 </property>75 </property>
69 </widget>76 </widget>
70 </item>77 </item>
71 <item row="2" column="1" colspan="3">78 <item row="6" column="0">
72 <widget class="QLineEdit" name="txtAlbum"/>79 <widget class="QLabel" name="label_12">
73 </item>80 <property name="text">
74 <item row="3" column="0">81 <string>Key</string>
75 <widget class="QLabel" name="label_9">82 </property>
76 <property name="text">83 </widget>
77 <string>Genre:</string>
78 </property>
79 </widget>
80 </item>
81 <item row="4" column="0">
82 <widget class="QLabel" name="label_11">
83 <property name="text">
84 <string>Composer:</string>
85 </property>
86 </widget>
87 </item>
88 <item row="4" column="1" colspan="3">
89 <widget class="QLineEdit" name="txtComposer"/>
90 </item>84 </item>
91 <item row="3" column="1" colspan="3">85 <item row="3" column="1" colspan="3">
92 <widget class="QLineEdit" name="txtGenre"/>86 <widget class="QLineEdit" name="txtGenre"/>
@@ -98,33 +92,30 @@
98 </property>92 </property>
99 </widget>93 </widget>
100 </item>94 </item>
101 <item row="5" column="3">95 <item row="9" column="3">
102 <widget class="QLineEdit" name="txtTrackNumber"/>96 <widget class="QLabel" name="txtBitrate">
103 </item>97 <property name="text">
104 <item row="5" column="1">98 <string/>
105 <widget class="QLineEdit" name="txtYear"/>99 </property>
106 </item>100 <property name="textInteractionFlags">
107 <item row="5" column="0">101 <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
108 <widget class="QLabel" name="label_8">102 </property>
109 <property name="text">103 </widget>
110 <string>Year</string>104 </item>
111 </property>105 <item row="12" column="0" colspan="4">
112 </widget>106 <widget class="Line" name="line_3">
113 </item>107 <property name="orientation">
114 <item row="6" column="1">108 <enum>Qt::Horizontal</enum>
115 <widget class="QLineEdit" name="txtKey"/>109 </property>
116 </item>110 </widget>
117 <item row="6" column="0">111 </item>
118 <widget class="QLabel" name="label_12">112 <item row="11" column="0">
119 <property name="text">113 <widget class="QLabel" name="label_5">
120 <string>Key</string>114 <property name="text">
121 </property>115 <string>Location:</string>
122 </widget>116 </property>
123 </item>117 <property name="alignment">
124 <item row="8" column="0">118 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
125 <widget class="QLabel" name="label_7">
126 <property name="text">
127 <string>Duration:</string>
128 </property>119 </property>
129 </widget>120 </widget>
130 </item>121 </item>
@@ -155,6 +146,16 @@
155 </property>146 </property>
156 </widget>147 </widget>
157 </item>148 </item>
149 <item row="5" column="0">
150 <widget class="QLabel" name="label_8">
151 <property name="text">
152 <string>Year</string>
153 </property>
154 </widget>
155 </item>
156 <item row="6" column="1">
157 <widget class="QLineEdit" name="txtKey"/>
158 </item>
158 <item row="9" column="0">159 <item row="9" column="0">
159 <widget class="QLabel" name="label_13">160 <widget class="QLabel" name="label_13">
160 <property name="text">161 <property name="text">
@@ -172,30 +173,29 @@
172 </property>173 </property>
173 </widget>174 </widget>
174 </item>175 </item>
175 <item row="9" column="3">176 <item row="4" column="0">
176 <widget class="QLabel" name="txtBitrate">177 <widget class="QLabel" name="label_11">
177 <property name="text">178 <property name="text">
178 <string/>179 <string>Composer:</string>
179 </property>180 </property>
180 <property name="textInteractionFlags">181 </widget>
181 <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>182 </item>
182 </property>183 <item row="4" column="1" colspan="3">
183 </widget>184 <widget class="QLineEdit" name="txtComposer"/>
184 </item>185 </item>
185 <item row="12" column="0" colspan="4">186 <item row="5" column="3">
186 <widget class="Line" name="line_3">187 <widget class="QLineEdit" name="txtTrackNumber"/>
187 <property name="orientation">188 </item>
188 <enum>Qt::Horizontal</enum>189 <item row="5" column="1">
189 </property>190 <widget class="QLineEdit" name="txtYear"/>
190 </widget>191 </item>
191 </item>192 <item row="2" column="1" colspan="3">
192 <item row="11" column="0">193 <widget class="QLineEdit" name="txtAlbum"/>
193 <widget class="QLabel" name="label_5">194 </item>
194 <property name="text">195 <item row="3" column="0">
195 <string>Location:</string>196 <widget class="QLabel" name="label_9">
196 </property>197 <property name="text">
197 <property name="alignment">198 <string>Genre:</string>
198 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
199 </property>199 </property>
200 </widget>200 </widget>
201 </item>201 </item>
@@ -212,7 +212,7 @@
212 </property>212 </property>
213 </widget>213 </widget>
214 </item>214 </item>
215 <item row="13" column="1" colspan="3">215 <item row="14" column="1" colspan="3">
216 <widget class="QPushButton" name="btnReloadFromFile">216 <widget class="QPushButton" name="btnReloadFromFile">
217 <property name="text">217 <property name="text">
218 <string>Reload track metadata from file.</string>218 <string>Reload track metadata from file.</string>
@@ -275,6 +275,13 @@
275 </property>275 </property>
276 </widget>276 </widget>
277 </item>277 </item>
278 <item row="13" column="1" colspan="3">
279 <widget class="QPushButton" name="btnFetchTag">
280 <property name="text">
281 <string>Reload track metadata from musicBrainz</string>
282 </property>
283 </widget>
284 </item>
278 </layout>285 </layout>
279 </item>286 </item>
280 <item>287 <item>
281288
=== added directory 'mixxx/src/musicbrainz'
=== added file 'mixxx/src/musicbrainz/acoustidclient.cpp'
--- mixxx/src/musicbrainz/acoustidclient.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/acoustidclient.cpp 2013-05-22 07:42:27 +0000
@@ -0,0 +1,109 @@
1/*****************************************************************************
2 * Copyright © 2012 John Maguire <john.maguire@gmail.com> *
3 * David Sansome <me@davidsansome.com> *
4 * This work is free. You can redistribute it and/or modify it under the *
5 * terms of the Do What The Fuck You Want To Public License, Version 2, *
6 * as published by Sam Hocevar. *
7 * See http://www.wtfpl.net/ for more details. *
8 *****************************************************************************/
9
10#include <QCoreApplication>
11#include <QNetworkReply>
12#include <QXmlStreamReader>
13
14#include "acoustidclient.h"
15#include "gzip.h"
16#include "network.h"
17
18// see API-KEY site here http://acoustid.org/application/496
19// I registered the KEY for version 1.12 -- kain88 (may 2013)
20const QString CLIENT_APIKEY = "czKxnkyO";
21const QString CLIENT_NAME = "Mixxx1.12";
22const QString ACOUSTID_URL = "http://api.acoustid.org/v2/lookup";
23const int AcoustidClient::m_DefaultTimeout = 5000; // msec
24
25AcoustidClient::AcoustidClient(QObject* parent)
26 : QObject(parent),
27 m_network(this),
28 m_timeouts(m_DefaultTimeout, this) {
29}
30
31void AcoustidClient::setTimeout(int msec) {
32 m_timeouts.setTimeout(msec);
33}
34
35void AcoustidClient::start(int id, const QString& fingerprint, int duration) {
36 QUrl url;
37 url.addQueryItem("format", "xml");
38 url.addQueryItem("client", CLIENT_APIKEY);
39 url.addQueryItem("duration", QString::number(duration));
40 url.addQueryItem("meta", "recordingids");
41 url.addQueryItem("fingerprint", fingerprint);
42
43 QNetworkRequest req(QUrl::fromEncoded(ACOUSTID_URL.toAscii()));
44 req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
45 req.setRawHeader("Content-Encoding", "gzip");
46 req.setRawHeader("User-Agent", CLIENT_NAME.toAscii());
47
48 QNetworkReply* reply = m_network.post(req, gzipCompress(url.encodedQuery()));
49 connect(reply, SIGNAL(finished()), SLOT(requestFinished()));
50 m_requests[reply] = id;
51
52 m_timeouts.addReply(reply);
53}
54
55void AcoustidClient::cancel(int id) {
56 QNetworkReply* reply = m_requests.key(id);
57 m_requests.remove(reply);
58 delete reply;
59}
60
61void AcoustidClient::cancelAll() {
62 qDeleteAll(m_requests.keys());
63 m_requests.clear();
64}
65
66void AcoustidClient::requestFinished() {
67 QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
68 if (!reply)
69 return;
70
71 reply->deleteLater();
72 if (!m_requests.contains(reply))
73 return;
74
75 int id = m_requests.take(reply);
76
77 if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
78 emit finished(id, QString());
79 return;
80 }
81
82 QXmlStreamReader reader(reply);
83 QString ID;
84 while (!reader.atEnd()) {
85 if (reader.readNext() == QXmlStreamReader::StartElement
86 && reader.name()== "results") {
87 ID = parseResult(reader);
88 }
89 }
90
91 emit finished(id, ID);
92}
93
94QString AcoustidClient::parseResult(QXmlStreamReader& reader){
95
96 while (!reader.atEnd()) {
97 QXmlStreamReader::TokenType type = reader.readNext();
98 if (type== QXmlStreamReader::StartElement) {
99 QStringRef name = reader.name();
100 if (name == "id") {
101 return reader.readElementText();
102 }
103 }
104 if (type == QXmlStreamReader::EndElement && reader.name()=="result") {
105 break;
106 }
107 }
108 return QString();
109}
0110
=== added file 'mixxx/src/musicbrainz/acoustidclient.h'
--- mixxx/src/musicbrainz/acoustidclient.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/acoustidclient.h 2013-05-22 07:42:27 +0000
@@ -0,0 +1,67 @@
1/*****************************************************************************
2 * Copyright © 2012 John Maguire <john.maguire@gmail.com> *
3 * David Sansome <me@davidsansome.com> *
4 * This work is free. You can redistribute it and/or modify it under the *
5 * terms of the Do What The Fuck You Want To Public License, Version 2, *
6 * as published by Sam Hocevar. *
7 * See http://www.wtfpl.net/ for more details. *
8 *****************************************************************************/
9
10#ifndef ACOUSTIDCLIENT_H
11#define ACOUSTIDCLIENT_H
12
13#include <QMap>
14#include <QObject>
15#include <QtNetwork>
16
17#include "network.h"
18#include "trackinfoobject.h"
19
20class QXmlStreamReader;
21
22class AcoustidClient : public QObject {
23 Q_OBJECT
24
25 // Gets a MBID from a Chromaprint fingerprint.
26 // A fingerprint identifies one particular encoding of a song and is created
27 // by Fingerprinter. An MBID identifies the actual song and can be passed to
28 // Musicbrainz to get metadata.
29 // You can create one AcoustidClient and make multiple requests using it.
30 // IDs are provided by the caller when a request is started and included in
31 // the finished signal - they have no meaning to AcoustidClient.
32
33 public:
34 AcoustidClient(QObject* parent = 0);
35
36 // Network requests will be aborted after this interval.
37 void setTimeout(int msec);
38
39 // Starts a request and returns immediately. Finished() will be emitted
40 // later with the same ID.
41 void start(int id, const QString& fingerprint, int duration);
42
43 // Cancels the request with the given ID. Finished() will never be emitted
44 // for that ID. Does nothing if there is no request with the given ID.
45 void cancel(int id);
46
47 // Cancels all requests. Finished() will never be emitted for any pending
48 // requests.
49 void cancelAll();
50
51 QString parseResult(QXmlStreamReader& reader);
52
53 signals:
54 void finished(int id, const QString& mbid);
55
56 private slots:
57 void requestFinished();
58
59 private:
60 static const int m_DefaultTimeout;
61
62 QNetworkAccessManager m_network;
63 NetworkTimeouts m_timeouts;
64 QMap<QNetworkReply*, int> m_requests;
65};
66
67#endif // ACOUSTIDCLIENT_H
068
=== added file 'mixxx/src/musicbrainz/chromaprinter.cpp'
--- mixxx/src/musicbrainz/chromaprinter.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/chromaprinter.cpp 2013-05-22 07:42:27 +0000
@@ -0,0 +1,88 @@
1#include <QtCore>
2#include <chromaprint.h>
3
4#include "musicbrainz/chromaprinter.h"
5#include "soundsourceproxy.h"
6#include "defs.h"
7
8chromaprinter::chromaprinter(QObject* parent)
9 : QObject(parent){
10}
11
12QString chromaprinter::getFingerPrint(TrackPointer pTrack){
13 SoundSourceProxy soundSource(pTrack);
14 return calcFingerPrint(soundSource);
15}
16
17QString chromaprinter::getFingerPrint(QString location){
18 SoundSourceProxy soundSource(location);
19 return calcFingerPrint(soundSource);
20}
21
22QString chromaprinter::calcFingerPrint(SoundSourceProxy& soundSource){
23 soundSource.open();
24 m_SampleRate = soundSource.getSampleRate();
25 unsigned int length = soundSource.length();
26 if (m_SampleRate == 0 ){
27 qDebug() << "Skipping invalid file:" << soundSource.getFilename();
28 return QString();
29 }
30
31 // this is worth 2min of audio, multiply by 2 because we have 2 channels
32 // AcoustID only stores a fingerprint for the first two minutes of a song
33 // on their server so we need only a fingerprint of the first two minutes
34 // --kain88 July 2012
35 m_NumSamples = 120*2*m_SampleRate;
36 // check that the song is actually longer then the amount of audio we use
37 if (m_NumSamples > length) {
38 m_NumSamples = length;
39 }
40
41 SAMPLE *pData = new SAMPLE[m_NumSamples];
42 QTime timerReadingFile;
43 timerReadingFile.start();
44 int read = soundSource.read(m_NumSamples, pData);
45
46 if (read!=m_NumSamples) {
47 qDebug() << "oh that's embarrasing I couldn't read the track";
48 return QString();
49 }
50 qDebug("reading file took: %d ms" , timerReadingFile.elapsed());
51
52 ChromaprintContext* ctx = chromaprint_new(CHROMAPRINT_ALGORITHM_DEFAULT);
53 // we have 2 channels in mixxx always
54 chromaprint_start(ctx, m_SampleRate, 2);
55
56 QTime timerGeneratingFingerPrint;
57 timerGeneratingFingerPrint.start();
58 int success = chromaprint_feed(ctx, pData, m_NumSamples);
59 if (!success) {
60 qDebug() << "could not generate fingerprint";
61 return QString();
62 }
63 chromaprint_finish(ctx);
64
65 void* fprint = NULL;
66 int size = 0;
67 int ret = chromaprint_get_raw_fingerprint(ctx, &fprint, &size);
68 QByteArray fingerprint;
69 if (ret == 1) {
70 void* encoded = NULL;
71 int encoded_size = 0;
72 chromaprint_encode_fingerprint(fprint, size,
73 CHROMAPRINT_ALGORITHM_DEFAULT,
74 &encoded,
75 &encoded_size, 1);
76
77 fingerprint.append(reinterpret_cast<char*>(encoded), encoded_size);
78
79 chromaprint_dealloc(fprint);
80 chromaprint_dealloc(encoded);
81 }
82 chromaprint_free(ctx);
83 delete pData;
84
85 qDebug("generating fingerprint took: %d ms" , timerGeneratingFingerPrint.elapsed());
86
87 return fingerprint;
88}
089
=== added file 'mixxx/src/musicbrainz/chromaprinter.h'
--- mixxx/src/musicbrainz/chromaprinter.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/chromaprinter.h 2013-05-22 07:42:27 +0000
@@ -0,0 +1,25 @@
1#ifndef CHROMAPRINTER_H
2#define CHROMAPRINTER_H
3
4#include <QObject>
5
6#include "trackinfoobject.h"
7
8class SoundSourceProxy;
9
10class chromaprinter: public QObject {
11 Q_OBJECT
12
13 public:
14 chromaprinter(QObject* parent=NULL);
15 QString getFingerPrint(TrackPointer pTrack);
16 QString getFingerPrint(QString location);
17
18 private:
19
20 QString calcFingerPrint(SoundSourceProxy& soundSource);
21 unsigned int m_NumSamples;
22 unsigned int m_SampleRate;
23};
24
25#endif //CHROMAPRINTER_H
026
=== added file 'mixxx/src/musicbrainz/crc.c'
--- mixxx/src/musicbrainz/crc.c 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/crc.c 2013-05-22 07:42:27 +0000
@@ -0,0 +1,133 @@
1/**
2 * \file crc.c
3 * Functions and types for CRC checks.
4 *
5 * Generated on Sun Oct 24 10:58:31 2010,
6 * by pycrc v0.7.6, http://www.tty1.net/pycrc/
7 * using the configuration:
8 * Width = 32
9 * Poly = 0x04c11db7
10 * XorIn = 0xffffffff
11 * ReflectIn = True
12 * XorOut = 0xffffffff
13 * ReflectOut = True
14 * Algorithm = table-driven
15 *****************************************************************************/
16#include "crc.h"
17#include <stdint.h>
18#include <stdlib.h>
19
20/**
21 * Static table used for the table_driven implementation.
22 *****************************************************************************/
23static const crc_t crc_table[256] = {
24 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
25 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
26 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
27 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
28 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
29 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
30 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
31 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
32 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
33 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
34 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
35 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
36 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
37 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
38 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
39 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
40 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
41 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
42 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
43 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
44 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
45 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
46 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
47 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
48 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
49 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
50 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
51 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
52 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
53 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
54 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
55 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
56 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
57 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
58 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
59 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
60 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
61 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
62 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
63 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
64 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
65 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
66 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
67 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
68 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
69 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
70 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
71 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
72 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
73 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
74 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
75 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
76 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
77 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
78 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
79 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
80 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
81 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
82 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
83 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
84 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
85 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
86 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
87 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
88};
89
90/**
91 * Reflect all bits of a \a data word of \a data_len bytes.
92 *
93 * \param data The data word to be reflected.
94 * \param data_len The width of \a data expressed in number of bits.
95 * \return The reflected data.
96 *****************************************************************************/
97crc_t crc_reflect(crc_t data, size_t data_len)
98{
99 unsigned int i;
100 crc_t ret;
101
102 ret = data & 0x01;
103 for (i = 1; i < data_len; i++) {
104 data >>= 1;
105 ret = (ret << 1) | (data & 0x01);
106 }
107 return ret;
108}
109
110
111/**
112 * Update the crc value with new data.
113 *
114 * \param crc The current crc value.
115 * \param data Pointer to a buffer of \a data_len bytes.
116 * \param data_len Number of bytes in the \a data buffer.
117 * \return The updated crc value.
118 *****************************************************************************/
119crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len)
120{
121 unsigned int tbl_idx;
122
123 while (data_len--) {
124 tbl_idx = (crc ^ *data) & 0xff;
125 crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff;
126
127 data++;
128 }
129 return crc & 0xffffffff;
130}
131
132
133
0134
=== added file 'mixxx/src/musicbrainz/crc.h'
--- mixxx/src/musicbrainz/crc.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/crc.h 2013-05-22 07:42:27 +0000
@@ -0,0 +1,83 @@
1/**
2 * \file crc.h
3 * Functions and types for CRC checks.
4 *
5 * Generated on Sun Oct 24 10:58:41 2010,
6 * by pycrc v0.7.6, http://www.tty1.net/pycrc/
7 * using the configuration:
8 * Width = 32
9 * Poly = 0x04c11db7
10 * XorIn = 0xffffffff
11 * ReflectIn = True
12 * XorOut = 0xffffffff
13 * ReflectOut = True
14 * Algorithm = table-driven
15 *****************************************************************************/
16#ifndef __CRC_H__
17#define __CRC_H__
18
19#include <stdint.h>
20#include <stdlib.h>
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25
26
27/**
28 * The definition of the used algorithm.
29 *****************************************************************************/
30#define CRC_ALGO_TABLE_DRIVEN 1
31
32
33/**
34 * The type of the CRC values.
35 *
36 * This type must be big enough to contain at least 32 bits.
37 *****************************************************************************/
38typedef unsigned long crc_t;
39
40
41/**
42 * Reflect all bits of a \a data word of \a data_len bytes.
43 *
44 * \param data The data word to be reflected.
45 * \param data_len The width of \a data expressed in number of bits.
46 * \return The reflected data.
47 *****************************************************************************/
48crc_t crc_reflect(crc_t data, size_t data_len);
49
50
51/**
52 * Calculate the initial crc value.
53 *
54 * \return The initial crc value.
55 *****************************************************************************/
56#define crc_init() (0xffffffff)
57
58
59/**
60 * Update the crc value with new data.
61 *
62 * \param crc The current crc value.
63 * \param data Pointer to a buffer of \a data_len bytes.
64 * \param data_len Number of bytes in the \a data buffer.
65 * \return The updated crc value.
66 *****************************************************************************/
67crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len);
68
69
70/**
71 * Calculate the final crc value.
72 *
73 * \param crc The current crc value.
74 * \return The final crc value.
75 *****************************************************************************/
76#define crc_finalize(crc) (crc ^ 0xffffffff)
77
78
79#ifdef __cplusplus
80} /* closing brace for extern "C" */
81#endif
82
83#endif /* __CRC_H__ */
084
=== added file 'mixxx/src/musicbrainz/gzip.cpp'
--- mixxx/src/musicbrainz/gzip.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/gzip.cpp 2013-05-22 07:42:27 +0000
@@ -0,0 +1,49 @@
1/*****************************************************************************
2 * Author: Lukáš Lalinský <info@acoustid.org> *
3 *****************************************************************************/
4
5#include "crc.h"
6#include "gzip.h"
7
8inline QByteArray render32BitInt(unsigned long value)
9{
10 unsigned char data[4];
11 data[0] = (value ) & 255;
12 data[1] = (value >> 8) & 255;
13 data[2] = (value >> 16) & 255;
14 data[3] = (value >> 24) & 255;
15 return QByteArray((char *)data, 4);
16}
17
18inline unsigned long calculateCrc32(const QByteArray &data)
19{
20 crc_t crc;
21 crc = crc_init();
22 crc = crc_update(crc, (unsigned char *)data.data(), data.size());
23 crc = crc_finalize(crc);
24 return crc;
25}
26
27QByteArray gzipCompress(const QByteArray &data)
28{
29 const char header[10] = {
30 0x1f, 0x8b, // ID1 + ID2
31 8, // Compression Method
32 0, // Flags
33 0, 0, 0, 0, // Modification Time
34 2, // Extra Flags
35 255, // Operating System
36 };
37
38 QByteArray compressedData = qCompress(data);
39 compressedData.remove(0, 6); // Qt size + zlib header
40 compressedData.remove(compressedData.size() - 4, 4); // zlib footer
41
42 QByteArray result;
43 result.append(header, 10);
44 result.append(compressedData);
45 result.append(render32BitInt(calculateCrc32(data)));
46 result.append(render32BitInt(data.size()));
47 return result;
48}
49
050
=== added file 'mixxx/src/musicbrainz/gzip.h'
--- mixxx/src/musicbrainz/gzip.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/gzip.h 2013-05-22 07:42:27 +0000
@@ -0,0 +1,12 @@
1/*****************************************************************************
2 * Author: Lukáš Lalinský <info@acoustid.org> *
3 *****************************************************************************/
4
5#ifndef FPSUBMIT_GZIP_H_
6#define FPSUBMIT_GZIP_H_
7
8#include <QByteArray>
9
10QByteArray gzipCompress(const QByteArray &data);
11
12#endif
013
=== added file 'mixxx/src/musicbrainz/musicbrainzclient.cpp'
--- mixxx/src/musicbrainz/musicbrainzclient.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/musicbrainzclient.cpp 2013-05-22 07:42:27 +0000
@@ -0,0 +1,180 @@
1/*****************************************************************************
2 * Copyright © 2012 John Maguire <john.maguire@gmail.com> *
3 * David Sansome <me@davidsansome.com> *
4 * This work is free. You can redistribute it and/or modify it under the *
5 * terms of the Do What The Fuck You Want To Public License, Version 2, *
6 * as published by Sam Hocevar. *
7 * See http://www.wtfpl.net/ for more details. *
8 *****************************************************************************/
9
10#include <QCoreApplication>
11#include <QNetworkReply>
12#include <QtNetwork>
13#include <QSet>
14#include <QXmlStreamReader>
15
16#include "musicbrainzclient.h"
17
18const QString MusicBrainzClient::m_TrackUrl = "http://musicbrainz.org/ws/2/recording/";
19const QString MusicBrainzClient::m_DateRegex = "^[12]\\d{3}";
20const int MusicBrainzClient::m_DefaultTimeout = 5000; // msec
21
22MusicBrainzClient::MusicBrainzClient(QObject* parent)
23 : QObject(parent),
24 m_network(this),
25 m_timeouts(m_DefaultTimeout, this){
26}
27
28void MusicBrainzClient::start(int id, const QString& mbid) {
29 typedef QPair<QString, QString> Param;
30
31 QList<Param> parameters;
32 parameters << Param("inc", "artists+releases+media");
33
34 QUrl url(m_TrackUrl + mbid);
35 url.setQueryItems(parameters);
36 QNetworkRequest req(url);
37
38 QNetworkReply* reply = m_network.get(req);
39 connect(reply, SIGNAL(finished()), SLOT(requestFinished()));
40 m_requests[reply] = id;
41
42 m_timeouts.addReply(reply);
43}
44
45void MusicBrainzClient::cancel(int id) {
46 QNetworkReply* reply = m_requests.key(id);
47 m_requests.remove(reply);
48 delete reply;
49}
50
51void MusicBrainzClient::cancelAll() {
52 qDeleteAll(m_requests.keys());
53 m_requests.clear();
54}
55
56void MusicBrainzClient::requestFinished() {
57 QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
58 if (!reply)
59 return;
60
61 reply->deleteLater();
62 if (!m_requests.contains(reply))
63 return;
64
65 int id = m_requests.take(reply);
66 ResultList ret;
67
68 if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
69 emit finished(id, ret);
70 return;
71 }
72
73 QXmlStreamReader reader(reply);
74 while (!reader.atEnd()) {
75 if (reader.readNext() == QXmlStreamReader::StartElement
76 && reader.name() == "recording") {
77
78 ResultList tracks = parseTrack(reader);
79 foreach (const Result& track, tracks) {
80 if (!track.m_title.isEmpty()) {
81 ret << track;
82 }
83 }
84 }
85 }
86 emit finished(id, uniqueResults(ret));
87}
88
89MusicBrainzClient::ResultList MusicBrainzClient::parseTrack(QXmlStreamReader& reader) {
90 Result result;
91 QList<Release> releases;
92
93 while (!reader.atEnd()) {
94 QXmlStreamReader::TokenType type = reader.readNext();
95
96 if (type == QXmlStreamReader::StartElement) {
97 QStringRef name = reader.name();
98 if (name == "title") {
99 result.m_title = reader.readElementText();
100 } else if (name == "length") {
101 // convert msec to sec
102 result.m_duration = reader.readElementText().toInt()*10000000;
103 } else if (name == "artist") {
104 parseArtist(reader, result.m_artist);
105 } else if (name == "release") {
106 releases << parseRelease(reader);
107 }
108 }
109
110 if (type == QXmlStreamReader::EndElement && reader.name() == "recording") {
111 break;
112 }
113 }
114
115 ResultList ret;
116 foreach (const Release& release, releases) {
117 ret << release.CopyAndMergeInto(result);
118 }
119 return ret;
120}
121
122void MusicBrainzClient::parseArtist(QXmlStreamReader& reader, QString& artist) {
123 while (!reader.atEnd()) {
124 QXmlStreamReader::TokenType type = reader.readNext();
125
126 if (type == QXmlStreamReader::StartElement && reader.name() == "name") {
127 artist = reader.readElementText();
128 }
129
130 if (type == QXmlStreamReader::EndElement && reader.name() == "artist") {
131 return;
132 }
133 }
134}
135
136MusicBrainzClient::Release MusicBrainzClient::parseRelease(QXmlStreamReader& reader) {
137 Release ret;
138
139 while (!reader.atEnd()) {
140 QXmlStreamReader::TokenType type = reader.readNext();
141
142 if (type == QXmlStreamReader::StartElement) {
143 QStringRef name = reader.name();
144 if (name == "title") {
145 ret.m_album = reader.readElementText();
146 } else if (name == "date") {
147 QRegExp regex(m_DateRegex);
148 if (regex.indexIn(reader.readElementText()) == 0) {
149 ret.m_year = regex.cap(0).toInt();
150 }
151 } else if (name == "track-list") {
152 ret.m_track = reader.attributes().value("offset").toString().toInt() + 1;
153 consumeCurrentElement(reader);
154 }
155 }
156
157 if (type == QXmlStreamReader::EndElement && reader.name() == "release") {
158 break;
159 }
160 }
161
162 return ret;
163}
164
165MusicBrainzClient::ResultList MusicBrainzClient::uniqueResults(const ResultList& results) {
166 ResultList ret = QSet<Result>::fromList(results).toList();
167 qSort(ret);
168 return ret;
169}
170
171void MusicBrainzClient::consumeCurrentElement(QXmlStreamReader& reader) {
172 int level = 1;
173 while (level != 0 && !reader.atEnd()) {
174 switch (reader.readNext()) {
175 case QXmlStreamReader::StartElement: ++level; break;
176 case QXmlStreamReader::EndElement: --level; break;
177 default: break;
178 }
179 }
180}
0181
=== added file 'mixxx/src/musicbrainz/musicbrainzclient.h'
--- mixxx/src/musicbrainz/musicbrainzclient.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/musicbrainzclient.h 2013-05-22 07:42:27 +0000
@@ -0,0 +1,130 @@
1/*****************************************************************************
2 * Copyright © 2012 John Maguire <john.maguire@gmail.com> *
3 * David Sansome <me@davidsansome.com> *
4 * This work is free. You can redistribute it and/or modify it under the *
5 * terms of the Do What The Fuck You Want To Public License, Version 2, *
6 * as published by Sam Hocevar. *
7 * See http://www.wtfpl.net/ for more details. *
8 *****************************************************************************/
9
10#ifndef MUSICBRAINZCLIENT_H
11#define MUSICBRAINZCLIENT_H
12
13#include <QHash>
14#include <QMap>
15#include <QObject>
16#include <QXmlStreamReader>
17#include <QtNetwork>
18
19#include "network.h"
20
21class MusicBrainzClient : public QObject {
22 Q_OBJECT
23
24 // Gets metadata for a particular MBID.
25 // An MBID is created from a fingerprint using chromaprint library.
26 // You can create one MusicBrainzClient and make multiple requests using it.
27 // IDs are provided by the caller when a request is started and included in
28 // the Finished signal - they have no meaning to MusicBrainzClient.
29
30 public:
31 MusicBrainzClient(QObject* parent = 0);
32
33 struct Result {
34 Result() : m_duration(0), m_track(0), m_year(-1) {}
35
36 bool operator <(const Result& other) const {
37 #define cmp(field) \
38 if (field < other.field) return true; \
39 if (field > other.field) return false;
40
41 cmp(m_track);
42 cmp(m_year);
43 cmp(m_title);
44 cmp(m_artist);
45 return false;
46
47 #undef cmp
48 }
49
50 bool operator ==(const Result& other) const {
51 return m_title == other.m_title &&
52 m_artist == other.m_artist &&
53 m_album == other.m_album &&
54 m_duration == other.m_duration &&
55 m_track == other.m_track &&
56 m_year == other.m_year;
57 }
58
59 QString m_title;
60 QString m_artist;
61 QString m_album;
62 int m_duration;
63 int m_track;
64 int m_year;
65 };
66 typedef QList<Result> ResultList;
67
68
69 // Starts a request and returns immediately. finished() will be emitted
70 // later with the same ID.
71 void start(int id, const QString& mbid);
72 static void consumeCurrentElement(QXmlStreamReader& reader);
73
74 // Cancels the request with the given ID. Finished() will never be emitted
75 // for that ID. Does nothing if there is no request with the given ID.
76 void cancel(int id);
77
78 // Cancels all requests. Finished() will never be emitted for any pending
79 // requests.
80 void cancelAll();
81
82 signals:
83 // Finished signal emitted when fechting songs tags
84 void finished(int id, const MusicBrainzClient::ResultList& result);
85
86 private slots:
87 void requestFinished();
88
89 private:
90 struct Release {
91 Release() : m_track(0), m_year(0) {}
92
93 Result CopyAndMergeInto(const Result& orig) const {
94 Result ret(orig);
95 ret.m_album = m_album;
96 ret.m_track = m_track;
97 ret.m_year = m_year;
98 return ret;
99 }
100
101 QString m_album;
102 int m_track;
103 int m_year;
104 };
105
106 static ResultList parseTrack(QXmlStreamReader& reader);
107 static void parseArtist(QXmlStreamReader& reader, QString& artist);
108 static Release parseRelease(QXmlStreamReader& reader);
109 static ResultList uniqueResults(const ResultList& results);
110
111 private:
112 static const QString m_TrackUrl;
113 static const QString m_DateRegex;
114 static const int m_DefaultTimeout;
115
116 QNetworkAccessManager m_network;
117 NetworkTimeouts m_timeouts;
118 QMap<QNetworkReply*, int> m_requests;
119};
120
121inline uint qHash(const MusicBrainzClient::Result& result) {
122 return qHash(result.m_album) ^
123 qHash(result.m_artist) ^
124 result.m_duration ^
125 qHash(result.m_title) ^
126 result.m_track ^
127 result.m_year;
128}
129
130#endif // MUSICBRAINZCLIENT_H
0131
=== added file 'mixxx/src/musicbrainz/tagfetcher.cpp'
--- mixxx/src/musicbrainz/tagfetcher.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/tagfetcher.cpp 2013-05-22 07:42:27 +0000
@@ -0,0 +1,121 @@
1/*****************************************************************************
2 * Copyright © 2012 John Maguire <john.maguire@gmail.com> *
3 * David Sansome <me@davidsansome.com> *
4 * This work is free. You can redistribute it and/or modify it under the *
5 * terms of the Do What The Fuck You Want To Public License, Version 2, *
6 * as published by Sam Hocevar. *
7 * See http://www.wtfpl.net/ for more details. *
8 *****************************************************************************/
9
10#include <QFuture>
11#include <QUrl>
12#include <QtConcurrentMap>
13
14#include "musicbrainz/tagfetcher.h"
15#include "musicbrainz/chromaprinter.h"
16#include "musicbrainz/musicbrainzclient.h"
17
18TagFetcher::TagFetcher(QObject* parent)
19 : QObject(parent),
20 m_pFingerprintWatcher(NULL),
21 m_AcoustidClient(this),
22 m_MusicbrainzClient(this) {
23 connect(&m_AcoustidClient, SIGNAL(finished(int,QString)),
24 this, SLOT(mbidFound(int,QString)));
25 connect(&m_MusicbrainzClient, SIGNAL(finished(int,MusicBrainzClient::ResultList)),
26 this, SLOT(tagsFetched(int,MusicBrainzClient::ResultList)));
27}
28
29QString TagFetcher::getFingerprint(const TrackPointer tio) {
30 return chromaprinter(NULL).getFingerPrint(tio);
31}
32
33void TagFetcher::startFetch(const TrackPointer track) {
34 cancel();
35 // qDebug() << "start to fetch track metadata";
36 QList<TrackPointer> tracks;
37 tracks.append(track);
38 m_tracks = tracks;
39
40 QFuture<QString> future = QtConcurrent::mapped(m_tracks, getFingerprint);
41 m_pFingerprintWatcher = new QFutureWatcher<QString>(this);
42 m_pFingerprintWatcher->setFuture(future);
43 connect(m_pFingerprintWatcher, SIGNAL(resultReadyAt(int)),
44 SLOT(fingerprintFound(int)));
45
46 foreach (const TrackPointer ptrack, m_tracks) {
47 emit fetchProgress(tr("Fingerprinting track"));
48 }
49}
50
51void TagFetcher::cancel() {
52 // qDebug()<< "Cancel tagfetching";
53 if (m_pFingerprintWatcher) {
54 m_pFingerprintWatcher->cancel();
55
56 delete m_pFingerprintWatcher;
57 m_pFingerprintWatcher = NULL;
58 }
59
60 m_AcoustidClient.cancelAll();
61 m_MusicbrainzClient.cancelAll();
62 m_tracks.clear();
63}
64
65void TagFetcher::fingerprintFound(int index) {
66 QFutureWatcher<QString>* watcher = reinterpret_cast<QFutureWatcher<QString>*>(sender());
67 if (!watcher || index >= m_tracks.count()) {
68 return;
69 }
70
71 const QString fingerprint = watcher->resultAt(index);
72 const TrackPointer ptrack = m_tracks[index];
73
74 if (fingerprint.isEmpty()) {
75 emit resultAvailable(ptrack, QList<TrackPointer>());
76 return;
77 }
78
79 emit fetchProgress(tr("Identifying track"));
80 // qDebug() << "start to look up the MBID";
81 m_AcoustidClient.start(index, fingerprint, ptrack->getDuration());
82}
83
84void TagFetcher::mbidFound(int index, const QString& mbid) {
85 if (index >= m_tracks.count()) {
86 return;
87 }
88
89 const TrackPointer pTrack = m_tracks[index];
90
91 if (mbid.isEmpty()) {
92 emit resultAvailable(pTrack, QList<TrackPointer>());
93 return;
94 }
95
96 emit fetchProgress(tr("Downloading Metadata"));
97 //qDebug() << "start to fetch tags from MB";
98 m_MusicbrainzClient.start(index, mbid);
99}
100
101void TagFetcher::tagsFetched(int index, const MusicBrainzClient::ResultList& results) {
102 if (index >= m_tracks.count()) {
103 return;
104 }
105 // qDebug() << "Tagfetcher got musicbrainz results and now parses them";
106 const TrackPointer originalTrack = m_tracks[index];
107 QList<TrackPointer> tracksGuessed;
108
109 foreach (const MusicBrainzClient::Result& result, results) {
110 TrackPointer track(new TrackInfoObject(originalTrack->getLocation(),false),
111 &QObject::deleteLater);
112 track->setTitle(result.m_title);
113 track->setArtist(result.m_artist);
114 track->setAlbum(result.m_album);
115 track->setDuration(result.m_duration);
116 track->setTrackNumber(QString::number(result.m_track));
117 track->setYear(QString::number(result.m_year));
118 tracksGuessed << track;
119 }
120 emit resultAvailable(originalTrack, tracksGuessed);
121}
0122
=== added file 'mixxx/src/musicbrainz/tagfetcher.h'
--- mixxx/src/musicbrainz/tagfetcher.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/musicbrainz/tagfetcher.h 2013-05-22 07:42:27 +0000
@@ -0,0 +1,58 @@
1/*****************************************************************************
2 * Copyright © 2012 John Maguire <john.maguire@gmail.com> *
3 * David Sansome <me@davidsansome.com> *
4 * This work is free. You can redistribute it and/or modify it under the *
5 * terms of the Do What The Fuck You Want To Public License, Version 2, *
6 * as published by Sam Hocevar. *
7 * See http://www.wtfpl.net/ for more details. *
8 *****************************************************************************/
9
10#ifndef TAGFETCHER_H
11#define TAGFETCHER_H
12
13#include <QFutureWatcher>
14#include <QObject>
15
16#include "musicbrainz/musicbrainzclient.h"
17#include "musicbrainz/acoustidclient.h"
18#include "trackinfoobject.h"
19
20
21class TagFetcher : public QObject {
22 Q_OBJECT
23
24 // High level interface to Fingerprinter, AcoustidClient and
25 // MusicBrainzClient.
26
27 public:
28 TagFetcher(QObject* parent = 0);
29
30 void startFetch(const TrackPointer track);
31
32 public slots:
33 void cancel();
34
35 signals:
36 void resultAvailable(const TrackPointer originalTrack,
37 const QList<TrackPointer>& tracksGuessed);
38 void fetchProgress(QString);
39
40 private slots:
41 void fingerprintFound(int index);
42 void mbidFound(int index, const QString& mbid);
43 void tagsFetched(int index, const MusicBrainzClient::ResultList& result);
44
45 private:
46 // has to be static so we can call it with QtConcurrent and have a nice
47 // responsive UI while the fingerprint is calculated
48 static QString getFingerprint(const TrackPointer tio);
49
50 QFutureWatcher<QString>* m_pFingerprintWatcher;
51 AcoustidClient m_AcoustidClient;
52 MusicBrainzClient m_MusicbrainzClient;
53
54 // Code can already be run on an arbitrary number of input tracks
55 QList<TrackPointer> m_tracks;
56};
57
58#endif // TAGFETCHER_H
059
=== added file 'mixxx/src/network.cpp'
--- mixxx/src/network.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/network.cpp 2013-05-22 07:42:27 +0000
@@ -0,0 +1,73 @@
1/*****************************************************************************
2 * Copyright © 2012 John Maguire <john.maguire@gmail.com> *
3 * David Sansome <me@davidsansome.com> *
4 * This work is free. You can redistribute it and/or modify it under the *
5 * terms of the Do What The Fuck You Want To Public License, Version 2, *
6 * as published by Sam Hocevar. *
7 * See http://www.wtfpl.net/ for more details. *
8 *****************************************************************************/
9
10#include <QCoreApplication>
11#include <QDir>
12#include <QNetworkAccessManager>
13#include <QNetworkDiskCache>
14#include <QNetworkReply>
15
16#include "network.h"
17
18NetworkAccessManager::NetworkAccessManager(QObject* parent)
19 : QNetworkAccessManager(parent) {
20}
21
22QNetworkReply* NetworkAccessManager::createRequest(Operation op,
23 const QNetworkRequest& request,
24 QIODevice* outgoingData) {
25 QNetworkRequest new_request(request);
26 new_request.setRawHeader("User-Agent", QString("%1 %2").arg(
27 QCoreApplication::applicationName(),
28 QCoreApplication::applicationVersion()).toUtf8());
29
30 if (op == QNetworkAccessManager::PostOperation &&
31 !new_request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
32 new_request.setHeader(QNetworkRequest::ContentTypeHeader,
33 "application/x-www-form-urlencoded");
34 }
35
36 // Prefer the cache unless the caller has changed the setting already
37 if (request.attribute(QNetworkRequest::CacheLoadControlAttribute).toInt()
38 == QNetworkRequest::PreferNetwork) {
39 new_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
40 QNetworkRequest::PreferCache);
41 }
42
43 return QNetworkAccessManager::createRequest(op, new_request, outgoingData);
44}
45
46
47NetworkTimeouts::NetworkTimeouts(int timeout_msec, QObject* parent)
48 : QObject(parent),
49 m_timeout_msec(timeout_msec) {
50}
51
52void NetworkTimeouts::addReply(QNetworkReply* reply) {
53 if (m_timers.contains(reply))
54 return;
55
56 connect(reply, SIGNAL(destroyed()), SLOT(replyFinished()));
57 connect(reply, SIGNAL(finished()), SLOT(replyFinished()));
58 m_timers[reply] = startTimer(m_timeout_msec);
59}
60
61void NetworkTimeouts::replyFinished() {
62 QNetworkReply* reply = reinterpret_cast<QNetworkReply*>(sender());
63 if (m_timers.contains(reply)) {
64 killTimer(m_timers.take(reply));
65 }
66}
67
68void NetworkTimeouts::timerEvent(QTimerEvent* e) {
69 QNetworkReply* reply = m_timers.key(e->timerId());
70 if (reply) {
71 reply->abort();
72 }
73}
074
=== added file 'mixxx/src/network.h'
--- mixxx/src/network.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/network.h 2013-05-22 07:42:27 +0000
@@ -0,0 +1,49 @@
1/*****************************************************************************
2 * Copyright © 2012 John Maguire <john.maguire@gmail.com> *
3 * David Sansome <me@davidsansome.com> *
4 * This work is free. You can redistribute it and/or modify it under the *
5 * terms of the Do What The Fuck You Want To Public License, Version 2, *
6 * as published by Sam Hocevar. *
7 * See http://www.wtfpl.net/ for more details. *
8 *****************************************************************************/
9
10#ifndef NETWORK_H
11#define NETWORK_H
12
13#include <QAbstractNetworkCache>
14#include <QNetworkAccessManager>
15#include <QNetworkReply>
16
17class NetworkAccessManager : public QNetworkAccessManager {
18 Q_OBJECT
19
20 public:
21 NetworkAccessManager(QObject* parent = 0);
22
23 protected:
24 QNetworkReply* createRequest(Operation op, const QNetworkRequest& request,
25 QIODevice* outgoingData);
26};
27
28
29class NetworkTimeouts : public QObject {
30 Q_OBJECT
31
32 public:
33 NetworkTimeouts(int timeout_msec, QObject* parent = 0);
34
35 void addReply(QNetworkReply* reply);
36 void setTimeout(int msec) { m_timeout_msec = msec; }
37
38 protected:
39 void timerEvent(QTimerEvent* e);
40
41 private slots:
42 void replyFinished();
43
44 private:
45 int m_timeout_msec;
46 QMap<QNetworkReply*, int> m_timers;
47};
48
49#endif // NETWORK_H
050
=== modified file 'mixxx/src/widget/wtracktableview.cpp'
--- mixxx/src/widget/wtracktableview.cpp 2013-05-21 03:02:42 +0000
+++ mixxx/src/widget/wtracktableview.cpp 2013-05-22 07:42:27 +0000
@@ -25,14 +25,20 @@
25 WTRACKTABLEVIEW_VSCROLLBARPOS_KEY)),25 WTRACKTABLEVIEW_VSCROLLBARPOS_KEY)),
26 m_pConfig(pConfig),26 m_pConfig(pConfig),
27 m_pTrackCollection(pTrackCollection),27 m_pTrackCollection(pTrackCollection),
28 m_DlgTagFetcher(NULL) ,
28 m_sorting(sorting) {29 m_sorting(sorting) {
29 // Give a NULL parent because otherwise it inherits our style which can make30 // Give a NULL parent because otherwise it inherits our style which can make
30 // it unreadable. Bug #67341131 // it unreadable. Bug #673411
31 m_pTrackInfo = new DlgTrackInfo(NULL);32 m_pTrackInfo = new DlgTrackInfo(NULL,m_DlgTagFetcher);
32 connect(m_pTrackInfo, SIGNAL(next()),33 connect(m_pTrackInfo, SIGNAL(next()),
33 this, SLOT(slotNextTrackInfo()));34 this, SLOT(slotNextTrackInfo()));
34 connect(m_pTrackInfo, SIGNAL(previous()),35 connect(m_pTrackInfo, SIGNAL(previous()),
35 this, SLOT(slotPrevTrackInfo()));36 this, SLOT(slotPrevTrackInfo()));
37 connect(&m_DlgTagFetcher, SIGNAL(next()),
38 this, SLOT(slotNextDlgTagFetcher()));
39 connect(&m_DlgTagFetcher, SIGNAL(previous()),
40 this, SLOT(slotPrevDlgTagFetcher()));
41
3642
37 connect(&m_loadTrackMapper, SIGNAL(mapped(QString)),43 connect(&m_loadTrackMapper, SIGNAL(mapped(QString)),
38 this, SLOT(loadSelectionToGroup(QString)));44 this, SLOT(loadSelectionToGroup(QString)));
@@ -75,8 +81,8 @@
75 this, SLOT(addSelectionToCrate(int)));81 this, SLOT(addSelectionToCrate(int)));
76}82}
7783
78WTrackTableView::~WTrackTableView()84WTrackTableView::~WTrackTableView() {
79{85 qDebug() << "~WTrackTableView()";
80 WTrackTableViewHeader* pHeader =86 WTrackTableViewHeader* pHeader =
81 dynamic_cast<WTrackTableViewHeader*>(horizontalHeader());87 dynamic_cast<WTrackTableViewHeader*>(horizontalHeader());
82 if (pHeader) {88 if (pHeader) {
@@ -84,6 +90,7 @@
84 }90 }
8591
86 delete m_pReloadMetadataAct;92 delete m_pReloadMetadataAct;
93 delete m_pReloadMetadataFromMusicBrainzAct;
87 delete m_pAddToPreviewDeck;94 delete m_pAddToPreviewDeck;
88 delete m_pAutoDJAct;95 delete m_pAutoDJAct;
89 delete m_pAutoDJTopAct;96 delete m_pAutoDJTopAct;
@@ -290,6 +297,10 @@
290 connect(m_pReloadMetadataAct, SIGNAL(triggered()),297 connect(m_pReloadMetadataAct, SIGNAL(triggered()),
291 this, SLOT(slotReloadTrackMetadata()));298 this, SLOT(slotReloadTrackMetadata()));
292299
300 m_pReloadMetadataFromMusicBrainzAct = new QAction(tr("Reload from Musicbrainz"),this);
301 connect(m_pReloadMetadataFromMusicBrainzAct, SIGNAL(triggered()),
302 this, SLOT(slotShowDlgTagFetcher()));
303
293 m_pAddToPreviewDeck = new QAction(tr("Load to Preview Deck"), this);304 m_pAddToPreviewDeck = new QAction(tr("Load to Preview Deck"), this);
294 // currently there is only one preview deck so just map it here.305 // currently there is only one preview deck so just map it here.
295 QString previewDeckGroup = PlayerManager::groupForPreviewDeck(0);306 QString previewDeckGroup = PlayerManager::groupForPreviewDeck(0);
@@ -456,6 +467,42 @@
456 m_pTrackInfo->show();467 m_pTrackInfo->show();
457}468}
458469
470void WTrackTableView::slotNextDlgTagFetcher() {
471 QModelIndex nextRow = currentTrackInfoIndex.sibling(
472 currentTrackInfoIndex.row()+1, currentTrackInfoIndex.column());
473 if (nextRow.isValid())
474 showDlgTagFetcher(nextRow);
475}
476
477void WTrackTableView::slotPrevDlgTagFetcher() {
478 QModelIndex prevRow = currentTrackInfoIndex.sibling(
479 currentTrackInfoIndex.row()-1, currentTrackInfoIndex.column());
480 if (prevRow.isValid())
481 showDlgTagFetcher(prevRow);
482}
483
484void WTrackTableView::showDlgTagFetcher(QModelIndex index) {
485 TrackModel* trackModel = getTrackModel();
486
487 if (!trackModel) {
488 return;
489 }
490
491 TrackPointer pTrack = trackModel->getTrack(index);
492 // NULL is fine
493 m_DlgTagFetcher.init(pTrack);
494 currentTrackInfoIndex = index;
495 m_DlgTagFetcher.show();
496}
497
498void WTrackTableView::slotShowDlgTagFetcher(){
499 QModelIndexList indices = selectionModel()->selectedRows();
500
501 if (indices.size() > 0) {
502 showDlgTagFetcher(indices[0]);
503 }
504}
505
459void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {506void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
460 QModelIndexList indices = selectionModel()->selectedRows();507 QModelIndexList indices = selectionModel()->selectedRows();
461508
@@ -613,8 +660,10 @@
613 m_pMenu->addSeparator();660 m_pMenu->addSeparator();
614 if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_RELOADMETADATA)) {661 if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_RELOADMETADATA)) {
615 m_pMenu->addAction(m_pReloadMetadataAct);662 m_pMenu->addAction(m_pReloadMetadataAct);
663 m_pReloadMetadataFromMusicBrainzAct->setEnabled(oneSongSelected);
664 m_pMenu->addAction(m_pReloadMetadataFromMusicBrainzAct);
616 }665 }
617 // REMOVE and HIDE should not be at the first menu position to avoid excitedly clicks666 // REMOVE and HIDE should not be at the first menu position to avoid accidental clicks
618 if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_REMOVE)) {667 if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_REMOVE)) {
619 m_pRemoveAct->setEnabled(!locked);668 m_pRemoveAct->setEnabled(!locked);
620 m_pMenu->addAction(m_pRemoveAct);669 m_pMenu->addAction(m_pRemoveAct);
621670
=== modified file 'mixxx/src/widget/wtracktableview.h'
--- mixxx/src/widget/wtracktableview.h 2013-05-21 03:02:42 +0000
+++ mixxx/src/widget/wtracktableview.h 2013-05-22 07:42:27 +0000
@@ -9,6 +9,7 @@
9#include "library/libraryview.h"9#include "library/libraryview.h"
10#include "library/trackmodel.h" // Can't forward declare enums10#include "library/trackmodel.h" // Can't forward declare enums
11#include "widget/wlibrarytableview.h"11#include "widget/wlibrarytableview.h"
12#include "dlgtagfetcher.h"
1213
13class ControlObjectThreadMain;14class ControlObjectThreadMain;
14class DlgTrackInfo;15class DlgTrackInfo;
@@ -44,8 +45,11 @@
44 void slotHide();45 void slotHide();
45 void slotOpenInFileBrowser();46 void slotOpenInFileBrowser();
46 void slotShowTrackInfo();47 void slotShowTrackInfo();
48 void slotShowDlgTagFetcher();
47 void slotNextTrackInfo();49 void slotNextTrackInfo();
50 void slotNextDlgTagFetcher();
48 void slotPrevTrackInfo();51 void slotPrevTrackInfo();
52 void slotPrevDlgTagFetcher();
49 void slotSendToAutoDJ();53 void slotSendToAutoDJ();
50 void slotSendToAutoDJTop();54 void slotSendToAutoDJTop();
51 void slotReloadTrackMetadata();55 void slotReloadTrackMetadata();
@@ -61,6 +65,7 @@
61 private:65 private:
62 void sendToAutoDJ(bool bTop);66 void sendToAutoDJ(bool bTop);
63 void showTrackInfo(QModelIndex index);67 void showTrackInfo(QModelIndex index);
68 void showDlgTagFetcher(QModelIndex index);
64 void createActions();69 void createActions();
65 void dragMoveEvent(QDragMoveEvent * event);70 void dragMoveEvent(QDragMoveEvent * event);
66 void dragEnterEvent(QDragEnterEvent * event);71 void dragEnterEvent(QDragEnterEvent * event);
@@ -81,6 +86,7 @@
81 QSignalMapper m_loadTrackMapper;86 QSignalMapper m_loadTrackMapper;
8287
83 DlgTrackInfo* m_pTrackInfo;88 DlgTrackInfo* m_pTrackInfo;
89 DlgTagFetcher m_DlgTagFetcher;
84 QModelIndex currentTrackInfoIndex;90 QModelIndex currentTrackInfoIndex;
8591
8692
@@ -94,6 +100,7 @@
94100
95 // Reload Track Metadata Action:101 // Reload Track Metadata Action:
96 QAction *m_pReloadMetadataAct;102 QAction *m_pReloadMetadataAct;
103 QAction *m_pReloadMetadataFromMusicBrainzAct;
97104
98 // Load Track to PreviewDeck105 // Load Track to PreviewDeck
99 QAction* m_pAddToPreviewDeck;106 QAction* m_pAddToPreviewDeck;