Merge lp:~l0rdt/mixxx/features_replaygain into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Sean M. Pappalardo
Status: Superseded
Proposed branch: lp:~l0rdt/mixxx/features_replaygain
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Prerequisite: lp:~mixxxdevelopers/mixxx/features_taglib
Diff against target: 3048 lines (+2280/-75)
35 files modified
mixxx/build/depends.py (+5/-1)
mixxx/res/mixxx.qrc (+30/-29)
mixxx/res/schema.xml (+9/-0)
mixxx/src/analyserqueue.cpp (+3/-0)
mixxx/src/analyserrg.cpp (+80/-0)
mixxx/src/analyserrg.h (+29/-0)
mixxx/src/analyserwaveform.cpp (+3/-3)
mixxx/src/analyserwavesummary.cpp (+1/-1)
mixxx/src/dlgpreferences.cpp (+22/-8)
mixxx/src/dlgpreferences.h (+3/-0)
mixxx/src/dlgprefreplaygain.cpp (+129/-0)
mixxx/src/dlgprefreplaygain.h (+43/-0)
mixxx/src/dlgprefreplaygaindlg.ui (+267/-0)
mixxx/src/dlgprefsound.cpp (+399/-0)
mixxx/src/dlgprefsound.h (+79/-0)
mixxx/src/dlgprefsounddlg.ui (+220/-0)
mixxx/src/dlgreplaygaindlg.ui (+237/-0)
mixxx/src/engine/enginepregain.cpp (+46/-11)
mixxx/src/engine/enginepregain.h (+11/-2)
mixxx/src/library/dao/trackdao.cpp (+8/-4)
mixxx/src/library/dao/trackdao.h (+1/-0)
mixxx/src/library/librarytablemodel.cpp (+1/-0)
mixxx/src/library/trackcollection.cpp (+1/-1)
mixxx/src/player.cpp (+9/-1)
mixxx/src/player.h (+1/-0)
mixxx/src/replaygain/replaygain_analysis.c (+431/-0)
mixxx/src/replaygain/replaygain_analysis.h (+58/-0)
mixxx/src/soundsource.cpp (+72/-3)
mixxx/src/soundsource.h (+4/-1)
mixxx/src/soundsourceproxy.cpp (+1/-0)
mixxx/src/trackinfoobject.cpp (+25/-1)
mixxx/src/trackinfoobject.h (+9/-0)
mixxx/src/waveform/waveformrenderbeat.cpp (+2/-0)
mixxx/src/waveform/waveformrendersignal.cpp (+31/-9)
mixxx/src/waveform/waveformrendersignal.h (+10/-0)
To merge this branch: bzr merge lp:~l0rdt/mixxx/features_replaygain
Reviewer Review Type Date Requested Status
Mixxx Development Team Pending
Review via email: mp+38917@code.launchpad.net

This proposal has been superseded by a proposal from 2010-11-07.

Description of the change

In this branch, Vittorio has completed the following work towards completing the ReplayGain support blueprint:

* The ability to load ReplayGain tags using the industry convention from common formats (Xiph, MP4, ID3v2, APE)
* An Analyser to calculate the ReplayGain for a track that does not have ReplayGain tags
* Preferences dialog settings to enable/disable ReplayGain, enable/disable the ReplayGain analyser, and set the normalized gain level
* Engine support for applying the replaygain.

I'm proposing this merge to start the code review of his work.

To post a comment you must log in.
Revision history for this message
Sean M. Pappalardo (pegasus-renegadetech) wrote :

I've asked Vittorio to have this auto-adjust the Gain knob rather than creating a hidden one that would make the on-screen one redundant (and confusing.)

This then puts a dependency on bug #555547 getting fixed (soft-takeover) so controllerists with physical gain knobs don't get any surprises.

lp:~l0rdt/mixxx/features_replaygain updated
2466. By Vittorio Colao

(Maybe) fix a compiler error

2467. By Vittorio Colao

Merged with features_taglib branch

2468. By Vittorio Colao

compile problem fixed.Now it's true ;)

Revision history for this message
Sean M. Pappalardo (pegasus-renegadetech) wrote :

I just tested this branch and it looks good from a UI perspective. I would suggest adding labels for each tick of the initial boost slider. It would also be nice if you added qDebugs at track load time saying if a track is being analyzed for ReplayGain (if the analyzer is enabled,) and what a track's ReplayGain value is, or not present.

I also noticed that the enabled/disabled setting didn't save every time I restarted Mixxx.

Now, as far as max dynamic range in the deck pre-fader signal path after ReplayGain, is it possible to auto-adjust the gain knob so that the highest peak of the song is just below clipping?

Revision history for this message
Vittorio Colao (l0rdt) wrote :

Thanks for your comment Sean, I will take care of all your observations.

About auto-adjusting gain knob for the track to stay below clipping, I am unsure on what you ask:

As for now, channels pregain is auto-adjusted so that two tracks playing in two different decks will play at the same perceived volume. That is very effective if one test it on tracks registered on very different volume levels. If I change the channel pregain to stay under clipping, it will not match anymore replaygain suggested volume. The only way I see to stay under clipping *and* preserve no differences in perceived loudness between tracks is that of acting on master volume and not on channel volume.
As a sketch, the only solution I see is to get track peak from tags and from analyser and try to figure out if the master volume is too high for it.
On the other hand, one may think of applying PeakIndicator routines to an analyser so that clipping is detected chunk by chunk.

In both cases and where possible, I may suggest of not altering pregain or master volume but just pass "PeakIndicator" to controllers/skins at loading track time.

Is this alternative way a possible solution?
thanks for reading,
Vittorio

lp:~l0rdt/mixxx/features_replaygain updated
2469. By Vittorio Colao

Fixed a typo in dlgprefreplaygain.cpp: now Analyser on/off value is saved in the configuration.
 Added some qDebug info about replaygain tags of a track and about analyser status.

2470. By Vittorio Colao

Added extra-coolness in terms of:
* comments in analyserrg.cpp
* old-fashioned LCD display in preferences ui to show current initial pregain boost

2471. By Vittorio Colao

Since it is possible to switch on/off ReplayGain Analyser, I added it to createPrepareViewAnalyserQueue too

2472. By Vittorio Colao

quick fix for #665524

2473. By Vittorio Colao

Merging from lp:mixxx

2474. By Vittorio Colao

merging with trunk

2475. By Vittorio Colao

Adjusting indentation/notations

2476. By Vittorio Colao

Merging again from trunk

2477. By Vittorio Colao

Merging from trunk.

2478. By Vittorio Colao

Adjust Waveform according to gain (see wishlist Bug #367153).
Fixed some mistake.

2479. By Vittorio Colao

Fixing indentation once again.

2480. By Vittorio Colao

I hate myself. Sometimes

2481. By Vittorio Colao

Merging from trunk.

2482. By Vittorio Colao

Fix few things.
Commented out some qDebug().
ReplayGain Analyser is now turned on by default.

2483. By Vittorio Colao

Something I forgot to fix.

2484. By Vittorio Colao

s/RG/ReplayGain/ where needed to improve readibilty.
Moved replaygain dir from src/ to lib/ .

2485. By Vittorio Colao

Added missing dir.

2486. By Vittorio Colao

Smooth fading to ReplayGain gain when a value is found after a track is loaded

2487. By Vittorio Colao

Just to be precise...

2488. By Vittorio Colao

delete LeftChannel and RightChannel in a more appropriate way

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mixxx/build/depends.py'
2--- mixxx/build/depends.py 2010-11-02 19:50:15 +0000
3+++ mixxx/build/depends.py 2010-11-07 16:11:47 +0000
4@@ -315,6 +315,7 @@
5 "dlgprefnomidi.cpp",
6 "dlgprefcontrols.cpp",
7 "dlgprefbpm.cpp",
8+ "dlgprefreplaygain.cpp",
9 "dlgbpmscheme.cpp",
10 "dlgabout.cpp",
11 "dlgprefeq.cpp",
12@@ -354,7 +355,9 @@
13 "engine/cuecontrol.cpp",
14 "engine/readaheadmanager.cpp",
15 "cachingreader.cpp",
16-
17+
18+ "replaygain/replaygain_analysis.c",
19+ "analyserrg.cpp",
20 "analyserqueue.cpp",
21 "analyserwavesummary.cpp",
22 "analyserbpm.cpp",
23@@ -521,6 +524,7 @@
24 build.env.Uic4('dlgprefeqdlg.ui')
25 build.env.Uic4('dlgprefcrossfaderdlg.ui')
26 build.env.Uic4('dlgprefbpmdlg.ui')
27+ build.env.Uic4('dlgprefreplaygaindlg.ui')
28 build.env.Uic4('dlgbpmschemedlg.ui')
29 # build.env.Uic4('dlgbpmtapdlg.ui')
30 build.env.Uic4('dlgprefvinyldlg.ui')
31
32=== removed directory 'mixxx/plugins/build/debian/usr'
33=== removed directory 'mixxx/plugins/build/debian/usr/lib'
34=== added file 'mixxx/res/images/preferences/ic_preferences_replaygain.png'
35Binary files mixxx/res/images/preferences/ic_preferences_replaygain.png 1970-01-01 00:00:00 +0000 and mixxx/res/images/preferences/ic_preferences_replaygain.png 2010-11-07 16:11:47 +0000 differ
36=== modified file 'mixxx/res/mixxx.qrc'
37--- mixxx/res/mixxx.qrc 2010-08-15 13:39:23 +0000
38+++ mixxx/res/mixxx.qrc 2010-11-07 16:11:47 +0000
39@@ -1,31 +1,32 @@
40 <RCC>
41- <qresource prefix="/">
42- <file>html/crates.html</file>
43- <file>html/playlists.html</file>
44- <file>images/mixxx-icon.png</file>
45- <file>images/ic_mixxx_window.png</file>
46- <file>images/templates/logo_mixxx.png</file>
47- <file>images/preferences/ic_preferences_bpmdetect.png</file>
48- <file>images/preferences/ic_preferences_broadcast.png</file>
49- <file>images/preferences/ic_preferences_controllers.png</file>
50- <file>images/preferences/ic_preferences_crossfader.png</file>
51- <file>images/preferences/ic_preferences_effects.png</file>
52- <file>images/preferences/ic_preferences_equalizers.png</file>
53- <file>images/preferences/ic_preferences_interface.png</file>
54- <file>images/preferences/ic_preferences_library.png</file>
55- <file>images/preferences/ic_preferences_recording.png</file>
56- <file>images/preferences/ic_preferences_sampler.png</file>
57- <file>images/preferences/ic_preferences_soundhardware.png</file>
58- <file>images/preferences/ic_preferences_vinyl.png</file>
59- <file>images/library/crates_art.png</file>
60- <file>images/library/ic_library_autodj.png</file>
61- <file>images/library/ic_library_browse.png</file>
62- <file>images/library/ic_library_crates.png</file>
63- <file>images/library/ic_library_itunes.png</file>
64- <file>images/library/ic_library_library.png</file>
65- <file>images/library/ic_library_playlist.png</file>
66- <file>images/library/ic_library_prepare.png</file>
67- <file>images/library/ic_library_promotracks.png</file>
68- <file>images/library/ic_library_rhythmbox.png</file>
69- </qresource>
70+ <qresource prefix="/">
71+ <file>html/crates.html</file>
72+ <file>html/playlists.html</file>
73+ <file>images/mixxx-icon.png</file>
74+ <file>images/ic_mixxx_window.png</file>
75+ <file>images/templates/logo_mixxx.png</file>
76+ <file>images/preferences/ic_preferences_bpmdetect.png</file>
77+ <file>images/preferences/ic_preferences_broadcast.png</file>
78+ <file>images/preferences/ic_preferences_controllers.png</file>
79+ <file>images/preferences/ic_preferences_crossfader.png</file>
80+ <file>images/preferences/ic_preferences_effects.png</file>
81+ <file>images/preferences/ic_preferences_equalizers.png</file>
82+ <file>images/preferences/ic_preferences_interface.png</file>
83+ <file>images/preferences/ic_preferences_library.png</file>
84+ <file>images/preferences/ic_preferences_recording.png</file>
85+ <file>images/preferences/ic_preferences_sampler.png</file>
86+ <file>images/preferences/ic_preferences_soundhardware.png</file>
87+ <file>images/preferences/ic_preferences_vinyl.png</file>
88+ <file>images/preferences/ic_preferences_replaygain.png</file>
89+ <file>images/library/crates_art.png</file>
90+ <file>images/library/ic_library_autodj.png</file>
91+ <file>images/library/ic_library_browse.png</file>
92+ <file>images/library/ic_library_crates.png</file>
93+ <file>images/library/ic_library_itunes.png</file>
94+ <file>images/library/ic_library_library.png</file>
95+ <file>images/library/ic_library_playlist.png</file>
96+ <file>images/library/ic_library_prepare.png</file>
97+ <file>images/library/ic_library_promotracks.png</file>
98+ <file>images/library/ic_library_rhythmbox.png</file>
99+ </qresource>
100 </RCC>
101
102=== modified file 'mixxx/res/schema.xml'
103--- mixxx/res/schema.xml 2010-09-14 14:45:35 +0000
104+++ mixxx/res/schema.xml 2010-11-07 16:11:47 +0000
105@@ -146,4 +146,13 @@
106 ALTER TABLE LibraryHashes ADD COLUMN needs_verification INTEGER DEFAULT 0;
107 </sql>
108 </revision>
109+ <revision version="6">
110+ <description>
111+ Added a ReplayGain Column.
112+ </description>
113+ <sql>
114+ ALTER TABLE library ADD COLUMN replaygain float DEFAULT 0;
115+ </sql>
116+ </revision>
117+
118 </schema>
119
120=== modified file 'mixxx/src/analyserqueue.cpp'
121--- mixxx/src/analyserqueue.cpp 2010-10-18 02:20:34 +0000
122+++ mixxx/src/analyserqueue.cpp 2010-11-07 16:11:47 +0000
123@@ -11,6 +11,7 @@
124 #include "analyserwaveform.h"
125 #include "analyserwavesummary.h"
126 #include "analyserbpm.h"
127+#include "analyserrg.h"
128
129 AnalyserQueue::AnalyserQueue() : m_aq(),
130 m_tioq(),
131@@ -202,6 +203,7 @@
132 ret->addAnalyser(new AnalyserWavesummary());
133 ret->addAnalyser(new AnalyserWaveform());
134 ret->addAnalyser(new AnalyserBPM(_config));
135+ ret->addAnalyser(new AnalyserGain(_config));
136
137 ret->start(QThread::IdlePriority);
138 return ret;
139@@ -211,6 +213,7 @@
140 AnalyserQueue* ret = new AnalyserQueue();
141 ret->addAnalyser(new AnalyserWavesummary());
142 ret->addAnalyser(new AnalyserBPM(_config));
143+ ret->addAnalyser(new AnalyserGain(_config));
144 ret->start(QThread::IdlePriority);
145 return ret;
146 }
147
148=== added file 'mixxx/src/analyserrg.cpp'
149--- mixxx/src/analyserrg.cpp 1970-01-01 00:00:00 +0000
150+++ mixxx/src/analyserrg.cpp 2010-11-07 16:11:47 +0000
151@@ -0,0 +1,80 @@
152+
153+
154+#include <QtDebug>
155+#include <time.h>
156+#include <math.h>
157+
158+#include "trackinfoobject.h"
159+#include "analyserrg.h"
160+#include "replaygain/replaygain_analysis.h"
161+
162+AnalyserGain::AnalyserGain(ConfigObject<ConfigValue> *_config) {
163+ m_pConfigRG = _config;
164+ m_iStepControl = 0;
165+}
166+//TODO: On may think on rewriting replaygain/replagain_analys.* to improve performances. Anyway those willing to do should be sure of
167+// the resulting values to exactly coincide with "classical" replaygain_analysis.* ones.
168+// On the other hand, every other ReplayGain tagger uses exactly these methods so that we do not have problems about
169+// values to coincide.
170+
171+void AnalyserGain::initialise(TrackPointer tio, int sampleRate, int totalSamples) {
172+
173+ bool bAnalyserEnabled = (bool)m_pConfigRG->getValueString(ConfigKey("[ReplayGain]","ReplayGainAnalyserEnabled")).toInt();
174+ float fRG = tio->getRG();
175+ if(totalSamples == 0 || fRG != 0 || !bAnalyserEnabled) {
176+ //qDebug() << "Replaygain Analyser will not start.";
177+ //if (fRG != 0 ) qDebug() << "Found a ReplayGain value of " << 20*log10(fRG) << "dB for track :" <<(tio->getFilename());
178+ return;
179+ }
180+ m_iStepControl = InitGainAnalysis( (long)sampleRate );
181+
182+ // m_iStartTime = clock();
183+}
184+
185+
186+
187+
188+void AnalyserGain::process(const CSAMPLE *pIn, const int iLen) {
189+
190+ if(m_iStepControl!=1) return;
191+
192+ CSAMPLE *m_fLems = new CSAMPLE[(int)(iLen/2)];
193+ CSAMPLE *m_fRems = new CSAMPLE[(int)(iLen/2)];
194+ int iRGCounter = 0;
195+ for(int i=0; i<iLen; i+=2) {
196+ m_fLems[iRGCounter] = pIn[i]*32767;
197+ m_fRems[iRGCounter] = pIn[i+1]*32767;
198+
199+ iRGCounter++;
200+ }
201+
202+ m_iStepControl = AnalyzeSamples(m_fLems,m_fRems,iRGCounter,2);
203+
204+ delete m_fLems;
205+ delete m_fRems;
206+ m_fLems = NULL;
207+ m_fRems = NULL;
208+
209+}
210+
211+
212+
213+
214+void AnalyserGain::finalise(TrackPointer tio) {
215+
216+ if(m_iStepControl!=1) return;
217+
218+ //TODO: We are going to store values as relative peaks so that "0" means that no replaygain has been evaluated.
219+ // This means that we are going to transform from dB to peaks and viceversa.
220+ // One may think to digg into replay_gain code and modify it so that
221+ // it directly sends results as relative peaks.
222+ // In that way there is no need to spend resources in calculating log10 or pow.
223+
224+ float fGain_Result = pow(10,GetTitleGain()/20);
225+ tio->setRG(fGain_Result);
226+ //if(fGain_Result) qDebug() << "ReplayGain Analyser found a ReplayGain value of "<< 20*log10(fGain_Result) << "dB for track " << (tio->getFilename());
227+ m_iStepControl=0;
228+ fGain_Result=0;
229+ //m_iStartTime = clock() - m_iStartTime;
230+ //qDebug() << "AnalyserGain :: Generation took " << double(m_iStartTime) / CLOCKS_PER_SEC << " seconds";
231+}
232
233=== added file 'mixxx/src/analyserrg.h'
234--- mixxx/src/analyserrg.h 1970-01-01 00:00:00 +0000
235+++ mixxx/src/analyserrg.h 2010-11-07 16:11:47 +0000
236@@ -0,0 +1,29 @@
237+/*
238+ * analyserrg.h
239+ *
240+ * Created on: 13/ott/2010
241+ * Author: Vittorio Colao
242+ * */
243+
244+#ifndef ANALYSERRG_H_
245+#define ANALYSERRG_H_
246+
247+#include "analyser.h"
248+#include "configobject.h"
249+
250+class AnalyserGain : public Analyser {
251+
252+public:
253+ AnalyserGain(ConfigObject<ConfigValue> *_config);
254+ void initialise(TrackPointer tio, int sampleRate, int totalSamples);
255+ void process(const CSAMPLE *pIn, const int iLen);
256+ void finalise(TrackPointer tio);
257+
258+private:
259+ int m_iStepControl;
260+ ConfigObject<ConfigValue> *m_pConfigRG;
261+ //int m_iStartTime;
262+};
263+
264+
265+#endif /* ANALYSERRG_H_ */
266
267=== modified file 'mixxx/src/analyserwaveform.cpp'
268--- mixxx/src/analyserwaveform.cpp 2010-10-07 06:37:36 +0000
269+++ mixxx/src/analyserwaveform.cpp 2010-11-07 16:11:47 +0000
270@@ -13,9 +13,9 @@
271
272 void AnalyserWaveform::initialise(TrackPointer tio, int sampleRate, int totalSamples) {
273
274- if(tio->getVisualWaveform() != NULL) {
275- return;
276- }
277+ if(tio->getVisualWaveform() != NULL) {
278+ return;
279+ }
280
281 if(totalSamples == 0) {
282 return; //?
283
284=== modified file 'mixxx/src/analyserwavesummary.cpp'
285--- mixxx/src/analyserwavesummary.cpp 2010-10-07 06:37:36 +0000
286+++ mixxx/src/analyserwavesummary.cpp 2010-11-07 16:11:47 +0000
287@@ -21,7 +21,7 @@
288 Q_UNUSED(sampleRate);
289 // Check if the preview has already been generated
290 const QByteArray* p = tio->getWaveSummary();
291- if(p != NULL && p->size() > 0) {
292+ if(p != NULL && p->size() > 0) {
293 return;
294 }
295
296
297=== modified file 'mixxx/src/dlgpreferences.cpp'
298--- mixxx/src/dlgpreferences.cpp 2010-10-21 01:57:15 +0000
299+++ mixxx/src/dlgpreferences.cpp 2010-11-07 16:11:47 +0000
300@@ -34,6 +34,7 @@
301 #include "dlgprefeq.h"
302 #include "dlgprefcrossfader.h"
303 #include "dlgprefrecord.h"
304+#include "dlgprefreplaygain.h"
305 #include "mixxx.h"
306 #include "midi/mididevicemanager.h"
307 #include "midi/mididevice.h"
308@@ -70,6 +71,7 @@
309 weq = new DlgPrefEQ(this, config);
310 wcrossfader = new DlgPrefCrossfader(this, config);
311 wbpm = new DlgPrefBpm(this, config);
312+ wreplaygain = new DlgPrefReplayGain(this, config);
313 wrecord = new DlgPrefRecord(this, config);
314 #ifdef __VINYLCONTROL__
315 wvinylcontrol = new DlgPrefVinyl(this, soundman, config);
316@@ -91,6 +93,7 @@
317 pagesWidget->addWidget(wcrossfader);
318 pagesWidget->addWidget(wrecord);
319 pagesWidget->addWidget(wbpm);
320+ pagesWidget->addWidget(wreplaygain);
321 #ifdef __VINYLCONTROL__
322 pagesWidget->addWidget(wvinylcontrol);
323 #endif
324@@ -110,12 +113,13 @@
325 connect(this, SIGNAL(closeDlg()), this, SLOT(slotHide()));
326 connect(m_pMidiDeviceManager, SIGNAL(devicesChanged()), this, SLOT(rescanMidi()));
327
328- connect(this, SIGNAL(showDlg()), wsound, SLOT(slotUpdate()));
329- connect(this, SIGNAL(showDlg()), wplaylist, SLOT(slotUpdate()));
330- connect(this, SIGNAL(showDlg()), wcontrols, SLOT(slotUpdate()));
331- connect(this, SIGNAL(showDlg()), weq, SLOT(slotUpdate()));
332- connect(this, SIGNAL(showDlg()),wcrossfader,SLOT(slotUpdate()));
333- connect(this, SIGNAL(showDlg()), wbpm, SLOT(slotUpdate()));
334+ connect(this, SIGNAL(showDlg()), wsound, SLOT(slotUpdate()));
335+ connect(this, SIGNAL(showDlg()), wplaylist, SLOT(slotUpdate()));
336+ connect(this, SIGNAL(showDlg()), wcontrols, SLOT(slotUpdate()));
337+ connect(this, SIGNAL(showDlg()), weq, SLOT(slotUpdate()));
338+ connect(this, SIGNAL(showDlg()),wcrossfader, SLOT(slotUpdate()));
339+ connect(this, SIGNAL(showDlg()), wbpm, SLOT(slotUpdate()));
340+ connect(this, SIGNAL(showDlg()), wreplaygain,SLOT(slotUpdate()));
341
342 connect(this, SIGNAL(showDlg()), wrecord, SLOT(slotUpdate()));
343 #ifdef __VINYLCONTROL__
344@@ -139,8 +143,9 @@
345 connect(buttonBox, SIGNAL(accepted()), weq, SLOT(slotApply()));
346 connect(buttonBox, SIGNAL(accepted()),wcrossfader,SLOT(slotApply()));
347 connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotApply()));
348- connect(buttonBox, SIGNAL(accepted()), wbpm, SLOT(slotApply()));
349- connect(buttonBox, SIGNAL(accepted()), wrecord, SLOT(slotApply()));
350+ connect(buttonBox, SIGNAL(accepted()), wbpm, SLOT(slotApply()));
351+ connect(buttonBox, SIGNAL(accepted()),wreplaygain,SLOT(slotApply()));
352+ connect(buttonBox, SIGNAL(accepted()), wrecord, SLOT(slotApply()));
353 #ifdef __SHOUTCAST__
354 connect(buttonBox, SIGNAL(accepted()), wshoutcast,SLOT(slotApply()));
355 #endif
356@@ -223,6 +228,12 @@
357 m_pBPMdetectButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter);
358 m_pBPMdetectButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
359
360+ m_pReplayGainButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type);
361+ m_pReplayGainButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_replaygain.png"));
362+ m_pReplayGainButton->setText(0, tr("Normalization"));
363+ m_pReplayGainButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter);
364+ m_pReplayGainButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
365+
366 #ifdef __VINYLCONTROL__
367 m_pVinylControlButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type);
368 //QT screws up my nice vinyl svg for some reason, so we'll use a PNG version
369@@ -265,6 +276,9 @@
370 pagesWidget->setCurrentWidget(wrecord);
371 else if (current == m_pBPMdetectButton)
372 pagesWidget->setCurrentWidget(wbpm);
373+ else if (current == m_pReplayGainButton)
374+ pagesWidget->setCurrentWidget(wreplaygain);
375+
376 #ifdef __VINYLCONTROL__
377 else if (current == m_pVinylControlButton)
378 pagesWidget->setCurrentWidget(wvinylcontrol);
379
380=== modified file 'mixxx/src/dlgpreferences.h'
381--- mixxx/src/dlgpreferences.h 2010-10-21 01:57:15 +0000
382+++ mixxx/src/dlgpreferences.h 2010-11-07 16:11:47 +0000
383@@ -43,6 +43,7 @@
384 class DlgPrefBpm;
385 class DlgPrefVinyl;
386 class DlgPrefShoutcast;
387+class DlgPrefReplayGain;
388 class PowerMate;
389 class MidiDeviceManager;
390 class SkinLoader;
391@@ -86,6 +87,7 @@
392 DlgPrefBpm *wbpm;
393 DlgPrefVinyl *wvinylcontrol;
394 DlgPrefShoutcast *wshoutcast;
395+ DlgPrefReplayGain *wreplaygain;
396
397 QTreeWidgetItem* m_pSoundButton;
398 QTreeWidgetItem* m_pPlaylistButton;
399@@ -96,6 +98,7 @@
400 QTreeWidgetItem* m_pBPMdetectButton;
401 QTreeWidgetItem* m_pVinylControlButton;
402 QTreeWidgetItem* m_pShoutcastButton;
403+ QTreeWidgetItem* m_pReplayGainButton;
404 QTreeWidgetItem* m_pMIDITreeItem;
405 QList<QTreeWidgetItem*> m_midiBindingsButtons;
406
407
408=== added file 'mixxx/src/dlgprefreplaygain.cpp'
409--- mixxx/src/dlgprefreplaygain.cpp 1970-01-01 00:00:00 +0000
410+++ mixxx/src/dlgprefreplaygain.cpp 2010-11-07 16:11:47 +0000
411@@ -0,0 +1,129 @@
412+
413+#include <qlineedit.h>
414+#include <qwidget.h>
415+#include <qcheckbox.h>
416+#include <qlabel.h>
417+#include <qstring.h>
418+#include <qpushbutton.h>
419+#include <qlcdnumber.h>
420+#include <qslider.h>
421+#include <QtCore>
422+#include <QMessageBox>
423+#include "controlobject.h"
424+
425+#include "dlgprefreplaygain.h"
426+
427+#define CONFIG_KEY "[ReplayGain]"
428+
429+
430+DlgPrefReplayGain::DlgPrefReplayGain(QWidget * parent, ConfigObject<ConfigValue> * _config) : QWidget(parent), Ui::DlgPrefReplayGainDlg()
431+{
432+
433+ config = _config;
434+
435+
436+ setupUi(this);
437+
438+ //Connections
439+ connect(EnableGain, SIGNAL(stateChanged(int)), this, SLOT(slotSetRGEnabled()));
440+ connect(EnableAnalyser, SIGNAL(stateChanged(int)), this, SLOT(slotSetRGAnalyserEnabled()));
441+ connect(SliderBoost, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateBoost()));
442+ connect(SliderBoost, SIGNAL(sliderReleased()), this, SLOT(slotApply()));
443+ connect(PushButtonReset, SIGNAL(clicked(bool)), this, SLOT(setDefaults()));
444+
445+ loadSettings();
446+}
447+
448+DlgPrefReplayGain::~DlgPrefReplayGain()
449+{
450+}
451+
452+void DlgPrefReplayGain::loadSettings()
453+{
454+ if(config->getValueString(ConfigKey(CONFIG_KEY,"ReplayGainEnabled"))==QString(""))
455+ setDefaults();
456+ else
457+ {
458+ SliderBoost->setValue(config->getValueString(ConfigKey(CONFIG_KEY, "InitialReplayGainBoost")).toInt());
459+ lcddB->display(config->getValueString(ConfigKey(CONFIG_KEY, "InitialReplayGainBoost")).toInt());
460+ EnableGain->setChecked(false);
461+ if(config->getValueString(ConfigKey(CONFIG_KEY, "ReplayGainEnabled")).toInt()==1)EnableGain->setChecked(true);
462+ EnableAnalyser->setChecked(false);
463+ if(config->getValueString(ConfigKey(CONFIG_KEY, "ReplayGainAnalyserEnabled")).toInt())EnableAnalyser->setChecked(true);
464+ }
465+ slotUpdate();
466+ slotApply();
467+}
468+
469+void DlgPrefReplayGain::setDefaults()
470+{
471+ EnableGain->setChecked(true);
472+ // Turn ReplayGain Analyser on by default as it does not give appreciable delay on recent hardware (<5 years old).
473+ EnableAnalyser->setChecked(true);
474+ SliderBoost->setValue(6);
475+ lcddB -> display(6);
476+ slotUpdate();
477+ slotApply();
478+}
479+
480+
481+void DlgPrefReplayGain::slotSetRGEnabled()
482+{
483+ if (EnableGain->isChecked())
484+ config->set(ConfigKey(CONFIG_KEY,"ReplayGainEnabled"), ConfigValue(1));
485+
486+ else
487+ {
488+ config->set(ConfigKey(CONFIG_KEY,"ReplayGainEnabled"), ConfigValue(0));
489+ config->set(ConfigKey(CONFIG_KEY,"ReplayGainAnalyserEnabled"), ConfigValue(0));
490+ }
491+
492+
493+ slotUpdate();
494+ slotApply();
495+}
496+
497+void DlgPrefReplayGain::slotSetRGAnalyserEnabled()
498+{
499+ if (EnableAnalyser->isChecked())
500+ config->set(ConfigKey(CONFIG_KEY,"ReplayGainAnalyserEnabled"), ConfigValue(1));
501+ else
502+ config->set(ConfigKey(CONFIG_KEY,"ReplayGainAnalyserEnabled"), ConfigValue(0));
503+ slotApply();
504+}
505+
506+
507+void DlgPrefReplayGain::slotUpdateBoost()
508+{
509+ config->set(ConfigKey(CONFIG_KEY, "InitialReplayGainBoost"), ConfigValue(SliderBoost->value()));
510+ slotApply();
511+}
512+
513+
514+void DlgPrefReplayGain::slotUpdate()
515+{
516+ if (config->getValueString(ConfigKey(CONFIG_KEY,"ReplayGainEnabled")).toInt()==1)
517+ {
518+ EnableAnalyser->setEnabled(true);
519+ SliderBoost->setEnabled(true);
520+ }
521+ else
522+ {
523+ EnableAnalyser->setChecked(false);
524+ EnableAnalyser->setEnabled(false);
525+ SliderBoost->setValue(0);
526+ SliderBoost->setEnabled(false);
527+ lcddB -> display(0);
528+ }
529+}
530+
531+void DlgPrefReplayGain::slotApply()
532+{
533+ ControlObject::getControl(ConfigKey(CONFIG_KEY, "InitialReplayGainBoost"))->set(SliderBoost->value());
534+ int iRGenabled = 0;
535+ int iRGAnalyserEnabled = 0;
536+ if (EnableGain->isChecked()) iRGenabled = 1;
537+ if (EnableAnalyser->isChecked()) iRGAnalyserEnabled = 1;
538+ ControlObject::getControl(ConfigKey(CONFIG_KEY, "ReplayGainEnabled"))->set(iRGenabled);
539+}
540+
541
542=== added file 'mixxx/src/dlgprefreplaygain.h'
543--- mixxx/src/dlgprefreplaygain.h 1970-01-01 00:00:00 +0000
544+++ mixxx/src/dlgprefreplaygain.h 2010-11-07 16:11:47 +0000
545@@ -0,0 +1,43 @@
546+/*
547+ * dlgprefreplaygain.h
548+ *
549+ * Created on: 18/ott/2010
550+ * Author: Vittorio Colao
551+ */
552+
553+#ifndef DLGPREFREPLAYGAIN_H_
554+#define DLGPREFREPLAYGAIN_H_
555+#include "ui_dlgprefreplaygaindlg.h"
556+#include "configobject.h"
557+
558+
559+class QWidget;
560+
561+class DlgPrefReplayGain: public QWidget, public Ui::DlgPrefReplayGainDlg {
562+ Q_OBJECT
563+public:
564+ DlgPrefReplayGain(QWidget *parent, ConfigObject<ConfigValue> *_config);
565+ ~DlgPrefReplayGain();
566+public slots:
567+/** Update initial gain increment */
568+void slotUpdateBoost();
569+void slotSetRGEnabled();
570+void slotSetRGAnalyserEnabled();
571+
572+void slotApply();
573+void slotUpdate();
574+void setDefaults();
575+signals:
576+void apply(const QString &);
577+private:
578+
579+// Determines whether or not to gray out the preferences
580+void loadSettings();
581+
582+/** Pointer to config object */
583+ConfigObject<ConfigValue> *config;
584+
585+};
586+
587+
588+#endif /* DLGPREFREPLAYGAIN_H_ */
589
590=== added file 'mixxx/src/dlgprefreplaygaindlg.ui'
591--- mixxx/src/dlgprefreplaygaindlg.ui 1970-01-01 00:00:00 +0000
592+++ mixxx/src/dlgprefreplaygaindlg.ui 2010-11-07 16:11:47 +0000
593@@ -0,0 +1,267 @@
594+<?xml version="1.0" encoding="UTF-8"?>
595+<ui version="4.0">
596+ <class>DlgPrefReplayGainDlg</class>
597+ <widget class="QWidget" name="DlgPrefReplayGainDlg">
598+ <property name="geometry">
599+ <rect>
600+ <x>0</x>
601+ <y>0</y>
602+ <width>433</width>
603+ <height>446</height>
604+ </rect>
605+ </property>
606+ <property name="windowTitle">
607+ <string>BPM Detection Settings</string>
608+ </property>
609+ <layout class="QVBoxLayout" name="verticalLayout">
610+ <item>
611+ <widget class="QGroupBox" name="groupBox">
612+ <property name="font">
613+ <font>
614+ <weight>50</weight>
615+ <bold>false</bold>
616+ </font>
617+ </property>
618+ <property name="title">
619+ <string>ReplayGain Normalization</string>
620+ </property>
621+ <layout class="QVBoxLayout" name="verticalLayout_2">
622+ <item>
623+ <widget class="QCheckBox" name="EnableGain">
624+ <property name="text">
625+ <string>Enable Replay Gain</string>
626+ </property>
627+ </widget>
628+ </item>
629+ <item>
630+ <widget class="Line" name="line1">
631+ <property name="frameShape">
632+ <enum>QFrame::HLine</enum>
633+ </property>
634+ <property name="frameShadow">
635+ <enum>QFrame::Sunken</enum>
636+ </property>
637+ </widget>
638+ </item>
639+ <item>
640+ <widget class="QCheckBox" name="EnableAnalyser">
641+ <property name="text">
642+ <string>Enable Replay Gain Analyser</string>
643+ </property>
644+ </widget>
645+ </item>
646+ </layout>
647+ </widget>
648+ </item>
649+ <item>
650+ <widget class="QLabel" name="bigfatwarning">
651+ <property name="font">
652+ <font>
653+ <weight>50</weight>
654+ <italic>true</italic>
655+ <bold>false</bold>
656+ <strikeout>false</strikeout>
657+ </font>
658+ </property>
659+ <property name="text">
660+ <string>On old computers the analyser may take some seconds.
661+ Wait until the song is normalized before playing with
662+ pregain and volume.</string>
663+ </property>
664+ </widget>
665+ </item>
666+ <item>
667+ <spacer name="verticalSpacer">
668+ <property name="orientation">
669+ <enum>Qt::Vertical</enum>
670+ </property>
671+ <property name="sizeHint" stdset="0">
672+ <size>
673+ <width>20</width>
674+ <height>40</height>
675+ </size>
676+ </property>
677+ </spacer>
678+ </item>
679+ <item>
680+ <widget class="QGroupBox" name="grpRP">
681+ <property name="minimumSize">
682+ <size>
683+ <width>409</width>
684+ <height>200</height>
685+ </size>
686+ </property>
687+ <property name="title">
688+ <string/>
689+ </property>
690+ <layout class="QGridLayout">
691+ <item row="1" column="0">
692+ <spacer>
693+ <property name="orientation">
694+ <enum>Qt::Vertical</enum>
695+ </property>
696+ <property name="sizeHint" stdset="0">
697+ <size>
698+ <width>20</width>
699+ <height>40</height>
700+ </size>
701+ </property>
702+ </spacer>
703+ </item>
704+ <item row="0" column="0">
705+ <layout class="QVBoxLayout" name="_2">
706+ <item>
707+ <layout class="QHBoxLayout" name="_3">
708+ <item>
709+ <widget class="QLabel" name="label_boost">
710+ <property name="text">
711+ <string>Initial Boost</string>
712+ </property>
713+ </widget>
714+ </item>
715+ <item>
716+ <spacer>
717+ <property name="orientation">
718+ <enum>Qt::Horizontal</enum>
719+ </property>
720+ <property name="sizeHint" stdset="0">
721+ <size>
722+ <width>40</width>
723+ <height>20</height>
724+ </size>
725+ </property>
726+ </spacer>
727+ </item>
728+ <item>
729+ <widget class="QLabel" name="label">
730+ <property name="text">
731+ <string/>
732+ </property>
733+ </widget>
734+ </item>
735+ </layout>
736+ </item>
737+ <item>
738+ <widget class="QSlider" name="SliderBoost">
739+ <property name="toolTip">
740+ <string>Initial Gain Boost</string>
741+ </property>
742+ <property name="locale">
743+ <locale language="C" country="AnyCountry"/>
744+ </property>
745+ <property name="minimum">
746+ <number>0</number>
747+ </property>
748+ <property name="maximum">
749+ <number>15</number>
750+ </property>
751+ <property name="singleStep">
752+ <number>1</number>
753+ </property>
754+ <property name="pageStep">
755+ <number>1</number>
756+ </property>
757+ <property name="value">
758+ <number>6</number>
759+ </property>
760+ <property name="orientation">
761+ <enum>Qt::Horizontal</enum>
762+ </property>
763+ <property name="tickInterval">
764+ <number>5</number>
765+ </property>
766+ </widget>
767+ </item>
768+ <item>
769+ <layout class="QHBoxLayout" name="_4">
770+ <item>
771+ <spacer>
772+ <property name="orientation">
773+ <enum>Qt::Horizontal</enum>
774+ </property>
775+ <property name="sizeHint" stdset="0">
776+ <size>
777+ <width>40</width>
778+ <height>20</height>
779+ </size>
780+ </property>
781+ </spacer>
782+ </item>
783+ <item>
784+ <widget class="QLCDNumber" name="lcddB">
785+ <property name="locale">
786+ <locale language="C" country="AnyCountry"/>
787+ </property>
788+ <property name="numDigits">
789+ <number>2</number>
790+ </property>
791+ <property name="digitCount">
792+ <number>2</number>
793+ </property>
794+ <property name="segmentStyle">
795+ <enum>QLCDNumber::Flat</enum>
796+ </property>
797+ </widget>
798+ </item>
799+ <item>
800+ <widget class="QLabel" name="label_2">
801+ <property name="text">
802+ <string>dB</string>
803+ </property>
804+ </widget>
805+ </item>
806+ </layout>
807+ </item>
808+ </layout>
809+ </item>
810+ <item row="2" column="0">
811+ <layout class="QHBoxLayout" name="_5">
812+ <item>
813+ <spacer>
814+ <property name="orientation">
815+ <enum>Qt::Horizontal</enum>
816+ </property>
817+ <property name="sizeHint" stdset="0">
818+ <size>
819+ <width>40</width>
820+ <height>20</height>
821+ </size>
822+ </property>
823+ </spacer>
824+ </item>
825+ <item>
826+ <widget class="QPushButton" name="PushButtonReset">
827+ <property name="text">
828+ <string>Reset</string>
829+ </property>
830+ </widget>
831+ </item>
832+ </layout>
833+ </item>
834+ </layout>
835+ </widget>
836+ </item>
837+ </layout>
838+ </widget>
839+ <layoutdefault spacing="6" margin="11"/>
840+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
841+ <resources/>
842+ <connections>
843+ <connection>
844+ <sender>SliderBoost</sender>
845+ <signal>sliderMoved(int)</signal>
846+ <receiver>lcddB</receiver>
847+ <slot>display(int)</slot>
848+ <hints>
849+ <hint type="sourcelabel">
850+ <x>220</x>
851+ <y>298</y>
852+ </hint>
853+ <hint type="destinationlabel">
854+ <x>355</x>
855+ <y>331</y>
856+ </hint>
857+ </hints>
858+ </connection>
859+ </connections>
860+</ui>
861
862=== added file 'mixxx/src/dlgprefsound.cpp'
863--- mixxx/src/dlgprefsound.cpp 1970-01-01 00:00:00 +0000
864+++ mixxx/src/dlgprefsound.cpp 2010-10-29 10:38:31 +0000
865@@ -0,0 +1,399 @@
866+/**
867+ * @file dlgprefsound.cpp
868+ * @author Bill Good <bkgood at gmail dot com>
869+ * @date 20100625
870+ */
871+
872+/***************************************************************************
873+ * *
874+ * This program is free software; you can redistribute it and/or modify *
875+ * it under the terms of the GNU General Public License as published by *
876+ * the Free Software Foundation; either version 2 of the License, or *
877+ * (at your option) any later version. *
878+ * *
879+ ***************************************************************************/
880+
881+#include <QDebug>
882+#include <QMessageBox>
883+#include "dlgprefsound.h"
884+#include "dlgprefsounditem.h"
885+#include "soundmanager.h"
886+#include "sounddevice.h"
887+#include "engine/enginemaster.h"
888+
889+/**
890+ * Construct a new sound preferences pane. Initializes and populates all the
891+ * all the controls to the values obtained from SoundManager.
892+ */
893+DlgPrefSound::DlgPrefSound(QWidget *parent, SoundManager *soundManager,
894+ ConfigObject<ConfigValue> *config)
895+ : QWidget(parent)
896+ , m_pSoundManager(soundManager)
897+ , m_pConfig(config)
898+ , m_settingsModified(false)
899+ , m_loading(false)
900+ , m_forceApply(false)
901+ , m_deckCount(0)
902+{
903+ setupUi(this);
904+
905+ connect(m_pSoundManager, SIGNAL(devicesUpdated()),
906+ this, SLOT(refreshDevices()));
907+
908+ applyButton->setEnabled(false);
909+ connect(applyButton, SIGNAL(clicked()),
910+ this, SLOT(slotApply()));
911+
912+ apiComboBox->clear();
913+ apiComboBox->addItem(tr("None"), "None");
914+ updateAPIs();
915+ connect(apiComboBox, SIGNAL(currentIndexChanged(int)),
916+ this, SLOT(apiChanged(int)));
917+
918+ sampleRateComboBox->clear();
919+ foreach (unsigned int srate, m_pSoundManager->getSampleRates()) {
920+ if (srate > 0) {
921+ // no ridiculous sample rate values. prohibiting zero means
922+ // avoiding a potential div-by-0 error in ::updateLatencies
923+ sampleRateComboBox->addItem(QString(tr("%1 Hz")).arg(srate), srate);
924+ }
925+ }
926+ connect(sampleRateComboBox, SIGNAL(currentIndexChanged(int)),
927+ this, SLOT(sampleRateChanged(int)));
928+ connect(sampleRateComboBox, SIGNAL(currentIndexChanged(int)),
929+ this, SLOT(updateLatencies(int)));
930+ connect(latencyComboBox, SIGNAL(currentIndexChanged(int)),
931+ this, SLOT(latencyChanged(int)));
932+
933+ // using math_max to give the signed return value of numChannels a lower
934+ // bound so we can safely stick it in the unsigned deckCount -bkgood
935+ m_deckCount = math_max(m_pSoundManager->getEngine()->numChannels(), 0);
936+
937+ initializePaths();
938+ loadSettings();
939+
940+ connect(apiComboBox, SIGNAL(currentIndexChanged(int)),
941+ this, SLOT(settingChanged()));
942+ connect(sampleRateComboBox, SIGNAL(currentIndexChanged(int)),
943+ this, SLOT(settingChanged()));
944+ connect(latencyComboBox, SIGNAL(currentIndexChanged(int)),
945+ this, SLOT(settingChanged()));
946+
947+ connect(queryButton, SIGNAL(clicked()),
948+ this, SLOT(queryClicked()));
949+ connect(resetButton, SIGNAL(clicked()),
950+ this, SLOT(resetClicked()));
951+}
952+
953+DlgPrefSound::~DlgPrefSound() {
954+
955+}
956+
957+/**
958+ * Slot called when the preferences dialog is opened or this pane is
959+ * selected.
960+ */
961+void DlgPrefSound::slotUpdate() {
962+ // this is unfortunate, because slotUpdate is called every time
963+ // we change to this pane, we lose changed and unapplied settings
964+ // every time. There's no real way around this, just anothe argument
965+ // for a prefs rewrite -- bkgood
966+ loadSettings();
967+ m_settingsModified = false;
968+ applyButton->setEnabled(false);
969+}
970+
971+/**
972+ * Slot called when the Apply or OK button is pressed.
973+ */
974+void DlgPrefSound::slotApply() {
975+ if (!m_settingsModified && !m_forceApply) {
976+ return;
977+ }
978+#ifdef __VINYLCONTROL__
979+ // Scratchlib sucks, throw rocks at it
980+ // XXX(bkgood) HACKS DELETE THIS WHEN SCRATCHLIB GETS NUKED KTHX
981+ if (m_pConfig->getValueString(ConfigKey("[VinylControl]", "strVinylType"))
982+ == MIXXX_VINYL_FINALSCRATCH &&
983+ sampleRateComboBox->itemData(sampleRateComboBox->currentIndex()).toUInt()
984+ != 44100) {
985+ QMessageBox::warning(this, tr("Mixxx Error"),
986+ tr("FinalScratch records currently only work properly with a "
987+ "44100 Hz sample rate.\nThe sample rate has been reset to 44100 Hz."));
988+ sampleRateComboBox->setCurrentIndex(sampleRateComboBox->findData(44100));
989+ }
990+#endif
991+ m_forceApply = false;
992+ m_config.clearInputs();
993+ m_config.clearOutputs();
994+ emit(writePaths(&m_config));
995+ int err = m_pSoundManager->setConfig(m_config);
996+ if (err != OK) {
997+ QString error;
998+ QString deviceName(tr("a device"));
999+ QString detailedError(tr("An unknown error occurred"));
1000+ SoundDevice *device = m_pSoundManager->getErrorDevice();
1001+ if (device != NULL) {
1002+ deviceName = QString(tr("sound device \"%1\"")).arg(device->getDisplayName());
1003+ detailedError = device->getError();
1004+ }
1005+ switch (err) {
1006+ case SOUNDDEVICE_ERROR_DUPLICATE_OUTPUT_CHANNEL:
1007+ error = QString(tr("Two outputs cannot share channels on %1")).arg(deviceName);
1008+ break;
1009+ case SOUNDDEVICE_ERROR_DUPLICATE_INPUT_CHANNEL:
1010+ error = QString(tr("Two inputs cannot share channels on %1")).arg(deviceName);
1011+ break;
1012+ default:
1013+ error = QString(tr("Error opening %1\n%2")).arg(deviceName).arg(detailedError);
1014+ break;
1015+ }
1016+ QMessageBox::warning(NULL, tr("Configuration error"), error);
1017+ }
1018+ m_settingsModified = false;
1019+ applyButton->setEnabled(false);
1020+ loadSettings(); // in case SM decided to change anything it didn't like
1021+}
1022+
1023+/**
1024+ * Slot called by DlgPrefVinyl when it needs slotApply here to call setupDevices.
1025+ * We're graced with this kludge because VC proxies are only initialized in
1026+ * SoundManager::setupDevices and reinit is the only way to make them reread
1027+ * their config.
1028+ */
1029+void DlgPrefSound::forceApply() {
1030+ m_forceApply = true;
1031+}
1032+
1033+/**
1034+ * Initializes (and creates) all the path items. Each path item widget allows
1035+ * the user to input a sound device name and channel number given a description
1036+ * of what will be done with that info. Inputs and outputs are grouped by tab,
1037+ * and each path item has an identifier (Master, Headphones, ...) and an index,
1038+ * if necessary.
1039+ */
1040+void DlgPrefSound::initializePaths() {
1041+ QList<DlgPrefSoundItem*> items;
1042+ foreach (AudioPathType type, AudioOutput::getSupportedTypes()) {
1043+ DlgPrefSoundItem *toInsert;
1044+ if (AudioPath::isIndexed(type)) {
1045+ for (unsigned int i = 0; i < m_deckCount; ++i) {
1046+ toInsert = new DlgPrefSoundItem(outputScrollAreaContents, type,
1047+ m_outputDevices, false, i);
1048+ connect(this, SIGNAL(refreshOutputDevices(const QList<SoundDevice*>&)),
1049+ toInsert, SLOT(refreshDevices(const QList<SoundDevice*>&)));
1050+ outputVLayout->insertWidget(outputVLayout->count() - 1, toInsert);
1051+ items.append(toInsert);
1052+ }
1053+ } else {
1054+ toInsert = new DlgPrefSoundItem(outputScrollAreaContents, type,
1055+ m_outputDevices, false);
1056+ connect(this, SIGNAL(refreshOutputDevices(const QList<SoundDevice*>&)),
1057+ toInsert, SLOT(refreshDevices(const QList<SoundDevice*>&)));
1058+ outputVLayout->insertWidget(outputVLayout->count() - 1, toInsert);
1059+ items.append(toInsert);
1060+ }
1061+ }
1062+ foreach (AudioPathType type, AudioInput::getSupportedTypes()) {
1063+ DlgPrefSoundItem *toInsert;
1064+ if (AudioPath::isIndexed(type)) {
1065+ for (unsigned int i = 0; i < m_deckCount; ++i) {
1066+ toInsert = new DlgPrefSoundItem(inputScrollAreaContents, type,
1067+ m_inputDevices, true, i);
1068+ connect(this, SIGNAL(refreshInputDevices(const QList<SoundDevice*>&)),
1069+ toInsert, SLOT(refreshDevices(const QList<SoundDevice*>&)));
1070+ inputVLayout->insertWidget(inputVLayout->count() - 1, toInsert);
1071+ items.append(toInsert);
1072+ }
1073+ } else {
1074+ toInsert = new DlgPrefSoundItem(inputScrollAreaContents, type,
1075+ m_inputDevices, true);
1076+ connect(this, SIGNAL(refreshInputDevices(const QList<SoundDevice*>&)),
1077+ toInsert, SLOT(refreshDevices(const QList<SoundDevice*>&)));
1078+ inputVLayout->insertWidget(inputVLayout->count() - 1, toInsert);
1079+ items.append(toInsert);
1080+ }
1081+ }
1082+ foreach (DlgPrefSoundItem *item, items) {
1083+ connect(item, SIGNAL(settingChanged()),
1084+ this, SLOT(settingChanged()));
1085+ connect(this, SIGNAL(loadPaths(const SoundManagerConfig&)),
1086+ item, SLOT(loadPath(const SoundManagerConfig&)));
1087+ connect(this, SIGNAL(writePaths(SoundManagerConfig*)),
1088+ item, SLOT(writePath(SoundManagerConfig*)));
1089+ connect(this, SIGNAL(updatingAPI()),
1090+ item, SLOT(save()));
1091+ connect(this, SIGNAL(updatedAPI()),
1092+ item, SLOT(reload()));
1093+ }
1094+}
1095+
1096+/**
1097+ * Convenience overload to load settings from the SoundManagerConfig owned by
1098+ * SoundManager.
1099+ */
1100+void DlgPrefSound::loadSettings() {
1101+ loadSettings(m_pSoundManager->getConfig());
1102+}
1103+
1104+/**
1105+ * Loads the settings in the given SoundManagerConfig into the dialog.
1106+ */
1107+void DlgPrefSound::loadSettings(const SoundManagerConfig &config) {
1108+ m_loading = true; // so settingsChanged ignores all our modifications here
1109+ m_config = config;
1110+ int apiIndex = apiComboBox->findData(m_config.getAPI());
1111+ if (apiIndex != -1) {
1112+ apiComboBox->setCurrentIndex(apiIndex);
1113+ }
1114+ int sampleRateIndex = sampleRateComboBox->findData(m_config.getSampleRate());
1115+ if (sampleRateIndex != -1) {
1116+ sampleRateComboBox->setCurrentIndex(sampleRateIndex);
1117+ }
1118+ int latencyIndex = latencyComboBox->findData(m_config.getLatency());
1119+ if (latencyIndex != -1) {
1120+ latencyComboBox->setCurrentIndex(latencyIndex);
1121+ }
1122+ emit(loadPaths(m_config));
1123+ m_loading = false;
1124+}
1125+
1126+/**
1127+ * Slot called when the user selects a different API, or the
1128+ * software changes it programatically (for instance, when it
1129+ * loads a value from SoundManager). Refreshes the device lists
1130+ * for the new API and pushes those to the path items.
1131+ */
1132+void DlgPrefSound::apiChanged(int index) {
1133+ m_config.setAPI(apiComboBox->itemData(index).toString());
1134+ refreshDevices();
1135+ // JACK sets its own latency
1136+ if (m_config.getAPI() == MIXXX_PORTAUDIO_JACK_STRING) {
1137+ latencyLabel->setEnabled(false);
1138+ latencyComboBox->setEnabled(false);
1139+ } else {
1140+ latencyLabel->setEnabled(true);
1141+ latencyComboBox->setEnabled(true);
1142+ }
1143+}
1144+
1145+/**
1146+ * Updates the list of APIs, trying to keep the API and device selections
1147+ * constant if possible.
1148+ */
1149+void DlgPrefSound::updateAPIs() {
1150+ QString currentAPI(apiComboBox->itemData(apiComboBox->currentIndex()).toString());
1151+ emit(updatingAPI());
1152+ while (apiComboBox->count() > 1) {
1153+ apiComboBox->removeItem(apiComboBox->count() - 1);
1154+ }
1155+ foreach (QString api, m_pSoundManager->getHostAPIList()) {
1156+ apiComboBox->addItem(api, api);
1157+ }
1158+ int newIndex = apiComboBox->findData(currentAPI);
1159+ if (newIndex > -1) {
1160+ apiComboBox->setCurrentIndex(newIndex);
1161+ }
1162+ emit(updatedAPI());
1163+}
1164+
1165+/**
1166+ * Slot called when the sample rate combo box changes to update the
1167+ * sample rate in the config.
1168+ */
1169+void DlgPrefSound::sampleRateChanged(int index) {
1170+ m_config.setSampleRate(
1171+ sampleRateComboBox->itemData(index).toUInt());
1172+}
1173+
1174+/**
1175+ * Slot called when the latency combo box is changed to update the
1176+ * latency in the config.
1177+ */
1178+void DlgPrefSound::latencyChanged(int index) {
1179+ m_config.setLatency(
1180+ latencyComboBox->itemData(index).toUInt());
1181+}
1182+
1183+/**
1184+ * Slot called whenever the selected sample rate is changed. Populates the
1185+ * latency input box with MAX_LATENCY values, starting at 1ms, representing
1186+ * a number of frames per buffer, which will always be a power of 2 (so the
1187+ * values displayed in ms won't be constant between sample rates, but they'll
1188+ * be close).
1189+ */
1190+void DlgPrefSound::updateLatencies(int sampleRateIndex) {
1191+ double sampleRate = sampleRateComboBox->itemData(sampleRateIndex).toDouble();
1192+ int oldLatency = latencyComboBox->currentIndex();
1193+ unsigned int framesPerBuffer = 1; // start this at 0 and inf loop happens
1194+ // we don't want to display any sub-1ms latencies (well maybe we do but I
1195+ // don't right now!), so we iterate over all the buffer sizes until we
1196+ // find the first that gives us a latency >= 1 ms -- bkgood
1197+ // no div-by-0 in the next line because we don't allow srates of 0 in our
1198+ // srate list when we construct it in the ctor -- bkgood
1199+ for (; framesPerBuffer / sampleRate * 1000 < 1.0; framesPerBuffer *= 2);
1200+ latencyComboBox->clear();
1201+ for (unsigned int i = 0; i < MAX_LATENCY; ++i) {
1202+ unsigned int latency = framesPerBuffer / sampleRate * 1000;
1203+ // i + 1 in the next line is a latency index as described in SSConfig
1204+ latencyComboBox->addItem(QString(tr("%1 ms")).arg(latency), i + 1);
1205+ framesPerBuffer <<= 1; // *= 2
1206+ }
1207+ if (oldLatency < latencyComboBox->count() && oldLatency >= 0) {
1208+ latencyComboBox->setCurrentIndex(oldLatency);
1209+ } else {
1210+ // set it to the max, let the user dig if they need better latency. better
1211+ // than having a user get the pops on first use and thinking poorly of mixxx
1212+ // because of it -- bkgood
1213+ latencyComboBox->setCurrentIndex(latencyComboBox->count() - 1);
1214+ }
1215+}
1216+
1217+/**
1218+ * Slot called when device lists go bad to refresh them, or the API
1219+ * just changes and we need to display new devices.
1220+ */
1221+void DlgPrefSound::refreshDevices() {
1222+ if (m_config.getAPI() == "None") {
1223+ m_outputDevices.clear();
1224+ m_inputDevices.clear();
1225+ } else {
1226+ m_outputDevices =
1227+ m_pSoundManager->getDeviceList(m_config.getAPI(), true, false);
1228+ m_inputDevices =
1229+ m_pSoundManager->getDeviceList(m_config.getAPI(), false, true);
1230+ }
1231+ emit(refreshOutputDevices(m_outputDevices));
1232+ emit(refreshInputDevices(m_inputDevices));
1233+}
1234+
1235+/**
1236+ * Called when any of the combo boxes in this dialog are changed. Enables the
1237+ * apply button and marks that settings have been changed so that
1238+ * DlgPrefSound::slotApply knows to apply them.
1239+ */
1240+void DlgPrefSound::settingChanged() {
1241+ if (m_loading) return; // doesn't count if we're just loading prefs
1242+ m_settingsModified = true;
1243+ if (!applyButton->isEnabled()) {
1244+ applyButton->setEnabled(true);
1245+ }
1246+}
1247+
1248+/**
1249+ * Slot called when the "Query Devices" button is clicked.
1250+ */
1251+void DlgPrefSound::queryClicked() {
1252+ m_pSoundManager->queryDevices();
1253+ updateAPIs();
1254+}
1255+
1256+/**
1257+ * Slot called when the "Reset to Defaults" button is clicked.
1258+ */
1259+void DlgPrefSound::resetClicked() {
1260+ SoundManagerConfig newConfig;
1261+ newConfig.loadDefaults(m_pSoundManager, SoundManagerConfig::ALL);
1262+ loadSettings(newConfig);
1263+ settingChanged(); // force the apply button to enable
1264+}
1265
1266=== added file 'mixxx/src/dlgprefsound.h'
1267--- mixxx/src/dlgprefsound.h 1970-01-01 00:00:00 +0000
1268+++ mixxx/src/dlgprefsound.h 2010-08-15 07:24:02 +0000
1269@@ -0,0 +1,79 @@
1270+/**
1271+ * @file dlgprefsound.h
1272+ * @author Bill Good <bkgood at gmail dot com>
1273+ * @date 20100625
1274+ */
1275+
1276+/***************************************************************************
1277+ * *
1278+ * This program is free software; you can redistribute it and/or modify *
1279+ * it under the terms of the GNU General Public License as published by *
1280+ * the Free Software Foundation; either version 2 of the License, or *
1281+ * (at your option) any later version. *
1282+ * *
1283+ ***************************************************************************/
1284+
1285+#ifndef DLGPREFSOUND_H
1286+#define DLGPREFSOUND_H
1287+
1288+#include <QtCore>
1289+#include "ui_dlgprefsounddlg.h"
1290+#include "configobject.h"
1291+#include "soundmanagerconfig.h"
1292+
1293+class SoundManager;
1294+class ControlObject;
1295+class SoundDevice;
1296+
1297+/*
1298+ * TODO(bkgood) (n-decks) establish a signal/slot connection with a signal
1299+ * on EngineMaster that emits every time a channel is added, and a slot here
1300+ * that updates the dialog accordingly.
1301+ */
1302+
1303+/**
1304+ * Class representing a preferences pane to configure sound devices for Mixxx.
1305+ */
1306+class DlgPrefSound : public QWidget, public Ui::DlgPrefSoundDlg {
1307+ Q_OBJECT;
1308+public:
1309+ DlgPrefSound(QWidget *parent, SoundManager *soundManager,
1310+ ConfigObject<ConfigValue> *config);
1311+ ~DlgPrefSound();
1312+signals:
1313+ void loadPaths(const SoundManagerConfig &config);
1314+ void writePaths(SoundManagerConfig *config);
1315+ void refreshOutputDevices(const QList<SoundDevice*> &devices);
1316+ void refreshInputDevices(const QList<SoundDevice*> &devices);
1317+ void updatingAPI();
1318+ void updatedAPI();
1319+public slots:
1320+ void slotUpdate(); // called on show
1321+ void slotApply(); // called on ok button
1322+ void forceApply(); // called by DlgPrefVinyl to make slotApply call setupDevices
1323+private:
1324+ void initializePaths();
1325+ void loadSettings();
1326+ void loadSettings(const SoundManagerConfig &config);
1327+ SoundManager *m_pSoundManager;
1328+ ConfigObject<ConfigValue> *m_pConfig;
1329+ QList<SoundDevice*> m_inputDevices;
1330+ QList<SoundDevice*> m_outputDevices;
1331+ bool m_settingsModified;
1332+ SoundManagerConfig m_config;
1333+ bool m_loading;
1334+ bool m_forceApply;
1335+ unsigned int m_deckCount;
1336+private slots:
1337+ void apiChanged(int index);
1338+ void updateAPIs();
1339+ void sampleRateChanged(int index);
1340+ void latencyChanged(int index);
1341+ void updateLatencies(int sampleRateIndex);
1342+ void refreshDevices();
1343+ void settingChanged();
1344+ void queryClicked();
1345+ void resetClicked();
1346+};
1347+
1348+#endif
1349
1350=== added file 'mixxx/src/dlgprefsounddlg.ui'
1351--- mixxx/src/dlgprefsounddlg.ui 1970-01-01 00:00:00 +0000
1352+++ mixxx/src/dlgprefsounddlg.ui 2010-07-27 01:34:13 +0000
1353@@ -0,0 +1,220 @@
1354+<?xml version="1.0" encoding="UTF-8"?>
1355+<ui version="4.0">
1356+ <class>DlgPrefSoundDlg</class>
1357+ <widget class="QWidget" name="DlgPrefSoundDlg">
1358+ <property name="geometry">
1359+ <rect>
1360+ <x>0</x>
1361+ <y>0</y>
1362+ <width>520</width>
1363+ <height>517</height>
1364+ </rect>
1365+ </property>
1366+ <layout class="QVBoxLayout" name="verticalLayout_2">
1367+ <item>
1368+ <layout class="QVBoxLayout" name="settingsVLayout">
1369+ <item>
1370+ <layout class="QHBoxLayout" name="apiHLayout">
1371+ <item>
1372+ <widget class="QLabel" name="apiLabel">
1373+ <property name="text">
1374+ <string>Sound API</string>
1375+ </property>
1376+ </widget>
1377+ </item>
1378+ <item>
1379+ <widget class="QComboBox" name="apiComboBox"/>
1380+ </item>
1381+ </layout>
1382+ </item>
1383+ <item>
1384+ <layout class="QHBoxLayout" name="sampleRateHLayout">
1385+ <item>
1386+ <widget class="QLabel" name="sampleRateLabel">
1387+ <property name="text">
1388+ <string>Sample Rate</string>
1389+ </property>
1390+ </widget>
1391+ </item>
1392+ <item>
1393+ <widget class="QComboBox" name="sampleRateComboBox"/>
1394+ </item>
1395+ </layout>
1396+ </item>
1397+ <item>
1398+ <layout class="QHBoxLayout" name="latencyHLayout">
1399+ <item>
1400+ <widget class="QLabel" name="latencyLabel">
1401+ <property name="text">
1402+ <string>Latency</string>
1403+ </property>
1404+ </widget>
1405+ </item>
1406+ <item>
1407+ <widget class="QComboBox" name="latencyComboBox"/>
1408+ </item>
1409+ </layout>
1410+ </item>
1411+ </layout>
1412+ </item>
1413+ <item>
1414+ <widget class="QGroupBox" name="latencyTipsGroupBox">
1415+ <property name="title">
1416+ <string>Latency Tips</string>
1417+ </property>
1418+ <layout class="QVBoxLayout" name="verticalLayout">
1419+ <item>
1420+ <widget class="QLabel" name="latencyTipsLabel">
1421+ <property name="text">
1422+ <string>&lt;ul&gt;
1423+&lt;li&gt;Increase your latency if you hear pops during playback&lt;/li&gt;
1424+&lt;li&gt;Reduce your latency to improve Mixxx's responsiveness&lt;/li&gt;
1425+&lt;/ul&gt;</string>
1426+ </property>
1427+ </widget>
1428+ </item>
1429+ </layout>
1430+ </widget>
1431+ </item>
1432+ <item>
1433+ <widget class="QTabWidget" name="ioTabs">
1434+ <property name="currentIndex">
1435+ <number>0</number>
1436+ </property>
1437+ <widget class="QWidget" name="outputTab">
1438+ <attribute name="title">
1439+ <string>Output</string>
1440+ </attribute>
1441+ <layout class="QVBoxLayout" name="verticalLayout_3">
1442+ <item>
1443+ <widget class="QScrollArea" name="outputScrollArea">
1444+ <property name="frameShape">
1445+ <enum>QFrame::NoFrame</enum>
1446+ </property>
1447+ <property name="widgetResizable">
1448+ <bool>true</bool>
1449+ </property>
1450+ <widget class="QWidget" name="outputScrollAreaContents">
1451+ <property name="geometry">
1452+ <rect>
1453+ <x>0</x>
1454+ <y>0</y>
1455+ <width>496</width>
1456+ <height>271</height>
1457+ </rect>
1458+ </property>
1459+ <layout class="QVBoxLayout" name="outputVLayout">
1460+ <property name="margin">
1461+ <number>0</number>
1462+ </property>
1463+ <item>
1464+ <spacer name="outputVSpacer">
1465+ <property name="orientation">
1466+ <enum>Qt::Vertical</enum>
1467+ </property>
1468+ <property name="sizeHint" stdset="0">
1469+ <size>
1470+ <width>20</width>
1471+ <height>167</height>
1472+ </size>
1473+ </property>
1474+ </spacer>
1475+ </item>
1476+ </layout>
1477+ </widget>
1478+ </widget>
1479+ </item>
1480+ </layout>
1481+ </widget>
1482+ <widget class="QWidget" name="inputTab">
1483+ <attribute name="title">
1484+ <string>Input</string>
1485+ </attribute>
1486+ <layout class="QVBoxLayout" name="verticalLayout_4">
1487+ <item>
1488+ <widget class="QScrollArea" name="inputScrollArea">
1489+ <property name="frameShape">
1490+ <enum>QFrame::NoFrame</enum>
1491+ </property>
1492+ <property name="widgetResizable">
1493+ <bool>true</bool>
1494+ </property>
1495+ <widget class="QWidget" name="inputScrollAreaContents">
1496+ <property name="geometry">
1497+ <rect>
1498+ <x>0</x>
1499+ <y>0</y>
1500+ <width>496</width>
1501+ <height>271</height>
1502+ </rect>
1503+ </property>
1504+ <layout class="QVBoxLayout" name="inputVLayout">
1505+ <property name="margin">
1506+ <number>0</number>
1507+ </property>
1508+ <item>
1509+ <spacer name="inputVSpacer">
1510+ <property name="orientation">
1511+ <enum>Qt::Vertical</enum>
1512+ </property>
1513+ <property name="sizeHint" stdset="0">
1514+ <size>
1515+ <width>20</width>
1516+ <height>40</height>
1517+ </size>
1518+ </property>
1519+ </spacer>
1520+ </item>
1521+ </layout>
1522+ </widget>
1523+ </widget>
1524+ </item>
1525+ </layout>
1526+ </widget>
1527+ </widget>
1528+ </item>
1529+ <item>
1530+ <layout class="QHBoxLayout" name="buttonsHLayout">
1531+ <item>
1532+ <widget class="QPushButton" name="queryButton">
1533+ <property name="text">
1534+ <string>Query Devices</string>
1535+ </property>
1536+ </widget>
1537+ </item>
1538+ <item>
1539+ <widget class="QPushButton" name="resetButton">
1540+ <property name="text">
1541+ <string>Reset to Defaults</string>
1542+ </property>
1543+ </widget>
1544+ </item>
1545+ <item>
1546+ <spacer name="buttonsHSpacer">
1547+ <property name="orientation">
1548+ <enum>Qt::Horizontal</enum>
1549+ </property>
1550+ <property name="sizeHint" stdset="0">
1551+ <size>
1552+ <width>40</width>
1553+ <height>20</height>
1554+ </size>
1555+ </property>
1556+ </spacer>
1557+ </item>
1558+ <item>
1559+ <widget class="QPushButton" name="applyButton">
1560+ <property name="text">
1561+ <string>Apply</string>
1562+ </property>
1563+ </widget>
1564+ </item>
1565+ </layout>
1566+ </item>
1567+ </layout>
1568+ </widget>
1569+ <layoutdefault spacing="6" margin="11"/>
1570+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
1571+ <resources/>
1572+ <connections/>
1573+</ui>
1574
1575=== added file 'mixxx/src/dlgreplaygaindlg.ui'
1576--- mixxx/src/dlgreplaygaindlg.ui 1970-01-01 00:00:00 +0000
1577+++ mixxx/src/dlgreplaygaindlg.ui 2010-11-07 16:11:47 +0000
1578@@ -0,0 +1,237 @@
1579+<?xml version="1.0" encoding="UTF-8"?>
1580+<ui version="4.0">
1581+ <class>DlgPrefReplayGainDlg</class>
1582+ <widget class="QWidget" name="DlgPrefReplayGainDlg">
1583+ <property name="geometry">
1584+ <rect>
1585+ <x>0</x>
1586+ <y>0</y>
1587+ <width>409</width>
1588+ <height>418</height>
1589+ </rect>
1590+ </property>
1591+ <property name="sizePolicy">
1592+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
1593+ <horstretch>0</horstretch>
1594+ <verstretch>0</verstretch>
1595+ </sizepolicy>
1596+ </property>
1597+ <property name="windowTitle">
1598+ <string>Form1</string>
1599+ </property>
1600+ <layout class="QVBoxLayout">
1601+ <item>
1602+ <widget class="Q3GroupBox" name="GroupBox1">
1603+ <property name="enabled">
1604+ <bool>true</bool>
1605+ </property>
1606+ <property name="sizePolicy">
1607+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
1608+ <horstretch>0</horstretch>
1609+ <verstretch>0</verstretch>
1610+ </sizepolicy>
1611+ </property>
1612+ <property name="font">
1613+ <font/>
1614+ </property>
1615+ <property name="title">
1616+ <string>Replay Gain Normalization</string>
1617+ </property>
1618+ <layout class="QVBoxLayout">
1619+ <item>
1620+ <widget class="QCheckBox" name="EnableGain">
1621+ <property name="text">
1622+ <string>Enable Replay Gain</string>
1623+ </property>
1624+ </widget>
1625+ </item>
1626+ <item>
1627+ <widget class="Line" name="line">
1628+ <property name="orientation">
1629+ <enum>Qt::Horizontal</enum>
1630+ </property>
1631+ </widget>
1632+ </item>
1633+ <item>
1634+ <widget class="QCheckBox" name="EnableAnalyser">
1635+ <property name="text">
1636+ <string>Enable Replay Gain Analyser</string>
1637+ </property>
1638+ </widget>
1639+ </item>
1640+ <item>
1641+ <widget class="QRadioButton" name="AffectVolume">
1642+ <property name="text">
1643+ <string>Affect Desk Volume</string>
1644+ </property>
1645+ </widget>
1646+ </item>
1647+ <item>
1648+ <widget class="QRadioButton" name="AffectGain">
1649+ <property name="text">
1650+ <string>Affect Desk Pregain</string>
1651+ </property>
1652+ </widget>
1653+ </item>
1654+ <item>
1655+ <layout class="QHBoxLayout">
1656+ <item>
1657+ <layout class="QVBoxLayout">
1658+ <item>
1659+ <layout class="QHBoxLayout">
1660+ <item>
1661+ <widget class="QLabel" name="label_2">
1662+ <property name="text">
1663+ <string>Initial Boost</string>
1664+ </property>
1665+ </widget>
1666+ </item>
1667+ <item>
1668+ <spacer>
1669+ <property name="orientation">
1670+ <enum>Qt::Horizontal</enum>
1671+ </property>
1672+ <property name="sizeHint" stdset="0">
1673+ <size>
1674+ <width>40</width>
1675+ <height>20</height>
1676+ </size>
1677+ </property>
1678+ </spacer>
1679+ </item>
1680+ <item>
1681+ <widget class="QLabel" name="label">
1682+ <property name="text">
1683+ <string/>
1684+ </property>
1685+ </widget>
1686+ </item>
1687+ </layout>
1688+ </item>
1689+ <item>
1690+ <widget class="QSlider" name="SliderBoost">
1691+ <property name="minimum">
1692+ <number>0</number>
1693+ </property>
1694+ <property name="maximum">
1695+ <number>100</number>
1696+ </property>
1697+ <property name="singleStep">
1698+ <number>1</number>
1699+ </property>
1700+ <property name="pageStep">
1701+ <number>1</number>
1702+ </property>
1703+ <property name="value">
1704+ <number>50</number>
1705+ </property>
1706+ <property name="orientation">
1707+ <enum>Qt::Horizontal</enum>
1708+ </property>
1709+ <property name="tickInterval">
1710+ <number>5</number>
1711+ </property>
1712+ </widget>
1713+ </item>
1714+ <item>
1715+ <layout class="QHBoxLayout">
1716+ <item>
1717+ <widget class="QLabel" name="TextLabel8">
1718+ <property name="font">
1719+ <font/>
1720+ </property>
1721+ <property name="text">
1722+ <string>0 Db</string>
1723+ </property>
1724+ <property name="wordWrap">
1725+ <bool>false</bool>
1726+ </property>
1727+ </widget>
1728+ </item>
1729+ <item>
1730+ <spacer>
1731+ <property name="orientation">
1732+ <enum>Qt::Horizontal</enum>
1733+ </property>
1734+ <property name="sizeHint" stdset="0">
1735+ <size>
1736+ <width>40</width>
1737+ <height>20</height>
1738+ </size>
1739+ </property>
1740+ </spacer>
1741+ </item>
1742+ <item>
1743+ <widget class="QLabel" name="TextLabel8_2">
1744+ <property name="font">
1745+ <font/>
1746+ </property>
1747+ <property name="text">
1748+ <string>+15 Db </string>
1749+ </property>
1750+ <property name="wordWrap">
1751+ <bool>false</bool>
1752+ </property>
1753+ </widget>
1754+ </item>
1755+ </layout>
1756+ </item>
1757+ </layout>
1758+ </item>
1759+ </layout>
1760+ </item>
1761+ </layout>
1762+ </widget>
1763+ </item>
1764+ <item>
1765+ <layout class="QHBoxLayout">
1766+ <item>
1767+ <spacer>
1768+ <property name="orientation">
1769+ <enum>Qt::Horizontal</enum>
1770+ </property>
1771+ <property name="sizeHint" stdset="0">
1772+ <size>
1773+ <width>40</width>
1774+ <height>20</height>
1775+ </size>
1776+ </property>
1777+ </spacer>
1778+ </item>
1779+ <item>
1780+ <widget class="QPushButton" name="PushButtonReset">
1781+ <property name="text">
1782+ <string>Reset</string>
1783+ </property>
1784+ </widget>
1785+ </item>
1786+ </layout>
1787+ </item>
1788+ <item>
1789+ <spacer>
1790+ <property name="orientation">
1791+ <enum>Qt::Vertical</enum>
1792+ </property>
1793+ <property name="sizeHint" stdset="0">
1794+ <size>
1795+ <width>20</width>
1796+ <height>40</height>
1797+ </size>
1798+ </property>
1799+ </spacer>
1800+ </item>
1801+ </layout>
1802+ </widget>
1803+ <layoutdefault spacing="6" margin="11"/>
1804+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
1805+ <customwidgets>
1806+ <customwidget>
1807+ <class>Q3GroupBox</class>
1808+ <extends>QGroupBox</extends>
1809+ <header>Qt3Support/Q3GroupBox</header>
1810+ <container>1</container>
1811+ </customwidget>
1812+ </customwidgets>
1813+ <resources/>
1814+ <connections/>
1815+</ui>
1816
1817=== modified file 'mixxx/src/engine/enginepregain.cpp'
1818--- mixxx/src/engine/enginepregain.cpp 2010-06-02 21:26:29 +0000
1819+++ mixxx/src/engine/enginepregain.cpp 2010-11-07 16:11:47 +0000
1820@@ -3,41 +3,76 @@
1821 -------------------
1822 copyright : (C) 2002 by Tue and Ken Haste Andersen
1823 email :
1824-***************************************************************************/
1825+ ***************************************************************************/
1826
1827 /***************************************************************************
1828-* *
1829-* This program is free software; you can redistribute it and/or modify *
1830-* it under the terms of the GNU General Public License as published by *
1831-* the Free Software Foundation; either version 2 of the License, or *
1832-* (at your option) any later version. *
1833-* *
1834-***************************************************************************/
1835+ * *
1836+ * This program is free software; you can redistribute it and/or modify *
1837+ * it under the terms of the GNU General Public License as published by *
1838+ * the Free Software Foundation; either version 2 of the License, or *
1839+ * (at your option) any later version. *
1840+ * *
1841+ ***************************************************************************/
1842
1843 #include "enginepregain.h"
1844 #include "controllogpotmeter.h"
1845+#include "controlpotmeter.h"
1846+#include "controlpushbutton.h"
1847+#include "configobject.h"
1848+#include "controlobject.h"
1849+
1850 #include "sampleutil.h"
1851
1852+
1853 /*----------------------------------------------------------------
1854 A pregaincontrol is ... a pregain.
1855 ----------------------------------------------------------------*/
1856 EnginePregain::EnginePregain(const char * group)
1857 {
1858- potmeterPregain = new ControlLogpotmeter(ConfigKey(group, "pregain"), 4.);
1859- // potmeterPregain = new ControlPotmeter(ConfigKey(group, "pregain"), -1., 1.);
1860+ potmeterPregain = new ControlLogpotmeter(ConfigKey(group, "pregain"), 4.);
1861+ //Replay Gain things
1862+ m_pControlReplayGain = new ControlObject(ConfigKey(group, "replaygain"));
1863+ m_pTotalGain = new ControlObject(ConfigKey(group, "total_gain"));
1864+
1865+
1866+ if(ControlObject::getControl(ConfigKey("[ReplayGain]", "InitialReplayGainBoost"))==NULL)
1867+ {
1868+ m_pReplayGainBoost = new ControlPotmeter(ConfigKey("[ReplayGain]", "InitialReplayGainBoost"),0., 15.);
1869+ m_pEnableRG = new ControlPotmeter(ConfigKey("[ReplayGain]", "ReplayGainEnabled"));
1870+ }
1871+ else
1872+ {
1873+ m_pReplayGainBoost = (ControlPotmeter*)ControlObject::getControl(ConfigKey("[ReplayGain]", "InitialReplayGainBoost"));
1874+ m_pEnableRG = (ControlPotmeter*)ControlObject::getControl(ConfigKey("[ReplayGain]", "ReplayGainEnabled"));
1875+ }
1876
1877 }
1878
1879 EnginePregain::~EnginePregain()
1880 {
1881 delete potmeterPregain;
1882+ delete m_pControlReplayGain;
1883+ delete m_pTotalGain;
1884 }
1885
1886 void EnginePregain::process(const CSAMPLE * pIn, const CSAMPLE * pOut, const int iBufferSize)
1887 {
1888+
1889+ float fEnableRG = m_pEnableRG->get();
1890+ float fReplayGainBoost = m_pReplayGainBoost->get();
1891 CSAMPLE * pOutput = (CSAMPLE *)pOut;
1892 float fGain = potmeterPregain->get();
1893- fGain = fGain/2;
1894+ float fRGain = m_pControlReplayGain->get();
1895+ m_fReplayGainCorrection=1;
1896+ if(fRGain*fEnableRG != 0)
1897+ {
1898+
1899+ //Passing a user defined boost
1900+ m_fReplayGainCorrection=fRGain*pow(10, fReplayGainBoost/20);
1901+ }
1902+ fGain = (fGain/2)*m_fReplayGainCorrection;
1903+ m_pTotalGain -> set(fGain);
1904+
1905
1906 // SampleUtil deals with aliased buffers and gains of 1 or 0.
1907 SampleUtil::copyWithGain(pOutput, pIn, fGain, iBufferSize);
1908
1909=== modified file 'mixxx/src/engine/enginepregain.h'
1910--- mixxx/src/engine/enginepregain.h 2009-01-24 04:39:32 +0000
1911+++ mixxx/src/engine/enginepregain.h 2010-11-07 16:11:47 +0000
1912@@ -18,18 +18,27 @@
1913 #define ENGINEPREGAIN_H
1914
1915 #include "engineobject.h"
1916+#include "controlobject.h"
1917+
1918
1919 class ControlLogpotmeter;
1920-
1921+class ControlPotmeter;
1922+class ControlObject;
1923 class EnginePregain : public EngineObject
1924 {
1925 public:
1926- EnginePregain(const char *group);
1927+ EnginePregain( const char *group);
1928 ~EnginePregain();
1929 void process(const CSAMPLE *pIn, const CSAMPLE *pOut, const int iBufferSize);
1930
1931+
1932+
1933 private:
1934 ControlLogpotmeter *potmeterPregain;
1935+ ControlPotmeter *m_pReplayGainBoost, *m_pEnableRG;
1936+ ControlObject *m_pControlReplayGain, *m_pTotalGain;
1937+ float m_fReplayGainCorrection;
1938+
1939 };
1940
1941 #endif
1942
1943=== modified file 'mixxx/src/library/dao/trackdao.cpp'
1944--- mixxx/src/library/dao/trackdao.cpp 2010-09-17 04:15:33 +0000
1945+++ mixxx/src/library/dao/trackdao.cpp 2010-11-07 16:11:47 +0000
1946@@ -188,12 +188,12 @@
1947 void TrackDAO::prepareLibraryInsert(QSqlQuery& query) {
1948 query.prepare("INSERT INTO library (artist, title, album, year, genre, tracknumber, "
1949 "filetype, location, comment, url, duration, "
1950- "bitrate, samplerate, cuepoint, bpm, wavesummaryhex, "
1951+ "bitrate, samplerate, cuepoint, bpm, replaygain, wavesummaryhex, "
1952 "channels, mixxx_deleted, header_parsed) "
1953 "VALUES (:artist, "
1954 ":title, :album, :year, :genre, :tracknumber, "
1955 ":filetype, :location, :comment, :url, :duration, "
1956- ":bitrate, :samplerate, :cuepoint, :bpm, :wavesummaryhex, "
1957+ ":bitrate, :samplerate, :cuepoint, :bpm, :replaygain, :wavesummaryhex, "
1958 ":channels, :mixxx_deleted, :header_parsed)");
1959 }
1960
1961@@ -213,6 +213,7 @@
1962 query.bindValue(":samplerate", pTrack->getSampleRate());
1963 query.bindValue(":cuepoint", pTrack->getCuePoint());
1964 query.bindValue(":bpm", pTrack->getBpm());
1965+ query.bindValue(":replaygain", pTrack->getRG());
1966 const QByteArray* pWaveSummary = pTrack->getWaveSummary();
1967 if (pWaveSummary) //Avoid null pointer deref
1968 query.bindValue(":wavesummaryhex", *pWaveSummary);
1969@@ -503,6 +504,7 @@
1970 int samplerate = query.value(query.record().indexOf("samplerate")).toInt();
1971 int cuepoint = query.value(query.record().indexOf("cuepoint")).toInt();
1972 QString bpm = query.value(query.record().indexOf("bpm")).toString();
1973+ QString replaygain = query.value(query.record().indexOf("replaygain")).toString();
1974 QByteArray* wavesummaryhex = new QByteArray(
1975 query.value(query.record().indexOf("wavesummaryhex")).toByteArray());
1976 //int timesplayed = query.value(query.record().indexOf("timesplayed")).toInt();
1977@@ -533,6 +535,7 @@
1978 track->setSampleRate(samplerate);
1979 track->setCuePoint((float)cuepoint);
1980 track->setBpm(bpm.toFloat());
1981+ track->setRG(replaygain.toFloat());
1982 track->setWaveSummary(wavesummaryhex, false);
1983 delete wavesummaryhex;
1984 //track->setTimesPlayed //Doesn't exist wtfbbq
1985@@ -607,7 +610,7 @@
1986 time.start();
1987 QSqlQuery query(m_database);
1988
1989- query.prepare("SELECT library.id, artist, title, album, year, genre, tracknumber, filetype, track_locations.location as location, track_locations.filesize as filesize, comment, url, duration, bitrate, samplerate, cuepoint, bpm, wavesummaryhex, channels, header_parsed FROM Library INNER JOIN track_locations ON library.location = track_locations.id WHERE library.id=" + QString("%1").arg(id));
1990+ query.prepare("SELECT library.id, artist, title, album, year, genre, tracknumber, filetype, track_locations.location as location, track_locations.filesize as filesize, comment, url, duration, bitrate, samplerate, cuepoint, bpm, replaygain, wavesummaryhex, channels, header_parsed FROM Library INNER JOIN track_locations ON library.location = track_locations.id WHERE library.id=" + QString("%1").arg(id));
1991 TrackPointer pTrack;
1992
1993 if (query.exec()) {
1994@@ -643,7 +646,7 @@
1995 "filetype=:filetype, tracknumber=:tracknumber, "
1996 "comment=:comment, url=:url, duration=:duration, "
1997 "bitrate=:bitrate, samplerate=:samplerate, cuepoint=:cuepoint, "
1998- "bpm=:bpm, wavesummaryhex=:wavesummaryhex, "
1999+ "bpm=:bpm, replaygain=:replaygain, wavesummaryhex=:wavesummaryhex, "
2000 "channels=:channels, header_parsed=:header_parsed "
2001 "WHERE id="+QString("%1").arg(trackId));
2002 query.bindValue(":artist", pTrack->getArtist());
2003@@ -660,6 +663,7 @@
2004 query.bindValue(":samplerate", pTrack->getSampleRate());
2005 query.bindValue(":cuepoint", pTrack->getCuePoint());
2006 query.bindValue(":bpm", pTrack->getBpm());
2007+ query.bindValue(":replaygain", pTrack->getRG());
2008 const QByteArray* pWaveSummary = pTrack->getWaveSummary();
2009 if (pWaveSummary) //Avoid null pointer deref
2010 query.bindValue(":wavesummaryhex", *pWaveSummary);
2011
2012=== modified file 'mixxx/src/library/dao/trackdao.h'
2013--- mixxx/src/library/dao/trackdao.h 2010-09-13 06:23:38 +0000
2014+++ mixxx/src/library/dao/trackdao.h 2010-11-07 16:11:47 +0000
2015@@ -30,6 +30,7 @@
2016 const QString LIBRARYTABLE_DURATION = "duration";
2017 const QString LIBRARYTABLE_BITRATE = "bitrate";
2018 const QString LIBRARYTABLE_BPM = "bpm";
2019+const QString LIBRARYTABLE_REPLAYGAIN = "replaygain";
2020 const QString LIBRARYTABLE_CUEPOINT = "cuepoint";
2021 const QString LIBRARYTABLE_URL = "url";
2022 const QString LIBRARYTABLE_SAMPLERATE = "samplerate";
2023
2024=== modified file 'mixxx/src/library/librarytablemodel.cpp'
2025--- mixxx/src/library/librarytablemodel.cpp 2010-10-28 06:41:43 +0000
2026+++ mixxx/src/library/librarytablemodel.cpp 2010-11-07 16:11:47 +0000
2027@@ -201,6 +201,7 @@
2028 if ((column == fieldIndex(LIBRARYTABLE_ID)) ||
2029 (column == fieldIndex(LIBRARYTABLE_URL)) ||
2030 (column == fieldIndex(LIBRARYTABLE_CUEPOINT)) ||
2031+ (column == fieldIndex(LIBRARYTABLE_REPLAYGAIN)) ||
2032 (column == fieldIndex(LIBRARYTABLE_WAVESUMMARYHEX)) ||
2033 (column == fieldIndex(LIBRARYTABLE_SAMPLERATE)) ||
2034 (column == fieldIndex(LIBRARYTABLE_MIXXXDELETED)) ||
2035
2036=== modified file 'mixxx/src/library/trackcollection.cpp'
2037--- mixxx/src/library/trackcollection.cpp 2010-10-26 08:02:18 +0000
2038+++ mixxx/src/library/trackcollection.cpp 2010-11-07 16:11:47 +0000
2039@@ -65,7 +65,7 @@
2040 return false;
2041 }
2042
2043- int requiredSchemaVersion = 5;
2044+ int requiredSchemaVersion = 6;
2045 if (!SchemaManager::upgradeToSchemaVersion(m_pConfig, m_db,
2046 requiredSchemaVersion)) {
2047 QMessageBox::warning(0, tr("Cannot upgrade database schema"),
2048
2049=== modified file 'mixxx/src/player.cpp'
2050--- mixxx/src/player.cpp 2010-11-01 06:44:32 +0000
2051+++ mixxx/src/player.cpp 2010-11-07 16:11:47 +0000
2052@@ -77,6 +77,8 @@
2053 //BPM of the current song
2054 m_pBPM = new ControlObjectThreadMain(
2055 ControlObject::getControl(ConfigKey(m_strChannel, "file_bpm")));
2056+ m_pRG = new ControlObjectThreadMain(
2057+ ControlObject::getControl(ConfigKey(m_strChannel, "replaygain")));
2058
2059 // Create WaveformRenderer last, because it relies on controls created above
2060 // (e.g. EngineBuffer)
2061@@ -100,6 +102,7 @@
2062 delete m_pLoopOutPoint;
2063 delete m_pPlayPosition;
2064 delete m_pBPM;
2065+ delete m_pRG;
2066 }
2067
2068 void Player::slotLoadTrack(TrackPointer track, bool bStartFromEndPos)
2069@@ -144,6 +147,10 @@
2070 connect(m_pLoadedTrack.data(), SIGNAL(bpmUpdated(double)),
2071 m_pBPM, SLOT(slotSet(double)));
2072
2073+ // Listen for updates to the file's Replay Gain
2074+ connect(m_pLoadedTrack.data(), SIGNAL(RGUpdated(double)),
2075+ m_pRG, SLOT(slotSet(double)));
2076+
2077 //Request a new track from the reader
2078 emit(loadTrack(track));
2079 }
2080@@ -167,6 +174,7 @@
2081 }
2082 m_pDuration->set(0);
2083 m_pBPM->slotSet(0);
2084+ m_pRG->slotSet(0);
2085 m_pLoopInPoint->slotSet(-1);
2086 m_pLoopOutPoint->slotSet(-1);
2087 m_pLoadedTrack.clear();
2088@@ -193,7 +201,7 @@
2089 //Update the BPM and duration values that are stored in ControlObjects
2090 m_pDuration->set(m_pLoadedTrack->getDuration());
2091 m_pBPM->slotSet(m_pLoadedTrack->getBpm());
2092-
2093+ m_pRG->slotSet(m_pLoadedTrack->getRG());
2094 // Update the PlayerInfo class that is used in EngineShoutcast to replace
2095 // the metadata of a stream
2096 PlayerInfo::Instance().setTrackInfo(m_strChannel.mid(8,1).toInt(), m_pLoadedTrack);
2097
2098=== modified file 'mixxx/src/player.h'
2099--- mixxx/src/player.h 2010-11-01 06:44:32 +0000
2100+++ mixxx/src/player.h 2010-11-07 16:11:47 +0000
2101@@ -43,6 +43,7 @@
2102 ControlObjectThreadMain* m_pPlayPosition;
2103 ControlObject* m_pDuration;
2104 ControlObjectThreadMain* m_pBPM;
2105+ ControlObjectThreadMain* m_pRG;
2106 WaveformRenderer* m_pWaveformRenderer;
2107 };
2108
2109
2110=== added directory 'mixxx/src/replaygain'
2111=== added file 'mixxx/src/replaygain/replaygain_analysis.c'
2112--- mixxx/src/replaygain/replaygain_analysis.c 1970-01-01 00:00:00 +0000
2113+++ mixxx/src/replaygain/replaygain_analysis.c 2010-11-07 16:11:47 +0000
2114@@ -0,0 +1,431 @@
2115+/*
2116+ * ReplayGainAnalysis - analyzes input samples and give the recommended dB change
2117+ * Copyright (C) 2001 David Robinson and Glen Sawyer
2118+ *
2119+ * This library is free software; you can redistribute it and/or
2120+ * modify it under the terms of the GNU Lesser General Public
2121+ * License as published by the Free Software Foundation; either
2122+ * version 2.1 of the License, or (at your option) any later version.
2123+ *
2124+ * This library is distributed in the hope that it will be useful,
2125+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2126+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2127+ * Lesser General Public License for more details.
2128+ *
2129+ * You should have received a copy of the GNU Lesser General Public
2130+ * License along with this library; if not, write to the Free Software
2131+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2132+ *
2133+ * concept and filter values by David Robinson (David@Robinson.org)
2134+ * -- blame him if you think the idea is flawed
2135+ * original coding by Glen Sawyer (glensawyer@hotmail.com)
2136+ * -- blame him if you think this runs too slowly, or the coding is otherwise flawed
2137+ *
2138+ * lots of code improvements by Frank Klemm ( http://www.uni-jena.de/~pfk/mpp/ )
2139+ * -- credit him for all the _good_ programming ;)
2140+ *
2141+ * minor cosmetic tweaks to integrate with FLAC by Josh Coalson
2142+ *
2143+ *
2144+ * For an explanation of the concepts and the basic algorithms involved, go to:
2145+ * http://www.replaygain.org/
2146+ */
2147+
2148+/*
2149+ * Here's the deal. Call
2150+ *
2151+ * InitGainAnalysis ( long samplefreq );
2152+ *
2153+ * to initialize everything. Call
2154+ *
2155+ * AnalyzeSamples ( const Float_t* left_samples,
2156+ * const Float_t* right_samples,
2157+ * size_t num_samples,
2158+ * int num_channels );
2159+ *
2160+ * as many times as you want, with as many or as few samples as you want.
2161+ * If mono, pass the sample buffer in through left_samples, leave
2162+ * right_samples NULL, and make sure num_channels = 1.
2163+ *
2164+ * GetTitleGain()
2165+ *
2166+ * will return the recommended dB level change for all samples analyzed
2167+ * SINCE THE LAST TIME you called GetTitleGain() OR InitGainAnalysis().
2168+ *
2169+ * GetAlbumGain()
2170+ *
2171+ * will return the recommended dB level change for all samples analyzed
2172+ * since InitGainAnalysis() was called and finalized with GetTitleGain().
2173+ *
2174+ * Pseudo-code to process an album:
2175+ *
2176+ * Float_t l_samples [4096];
2177+ * Float_t r_samples [4096];
2178+ * size_t num_samples;
2179+ * unsigned int num_songs;
2180+ * unsigned int i;
2181+ *
2182+ * InitGainAnalysis ( 44100 );
2183+ * for ( i = 1; i <= num_songs; i++ ) {
2184+ * while ( ( num_samples = getSongSamples ( song[i], left_samples, right_samples ) ) > 0 )
2185+ * AnalyzeSamples ( left_samples, right_samples, num_samples, 2 );
2186+ * fprintf ("Recommended dB change for song %2d: %+6.2f dB\n", i, GetTitleGain() );
2187+ * }
2188+ * fprintf ("Recommended dB change for whole album: %+6.2f dB\n", GetAlbumGain() );
2189+ */
2190+
2191+/*
2192+ * So here's the main source of potential code confusion:
2193+ *
2194+ * The filters applied to the incoming samples are IIR filters,
2195+ * meaning they rely on up to <filter order> number of previous samples
2196+ * AND up to <filter order> number of previous filtered samples.
2197+ *
2198+ * I set up the AnalyzeSamples routine to minimize memory usage and interface
2199+ * complexity. The speed isn't compromised too much (I don't think), but the
2200+ * internal complexity is higher than it should be for such a relatively
2201+ * simple routine.
2202+ *
2203+ * Optimization/clarity suggestions are welcome.
2204+ */
2205+
2206+#if HAVE_CONFIG_H
2207+# include <config.h>
2208+#endif
2209+
2210+#include <stdio.h>
2211+#include <stdlib.h>
2212+#include <string.h>
2213+#include <math.h>
2214+
2215+#include "replaygain_analysis.h"
2216+
2217+Float_t ReplayGainReferenceLoudness = 89.0; /* in dB SPL */
2218+
2219+typedef unsigned short Uint16_t;
2220+typedef signed short Int16_t;
2221+typedef unsigned int Uint32_t;
2222+typedef signed int Int32_t;
2223+
2224+#define YULE_ORDER 10
2225+#define BUTTER_ORDER 2
2226+#define RMS_PERCENTILE 0.95 /* percentile which is louder than the proposed level */
2227+#define MAX_SAMP_FREQ 48000. /* maximum allowed sample frequency [Hz] */
2228+#define RMS_WINDOW_TIME 0.050 /* Time slice size [s] */
2229+#define STEPS_per_dB 100. /* Table entries per dB */
2230+#define MAX_dB 120. /* Table entries for 0...MAX_dB (normal max. values are 70...80 dB) */
2231+
2232+#define MAX_ORDER (BUTTER_ORDER > YULE_ORDER ? BUTTER_ORDER : YULE_ORDER)
2233+/* [JEC] the following was originally #defined as:
2234+ * (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME)
2235+ * but that seemed to fail to take into account the ceil() part of the
2236+ * sampleWindow calculation in ResetSampleFrequency(), and was causing
2237+ * buffer overflows for 48kHz analysis, hence the +1.
2238+ */
2239+#ifndef __sun
2240+ #define MAX_SAMPLES_PER_WINDOW (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME + 1.) /* max. Samples per Time slice */
2241+#else
2242+ /* [JEC] Solaris Forte compiler doesn't like float calc in array indices */
2243+ #define MAX_SAMPLES_PER_WINDOW (size_t) (2401)
2244+#endif
2245+#define PINK_REF 64.82 /* 298640883795 */ /* calibration value */
2246+
2247+static Float_t linprebuf [MAX_ORDER * 2];
2248+static Float_t* linpre; /* left input samples, with pre-buffer */
2249+static Float_t lstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
2250+static Float_t* lstep; /* left "first step" (i.e. post first filter) samples */
2251+static Float_t loutbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
2252+static Float_t* lout; /* left "out" (i.e. post second filter) samples */
2253+static Float_t rinprebuf [MAX_ORDER * 2];
2254+static Float_t* rinpre; /* right input samples ... */
2255+static Float_t rstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
2256+static Float_t* rstep;
2257+static Float_t routbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER];
2258+static Float_t* rout;
2259+static unsigned int sampleWindow; /* number of samples required to reach number of milliseconds required for RMS window */
2260+static unsigned long totsamp;
2261+static double lsum;
2262+static double rsum;
2263+static int freqindex;
2264+#ifndef __sun
2265+static Uint32_t A [(size_t)(STEPS_per_dB * MAX_dB)];
2266+/*static Uint32_t B [(size_t)(STEPS_per_dB * MAX_dB)];*/
2267+#else
2268+/* [JEC] Solaris Forte compiler doesn't like float calc in array indices */
2269+static Uint32_t A [12000];
2270+/*static Uint32_t B [12000];*/
2271+#endif
2272+
2273+/* for each filter:
2274+ [0] 48 kHz, [1] 44.1 kHz, [2] 32 kHz, [3] 24 kHz, [4] 22050 Hz, [5] 16 kHz, [6] 12 kHz, [7] is 11025 Hz, [8] 8 kHz */
2275+
2276+#ifdef WIN32
2277+#pragma warning ( disable : 4305 )
2278+#endif
2279+
2280+static const Float_t AYule [9] [11] = {
2281+ { 1., -3.84664617118067, 7.81501653005538,-11.34170355132042, 13.05504219327545,-12.28759895145294, 9.48293806319790, -5.87257861775999, 2.75465861874613, -0.86984376593551, 0.13919314567432 },
2282+ { 1., -3.47845948550071, 6.36317777566148, -8.54751527471874, 9.47693607801280, -8.81498681370155, 6.85401540936998, -4.39470996079559, 2.19611684890774, -0.75104302451432, 0.13149317958808 },
2283+ { 1., -2.37898834973084, 2.84868151156327, -2.64577170229825, 2.23697657451713, -1.67148153367602, 1.00595954808547, -0.45953458054983, 0.16378164858596, -0.05032077717131, 0.02347897407020 },
2284+ { 1., -1.61273165137247, 1.07977492259970, -0.25656257754070, -0.16276719120440, -0.22638893773906, 0.39120800788284, -0.22138138954925, 0.04500235387352, 0.02005851806501, 0.00302439095741 },
2285+ { 1., -1.49858979367799, 0.87350271418188, 0.12205022308084, -0.80774944671438, 0.47854794562326, -0.12453458140019, -0.04067510197014, 0.08333755284107, -0.04237348025746, 0.02977207319925 },
2286+ { 1., -0.62820619233671, 0.29661783706366, -0.37256372942400, 0.00213767857124, -0.42029820170918, 0.22199650564824, 0.00613424350682, 0.06747620744683, 0.05784820375801, 0.03222754072173 },
2287+ { 1., -1.04800335126349, 0.29156311971249, -0.26806001042947, 0.00819999645858, 0.45054734505008, -0.33032403314006, 0.06739368333110, -0.04784254229033, 0.01639907836189, 0.01807364323573 },
2288+ { 1., -0.51035327095184, -0.31863563325245, -0.20256413484477, 0.14728154134330, 0.38952639978999, -0.23313271880868, -0.05246019024463, -0.02505961724053, 0.02442357316099, 0.01818801111503 },
2289+ { 1., -0.25049871956020, -0.43193942311114, -0.03424681017675, -0.04678328784242, 0.26408300200955, 0.15113130533216, -0.17556493366449, -0.18823009262115, 0.05477720428674, 0.04704409688120 }
2290+};
2291+
2292+static const Float_t BYule [9] [11] = {
2293+ { 0.03857599435200, -0.02160367184185, -0.00123395316851, -0.00009291677959, -0.01655260341619, 0.02161526843274, -0.02074045215285, 0.00594298065125, 0.00306428023191, 0.00012025322027, 0.00288463683916 },
2294+ { 0.05418656406430, -0.02911007808948, -0.00848709379851, -0.00851165645469, -0.00834990904936, 0.02245293253339, -0.02596338512915, 0.01624864962975, -0.00240879051584, 0.00674613682247, -0.00187763777362 },
2295+ { 0.15457299681924, -0.09331049056315, -0.06247880153653, 0.02163541888798, -0.05588393329856, 0.04781476674921, 0.00222312597743, 0.03174092540049, -0.01390589421898, 0.00651420667831, -0.00881362733839 },
2296+ { 0.30296907319327, -0.22613988682123, -0.08587323730772, 0.03282930172664, -0.00915702933434, -0.02364141202522, -0.00584456039913, 0.06276101321749, -0.00000828086748, 0.00205861885564, -0.02950134983287 },
2297+ { 0.33642304856132, -0.25572241425570, -0.11828570177555, 0.11921148675203, -0.07834489609479, -0.00469977914380, -0.00589500224440, 0.05724228140351, 0.00832043980773, -0.01635381384540, -0.01760176568150 },
2298+ { 0.44915256608450, -0.14351757464547, -0.22784394429749, -0.01419140100551, 0.04078262797139, -0.12398163381748, 0.04097565135648, 0.10478503600251, -0.01863887810927, -0.03193428438915, 0.00541907748707 },
2299+ { 0.56619470757641, -0.75464456939302, 0.16242137742230, 0.16744243493672, -0.18901604199609, 0.30931782841830, -0.27562961986224, 0.00647310677246, 0.08647503780351, -0.03788984554840, -0.00588215443421 },
2300+ { 0.58100494960553, -0.53174909058578, -0.14289799034253, 0.17520704835522, 0.02377945217615, 0.15558449135573, -0.25344790059353, 0.01628462406333, 0.06920467763959, -0.03721611395801, -0.00749618797172 },
2301+ { 0.53648789255105, -0.42163034350696, -0.00275953611929, 0.04267842219415, -0.10214864179676, 0.14590772289388, -0.02459864859345, -0.11202315195388, -0.04060034127000, 0.04788665548180, -0.02217936801134 }
2302+};
2303+
2304+static const Float_t AButter [9] [3] = {
2305+ { 1., -1.97223372919527, 0.97261396931306 },
2306+ { 1., -1.96977855582618, 0.97022847566350 },
2307+ { 1., -1.95835380975398, 0.95920349965459 },
2308+ { 1., -1.95002759149878, 0.95124613669835 },
2309+ { 1., -1.94561023566527, 0.94705070426118 },
2310+ { 1., -1.92783286977036, 0.93034775234268 },
2311+ { 1., -1.91858953033784, 0.92177618768381 },
2312+ { 1., -1.91542108074780, 0.91885558323625 },
2313+ { 1., -1.88903307939452, 0.89487434461664 }
2314+};
2315+
2316+static const Float_t BButter [9] [3] = {
2317+ { 0.98621192462708, -1.97242384925416, 0.98621192462708 },
2318+ { 0.98500175787242, -1.97000351574484, 0.98500175787242 },
2319+ { 0.97938932735214, -1.95877865470428, 0.97938932735214 },
2320+ { 0.97531843204928, -1.95063686409857, 0.97531843204928 },
2321+ { 0.97316523498161, -1.94633046996323, 0.97316523498161 },
2322+ { 0.96454515552826, -1.92909031105652, 0.96454515552826 },
2323+ { 0.96009142950541, -1.92018285901082, 0.96009142950541 },
2324+ { 0.95856916599601, -1.91713833199203, 0.95856916599601 },
2325+ { 0.94597685600279, -1.89195371200558, 0.94597685600279 }
2326+};
2327+
2328+#ifdef WIN32
2329+#pragma warning ( default : 4305 )
2330+#endif
2331+
2332+/* When calling this procedure, make sure that ip[-order] and op[-order] point to real data! */
2333+
2334+static void
2335+filter ( const Float_t* input, Float_t* output, size_t nSamples, const Float_t* a, const Float_t* b, size_t order )
2336+{
2337+ double y;
2338+ size_t i;
2339+ size_t k;
2340+
2341+ for ( i = 0; i < nSamples; i++ ) {
2342+ y = input[i] * b[0];
2343+ for ( k = 1; k <= order; k++ )
2344+ y += input[i-k] * b[k] - output[i-k] * a[k];
2345+ output[i] = (Float_t)y;
2346+ }
2347+}
2348+
2349+/* returns a INIT_GAIN_ANALYSIS_OK if successful, INIT_GAIN_ANALYSIS_ERROR if not */
2350+
2351+int
2352+ResetSampleFrequency ( long samplefreq ) {
2353+ int i;
2354+
2355+ /* zero out initial values */
2356+ for ( i = 0; i < MAX_ORDER; i++ )
2357+ linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.;
2358+
2359+ switch ( (int)(samplefreq) ) {
2360+ case 48000: freqindex = 0; break;
2361+ case 44100: freqindex = 1; break;
2362+ case 32000: freqindex = 2; break;
2363+ case 24000: freqindex = 3; break;
2364+ case 22050: freqindex = 4; break;
2365+ case 16000: freqindex = 5; break;
2366+ case 12000: freqindex = 6; break;
2367+ case 11025: freqindex = 7; break;
2368+ case 8000: freqindex = 8; break;
2369+ default: return INIT_GAIN_ANALYSIS_ERROR;
2370+ }
2371+
2372+ sampleWindow = (int) ceil (samplefreq * RMS_WINDOW_TIME);
2373+
2374+ lsum = 0.;
2375+ rsum = 0.;
2376+ totsamp = 0;
2377+
2378+ memset ( A, 0, sizeof(A) );
2379+
2380+ return INIT_GAIN_ANALYSIS_OK;
2381+}
2382+
2383+int
2384+InitGainAnalysis ( long samplefreq )
2385+{
2386+ if (ResetSampleFrequency(samplefreq) != INIT_GAIN_ANALYSIS_OK) {
2387+ return INIT_GAIN_ANALYSIS_ERROR;
2388+ }
2389+
2390+ linpre = linprebuf + MAX_ORDER;
2391+ rinpre = rinprebuf + MAX_ORDER;
2392+ lstep = lstepbuf + MAX_ORDER;
2393+ rstep = rstepbuf + MAX_ORDER;
2394+ lout = loutbuf + MAX_ORDER;
2395+ rout = routbuf + MAX_ORDER;
2396+
2397+ //memset ( B, 0, sizeof(B) );
2398+
2399+ return INIT_GAIN_ANALYSIS_OK;
2400+}
2401+
2402+/* returns GAIN_ANALYSIS_OK if successful, GAIN_ANALYSIS_ERROR if not */
2403+
2404+int
2405+AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels )
2406+{
2407+ const Float_t* curleft;
2408+ const Float_t* curright;
2409+ long batchsamples;
2410+ long cursamples;
2411+ long cursamplepos;
2412+ int i;
2413+
2414+ if ( num_samples == 0 )
2415+ return GAIN_ANALYSIS_OK;
2416+
2417+ cursamplepos = 0;
2418+ batchsamples = num_samples;
2419+
2420+ switch ( num_channels) {
2421+ case 1: right_samples = left_samples;
2422+ case 2: break;
2423+ default: return GAIN_ANALYSIS_ERROR;
2424+ }
2425+
2426+ if ( num_samples < MAX_ORDER ) {
2427+ memcpy ( linprebuf + MAX_ORDER, left_samples , num_samples * sizeof(Float_t) );
2428+ memcpy ( rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t) );
2429+ }
2430+ else {
2431+ memcpy ( linprebuf + MAX_ORDER, left_samples, MAX_ORDER * sizeof(Float_t) );
2432+ memcpy ( rinprebuf + MAX_ORDER, right_samples, MAX_ORDER * sizeof(Float_t) );
2433+ }
2434+
2435+ while ( batchsamples > 0 ) {
2436+ cursamples = batchsamples > (long)(sampleWindow-totsamp) ? (long)(sampleWindow - totsamp) : batchsamples;
2437+ if ( cursamplepos < MAX_ORDER ) {
2438+ curleft = linpre+cursamplepos;
2439+ curright = rinpre+cursamplepos;
2440+ if (cursamples > MAX_ORDER - cursamplepos )
2441+ cursamples = MAX_ORDER - cursamplepos;
2442+ }
2443+ else {
2444+ curleft = left_samples + cursamplepos;
2445+ curright = right_samples + cursamplepos;
2446+ }
2447+
2448+ filter ( curleft , lstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER );
2449+ filter ( curright, rstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER );
2450+
2451+ filter ( lstep + totsamp, lout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER );
2452+ filter ( rstep + totsamp, rout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER );
2453+
2454+ for ( i = 0; i < cursamples; i++ ) { /* Get the squared values */
2455+ lsum += lout [totsamp+i] * lout [totsamp+i];
2456+ rsum += rout [totsamp+i] * rout [totsamp+i];
2457+ }
2458+
2459+ batchsamples -= cursamples;
2460+ cursamplepos += cursamples;
2461+ totsamp += cursamples;
2462+ if ( totsamp == sampleWindow ) { /* Get the Root Mean Square (RMS) for this set of samples */
2463+ double val = STEPS_per_dB * 10. * log10 ( (lsum+rsum) / totsamp * 0.5 + 1.e-37 );
2464+ int ival = (int) val;
2465+ if ( ival < 0 ) ival = 0;
2466+ if ( ival >= (int)(sizeof(A)/sizeof(*A)) ) ival = (int)(sizeof(A)/sizeof(*A)) - 1;
2467+ A [ival]++;
2468+ lsum = rsum = 0.;
2469+ memmove ( loutbuf , loutbuf + totsamp, MAX_ORDER * sizeof(Float_t) );
2470+ memmove ( routbuf , routbuf + totsamp, MAX_ORDER * sizeof(Float_t) );
2471+ memmove ( lstepbuf, lstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) );
2472+ memmove ( rstepbuf, rstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) );
2473+ totsamp = 0;
2474+ }
2475+ if ( totsamp > sampleWindow ) /* somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow */
2476+ return GAIN_ANALYSIS_ERROR;
2477+ }
2478+ if ( num_samples < MAX_ORDER ) {
2479+ memmove ( linprebuf, linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) );
2480+ memmove ( rinprebuf, rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) );
2481+ memcpy ( linprebuf + MAX_ORDER - num_samples, left_samples, num_samples * sizeof(Float_t) );
2482+ memcpy ( rinprebuf + MAX_ORDER - num_samples, right_samples, num_samples * sizeof(Float_t) );
2483+ }
2484+ else {
2485+ memcpy ( linprebuf, left_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) );
2486+ memcpy ( rinprebuf, right_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) );
2487+ }
2488+
2489+ return GAIN_ANALYSIS_OK;
2490+}
2491+
2492+
2493+static Float_t
2494+analyzeResult ( Uint32_t* Array, size_t len )
2495+{
2496+ Uint32_t elems;
2497+ Int32_t upper;
2498+ size_t i;
2499+
2500+ elems = 0;
2501+ for ( i = 0; i < len; i++ )
2502+ elems += Array[i];
2503+ if ( elems == 0 )
2504+ return GAIN_NOT_ENOUGH_SAMPLES;
2505+
2506+ upper = (Int32_t) ceil (elems * (1. - RMS_PERCENTILE));
2507+ for ( i = len; i-- > 0; ) {
2508+ if ( (upper -= Array[i]) <= 0 )
2509+ break;
2510+ }
2511+
2512+ return (Float_t) ((Float_t)PINK_REF - (Float_t)i / (Float_t)STEPS_per_dB);
2513+ // return (Float_t) ((Float_t)i / (Float_t)STEPS_per_dB);
2514+}
2515+
2516+
2517+Float_t
2518+GetTitleGain ( void )
2519+{
2520+ Float_t retval;
2521+ unsigned int i;
2522+
2523+ retval = analyzeResult ( A, sizeof(A)/sizeof(*A) );
2524+
2525+ for ( i = 0; i < sizeof(A)/sizeof(*A); i++ ) {
2526+ //B[i] += A[i];
2527+ A[i] = 0;
2528+ }
2529+
2530+ for ( i = 0; i < MAX_ORDER; i++ )
2531+ linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.f;
2532+
2533+ totsamp = 0;
2534+ lsum = rsum = 0.;
2535+ return retval;
2536+}
2537+
2538+
2539+//Float_t
2540+//GetAlbumGain ( void )
2541+//{
2542+// return analyzeResult ( B, sizeof(B)/sizeof(*B) );
2543+//}
2544+
2545+/* end of replaygain_analysis.c */
2546
2547=== added file 'mixxx/src/replaygain/replaygain_analysis.h'
2548--- mixxx/src/replaygain/replaygain_analysis.h 1970-01-01 00:00:00 +0000
2549+++ mixxx/src/replaygain/replaygain_analysis.h 2010-11-07 16:11:47 +0000
2550@@ -0,0 +1,58 @@
2551+/*
2552+ * ReplayGainAnalysis - analyzes input samples and give the recommended dB change
2553+ * Copyright (C) 2001 David Robinson and Glen Sawyer
2554+ *
2555+ * This library is free software; you can redistribute it and/or
2556+ * modify it under the terms of the GNU Lesser General Public
2557+ * License as published by the Free Software Foundation; either
2558+ * version 2.1 of the License, or (at your option) any later version.
2559+ *
2560+ * This library is distributed in the hope that it will be useful,
2561+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2562+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2563+ * Lesser General Public License for more details.
2564+ *
2565+ * You should have received a copy of the GNU Lesser General Public
2566+ * License along with this library; if not, write to the Free Software
2567+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2568+ *
2569+ * concept and filter values by David Robinson (David@Robinson.org)
2570+ * -- blame him if you think the idea is flawed
2571+ * coding by Glen Sawyer (glensawyer@hotmail.com) 442 N 700 E, Provo, UT 84606 USA
2572+ * -- blame him if you think this runs too slowly, or the coding is otherwise flawed
2573+ * minor cosmetic tweaks to integrate with FLAC by Josh Coalson
2574+ *
2575+ * For an explanation of the concepts and the basic algorithms involved, go to:
2576+ * http://www.replaygain.org/
2577+ */
2578+
2579+#ifndef GAIN_ANALYSIS_H
2580+#define GAIN_ANALYSIS_H
2581+
2582+#include <stddef.h>
2583+
2584+#define GAIN_NOT_ENOUGH_SAMPLES -24601
2585+#define GAIN_ANALYSIS_ERROR 0
2586+#define GAIN_ANALYSIS_OK 1
2587+#define INIT_GAIN_ANALYSIS_ERROR 0
2588+#define INIT_GAIN_ANALYSIS_OK 1
2589+
2590+#ifdef __cplusplus
2591+extern "C" {
2592+#endif
2593+
2594+typedef float Float_t; /* Type used for filtering */
2595+
2596+extern Float_t ReplayGainReferenceLoudness; /* in dB SPL, currently == 89.0 */
2597+
2598+int InitGainAnalysis ( long samplefreq );
2599+int AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels );
2600+int ResetSampleFrequency ( long samplefreq );
2601+Float_t GetTitleGain ( void );
2602+/*Float_t GetAlbumGain ( void );*/
2603+
2604+#ifdef __cplusplus
2605+}
2606+#endif
2607+
2608+#endif /* GAIN_ANALYSIS_H */
2609
2610=== modified file 'mixxx/src/soundsource.cpp'
2611--- mixxx/src/soundsource.cpp 2010-10-21 01:16:19 +0000
2612+++ mixxx/src/soundsource.cpp 2010-11-07 16:11:47 +0000
2613@@ -25,6 +25,7 @@
2614 #include <taglib/id3v1tag.h>
2615 #include <taglib/tmap.h>
2616 #include <taglib/tstringlist.h>
2617+#include <taglib/textidentificationframe.h>
2618 #include <taglib/wavpackfile.h>
2619
2620
2621@@ -50,6 +51,7 @@
2622 m_qFilename = qFilename;
2623 m_iSampleRate = 0;
2624 m_fBPM = 0.0f;
2625+ m_fRG = 0.0f;
2626 m_iDuration = 0;
2627 m_iBitrate = 0;
2628 m_iChannels = 0;
2629@@ -109,6 +111,10 @@
2630 {
2631 return m_sTrackNumber;
2632 }
2633+float SoundSource::getRG()
2634+{
2635+ return m_fRG;
2636+}
2637 float SoundSource::getBPM()
2638 {
2639 return m_fBPM;
2640@@ -162,6 +168,10 @@
2641 {
2642 m_sTrackNumber = trackNumber;
2643 }
2644+void SoundSource::setRG(float replaygain)
2645+{
2646+ m_fRG = replaygain;
2647+}
2648 void SoundSource::setBPM(float bpm)
2649 {
2650 m_fBPM = bpm;
2651@@ -245,6 +255,17 @@
2652 return false;
2653 }
2654
2655+void SoundSource::parseRGString (QString sRG) {
2656+ QString RGstring = sRG.remove( " dB" );
2657+ float fRG = pow(10,(RGstring.toFloat())/20);
2658+ //I found some mp3s of mine with replaygain tag set to 0dB even if not normalized.
2659+ //This is because of Rapid Evolution 3, I suppose. I prefer to rescan them by setting value to 0 (i.e. rescan via analyserrg)
2660+ if(fRG==1.0f){
2661+ fRG= 0.0f;
2662+ }
2663+ setRG(fRG);
2664+}
2665+
2666 void SoundSource::processBpmString(QString tagName, QString sBpm) {
2667 if (s_bDebugMetadata)
2668 qDebug() << tagName << "BPM" << sBpm;
2669@@ -262,7 +283,7 @@
2670 TagLib::ID3v2::FrameList::ConstIterator it = id3v2->frameList().begin();
2671 for(; it != id3v2->frameList().end(); it++) {
2672 qDebug() << "ID3V2" << (*it)->frameID().data() << "-"
2673- << TStringToQString((*it)->toString());
2674+ << TStringToQString((*it)->toString());
2675 }
2676 }
2677
2678@@ -279,6 +300,28 @@
2679 qDebug() << "KEY" << sKey;
2680 // TODO(XXX) write key to SoundSource and copy that to the Track
2681 }
2682+ // Foobar2000-style ID3v2.3.0 tags
2683+ // TODO: Check if everything is ok.
2684+ TagLib::ID3v2::FrameList frames = id3v2->frameListMap()["TXXX"];
2685+ for ( TagLib::ID3v2::FrameList::Iterator it = frames.begin(); it != frames.end(); ++it ) {
2686+ TagLib::ID3v2::UserTextIdentificationFrame* RGframe =
2687+ dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>( *it );
2688+ if ( RGframe && RGframe->fieldList().size() >= 2 )
2689+ {
2690+ QString desc = TStringToQString( RGframe->description() ).toLower();
2691+ if ( desc == "replaygain_album_gain" ){
2692+ QString sRG = TStringToQString( RGframe->fieldList()[1]);
2693+ parseRGString(sRG);
2694+ }
2695+ if ( desc == "replaygain_track_gain" ){
2696+ QString sRG = TStringToQString( RGframe->fieldList()[1]);
2697+ parseRGString(sRG);
2698+ }
2699+ }
2700+ }
2701+
2702+
2703+
2704
2705 return true;
2706 }
2707@@ -286,7 +329,7 @@
2708 bool SoundSource::processAPETag(TagLib::APE::Tag* ape) {
2709 if (s_bDebugMetadata) {
2710 for(TagLib::APE::ItemListMap::ConstIterator it = ape->itemListMap().begin();
2711- it != ape->itemListMap().end(); ++it) {
2712+ it != ape->itemListMap().end(); ++it) {
2713 qDebug() << "APE" << TStringToQString((*it).first) << "-" << TStringToQString((*it).second.toString());
2714 }
2715 }
2716@@ -295,13 +338,25 @@
2717 QString sBpm = TStringToQString(ape->itemListMap()["BPM"].toString());
2718 processBpmString("APE", sBpm);
2719 }
2720+
2721+ if ( ape->itemListMap().contains("REPLAYGAIN_ALBUM_GAIN") ) {
2722+ QString sRG = TStringToQString(ape->itemListMap()["REPLAYGAIN_ALBUM_GAIN"].toString());
2723+ parseRGString(sRG);
2724+ }
2725+
2726+ //Prefer track gain over album gain.
2727+ if ( ape->itemListMap().contains("REPLAYGAIN_TRACK_GAIN") ) {
2728+ QString sRG = TStringToQString(ape->itemListMap()["REPLAYGAIN_TRACK_GAIN"].toString());
2729+ qDebug()<<"APE value" << sRG;
2730+ parseRGString(sRG);
2731+ }
2732 return true;
2733 }
2734
2735 bool SoundSource::processXiphComment(TagLib::Ogg::XiphComment* xiph) {
2736 if (s_bDebugMetadata) {
2737 for (TagLib::Ogg::FieldListMap::ConstIterator it = xiph->fieldListMap().begin();
2738- it != xiph->fieldListMap().end(); ++it) {
2739+ it != xiph->fieldListMap().end(); ++it) {
2740 qDebug() << "XIPH" << TStringToQString((*it).first) << "-" << TStringToQString((*it).second.toString());
2741 }
2742 }
2743@@ -320,6 +375,20 @@
2744 processBpmString("XIPH-TEMPO", sBpm);
2745 }
2746
2747+
2748+ if (xiph->fieldListMap().contains("REPLAYGAIN_ALBUM_GAIN")) {
2749+ TagLib::StringList rgainString = xiph->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"];
2750+ QString sRG = TStringToQString(rgainString.toString());
2751+ parseRGString(sRG);
2752+ }
2753+
2754+ if (xiph->fieldListMap().contains("REPLAYGAIN_TRACK_GAIN")) {
2755+ TagLib::StringList rgainString = xiph->fieldListMap()["REPLAYGAIN_TRACK_GAIN"];
2756+ QString sRG = TStringToQString(rgainString.toString());
2757+ parseRGString(sRG);
2758+ }
2759+
2760+
2761 return true;
2762 }
2763
2764
2765=== modified file 'mixxx/src/soundsource.h'
2766--- mixxx/src/soundsource.h 2010-09-10 23:41:49 +0000
2767+++ mixxx/src/soundsource.h 2010-11-07 16:11:47 +0000
2768@@ -69,6 +69,7 @@
2769 virtual QString getYear();
2770 virtual QString getGenre();
2771 virtual QString getTrackNumber();
2772+ virtual float getRG();
2773 virtual float getBPM();
2774 virtual int getDuration();
2775 virtual int getBitrate();
2776@@ -83,6 +84,7 @@
2777 virtual void setYear(QString);
2778 virtual void setGenre(QString);
2779 virtual void setTrackNumber(QString);
2780+ virtual void setRG(float);
2781 virtual void setBPM(float);
2782 virtual void setDuration(int);
2783 virtual void setBitrate(int);
2784@@ -99,7 +101,7 @@
2785 bool processXiphComment(TagLib::Ogg::XiphComment* xiph);
2786 bool processMP4Tag(TagLib::MP4::Tag* mp4);
2787 void processBpmString(QString tagName, QString sBpm);
2788-
2789+ void parseRGString(QString sRG);
2790
2791 /** File name */
2792 QString m_qFilename;
2793@@ -112,6 +114,7 @@
2794 QString m_sYear;
2795 QString m_sGenre;
2796 QString m_sTrackNumber;
2797+ float m_fRG;
2798 float m_fBPM;
2799 int m_iDuration;
2800 int m_iBitrate;
2801
2802=== modified file 'mixxx/src/soundsourceproxy.cpp'
2803--- mixxx/src/soundsourceproxy.cpp 2010-10-14 04:25:11 +0000
2804+++ mixxx/src/soundsourceproxy.cpp 2010-11-07 16:11:47 +0000
2805@@ -287,6 +287,7 @@
2806 p->setGenre(sndsrc->getGenre());
2807 p->setComment(sndsrc->getComment());
2808 p->setTrackNumber(sndsrc->getTrackNumber());
2809+ p->setRG(sndsrc->getRG());
2810 p->setBpm(sndsrc->getBPM());
2811 p->setDuration(sndsrc->getDuration());
2812 p->setBitrate(sndsrc->getBitrate());
2813
2814=== modified file 'mixxx/src/trackinfoobject.cpp'
2815--- mixxx/src/trackinfoobject.cpp 2010-10-07 03:05:48 +0000
2816+++ mixxx/src/trackinfoobject.cpp 2010-11-07 16:11:47 +0000
2817@@ -68,6 +68,7 @@
2818 m_iBitrate = XmlParse::selectNodeQString(nodeHeader, "Bitrate").toInt();
2819 m_iLength = XmlParse::selectNodeQString(nodeHeader, "Length").toInt();
2820 m_iTimesPlayed = XmlParse::selectNodeQString(nodeHeader, "TimesPlayed").toInt();
2821+ m_fRG = XmlParse::selectNodeQString(nodeHeader, "replaygain").toFloat();
2822 m_fBpm = XmlParse::selectNodeQString(nodeHeader, "Bpm").toFloat();
2823 m_bBpmConfirm = XmlParse::selectNodeQString(nodeHeader, "BpmConfirm").toInt();
2824 m_fBeatFirst = XmlParse::selectNodeQString(nodeHeader, "BeatFirst").toFloat();
2825@@ -113,6 +114,7 @@
2826 m_iBitrate = 0;
2827 m_iTimesPlayed = 0;
2828 m_fBpm = 0.;
2829+ m_fRG = 0.;
2830 m_bBpmConfirm = false;
2831 m_bIsValid = false;
2832 m_bHeaderParsed = false;
2833@@ -160,6 +162,7 @@
2834 XmlParse::addElement( doc, header, "Bitrate", QString("%1").arg(m_iBitrate));
2835 XmlParse::addElement( doc, header, "Length", QString("%1").arg(m_iLength) );
2836 XmlParse::addElement( doc, header, "TimesPlayed", QString("%1").arg(m_iTimesPlayed) );
2837+ XmlParse::addElement( doc, header, "replaygain", QString("%1").arg(m_fRG) );
2838 XmlParse::addElement( doc, header, "Bpm", QString("%1").arg(m_fBpm) );
2839 XmlParse::addElement( doc, header, "BpmConfirm", QString("%1").arg(m_bBpmConfirm) );
2840 XmlParse::addElement( doc, header, "BeatFirst", QString("%1").arg(m_fBeatFirst) );
2841@@ -261,6 +264,25 @@
2842 return m_bExists;
2843 }
2844
2845+//Added for replaygain
2846+
2847+float TrackInfoObject::getRG() const
2848+{
2849+ QMutexLocker lock(&m_qMutex);
2850+ return m_fRG;
2851+}
2852+
2853+void TrackInfoObject::setRG(float f)
2854+{
2855+ QMutexLocker lock(&m_qMutex);
2856+ bool dirty = m_fRG != f;
2857+ m_fRG = f;
2858+ //qDebug() << "Reported ReplayGain value: " << m_fRG;
2859+ if (dirty)
2860+ setDirty(true);
2861+ emit(RGUpdated(f));
2862+ lock.unlock();
2863+}
2864
2865 float TrackInfoObject::getBpm() const
2866 {
2867@@ -268,6 +290,8 @@
2868 return m_fBpm;
2869 }
2870
2871+
2872+
2873 void TrackInfoObject::setBpm(float f)
2874 {
2875 QMutexLocker lock(&m_qMutex);
2876@@ -275,8 +299,8 @@
2877 m_fBpm = f;
2878 if (dirty)
2879 setDirty(true);
2880+
2881 lock.unlock();
2882-
2883 //Tell the GUI to update the bpm label...
2884 //qDebug() << "TrackInfoObject signaling BPM update to" << f;
2885 emit(bpmUpdated(f));
2886
2887=== modified file 'mixxx/src/trackinfoobject.h'
2888--- mixxx/src/trackinfoobject.h 2010-10-22 21:35:42 +0000
2889+++ mixxx/src/trackinfoobject.h 2010-11-07 16:11:47 +0000
2890@@ -98,6 +98,12 @@
2891 // the TrackInfoObject is created, or when setLocation() is called.
2892 bool exists() const;
2893
2894+
2895+
2896+ /** Returns ReplayGain*/
2897+ float getRG() const;
2898+ /** Set ReplayGain*/
2899+ void setRG(float);
2900 /** Returns BPM */
2901 float getBpm() const;
2902 /** Set BPM */
2903@@ -227,6 +233,7 @@
2904 signals:
2905 void wavesummaryUpdated(TrackInfoObject*);
2906 void bpmUpdated(double bpm);
2907+ void RGUpdated(double replaygain);
2908 void cuesUpdated();
2909 void changed();
2910 void dirty();
2911@@ -301,6 +308,8 @@
2912 int m_iBitrate;
2913 /** Number of times the track has been played */
2914 int m_iTimesPlayed;
2915+ /** Replay Gain volume */
2916+ float m_fRG;
2917 /** Beat per minutes (BPM) */
2918 float m_fBpm;
2919 /** Minimum BPM range. If this is 0.0, then the config min BPM will be used */
2920
2921=== modified file 'mixxx/src/waveform/waveformrenderbeat.cpp'
2922--- mixxx/src/waveform/waveformrenderbeat.cpp 2010-07-15 19:07:16 +0000
2923+++ mixxx/src/waveform/waveformrenderbeat.cpp 2010-11-07 16:11:47 +0000
2924@@ -113,6 +113,8 @@
2925 if(m_dBpm == -1 || m_dBpm == 0)
2926 return;
2927
2928+ slotUpdateTrackSamples(m_pTrackSamples->get());
2929+
2930 if(m_iSampleRate == -1 || m_iSampleRate == 0 || m_iNumSamples == 0)
2931 return;
2932
2933
2934=== modified file 'mixxx/src/waveform/waveformrendersignal.cpp'
2935--- mixxx/src/waveform/waveformrendersignal.cpp 2010-09-10 04:44:28 +0000
2936+++ mixxx/src/waveform/waveformrendersignal.cpp 2010-11-07 16:11:47 +0000
2937@@ -18,12 +18,26 @@
2938 #include "trackinfoobject.h"
2939
2940 WaveformRenderSignal::WaveformRenderSignal(const char* group, WaveformRenderer *parent)
2941- : m_pParent(parent),
2942- m_iWidth(0),
2943- m_iHeight(0),
2944- m_lines(0),
2945- m_pTrack(NULL),
2946- signalColor(255,255,255) {
2947+: m_pParent(parent),
2948+ m_iWidth(0),
2949+ m_iHeight(0),
2950+ m_lines(0),
2951+ m_pTrack(NULL),
2952+ m_fGain(1),
2953+ signalColor(255,255,255) {
2954+
2955+ m_pGain = new ControlObjectThreadMain(
2956+ ControlObject::getControl(ConfigKey(group, "total_gain")));
2957+ if(m_pGain != NULL) {
2958+ connect(m_pGain, SIGNAL(valueChanged(double)),
2959+ this, SLOT(slotUpdateGain(double)));
2960+ }
2961+}
2962+
2963+WaveformRenderSignal::~WaveformRenderSignal() {
2964+ if(m_pGain)
2965+ delete m_pGain;
2966+ m_pGain = NULL;
2967 }
2968
2969 void WaveformRenderSignal::resize(int w, int h) {
2970@@ -31,15 +45,22 @@
2971 m_iHeight = h;
2972 }
2973
2974+
2975+
2976 void WaveformRenderSignal::newTrack(TrackPointer pTrack) {
2977 m_pTrack = pTrack;
2978 }
2979
2980+void WaveformRenderSignal::slotUpdateGain(double v) {
2981+ m_fGain = v;
2982+}
2983+
2984 void WaveformRenderSignal::setup(QDomNode node) {
2985 signalColor.setNamedColor(WWidget::selectNodeQString(node, "SignalColor"));
2986 signalColor = WSkinColor::getCorrectColor(signalColor);
2987 }
2988
2989+
2990 void WaveformRenderSignal::draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double dPlayPos, double rateAdjust) {
2991 if(buffer == NULL)
2992 return;
2993@@ -74,11 +95,11 @@
2994 // Start at curPos minus half the waveform viewer
2995 int thisIndex = iCurPos+2*(i-halfw);
2996 if(thisIndex >= 0 && (thisIndex+1) < numBufferSamples) {
2997- float sampl = baseBuffer[thisIndex];
2998- float sampr = baseBuffer[thisIndex+1];
2999+ float sampl = math_min(1,baseBuffer[thisIndex]*m_fGain);
3000+ float sampr = math_min(1,baseBuffer[thisIndex+1]*m_fGain);
3001 const qreal xPos = i/subpixelsPerPixel;
3002 m_lines[i].setLine(xPos, -sampr*0.40*m_iHeight,
3003- xPos, sampl*0.40*m_iHeight);
3004+ xPos, sampl*0.40*m_iHeight);
3005 } else {
3006 m_lines[i].setLine(0,0,0,0);
3007 }
3008@@ -88,4 +109,5 @@
3009 pPainter->drawLines(m_lines.data(), subpixelWidth);
3010
3011 pPainter->restore();
3012+
3013 }
3014
3015=== modified file 'mixxx/src/waveform/waveformrendersignal.h'
3016--- mixxx/src/waveform/waveformrendersignal.h 2010-07-15 19:07:16 +0000
3017+++ mixxx/src/waveform/waveformrendersignal.h 2010-11-07 16:11:47 +0000
3018@@ -10,6 +10,7 @@
3019
3020 #include "renderobject.h"
3021
3022+
3023 class QDomNode;
3024 class QPainter;
3025 class QPaintEvent;
3026@@ -23,13 +24,22 @@
3027 Q_OBJECT
3028 public:
3029 WaveformRenderSignal(const char *group, WaveformRenderer *parent);
3030+ ~WaveformRenderSignal();
3031 void resize(int w, int h);
3032 void setup(QDomNode node);
3033 void draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust);
3034+ void drawgain(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust, double gain);
3035 void newTrack(TrackPointer pTrack);
3036
3037+public slots:
3038+ void slotUpdateGain(double gain);
3039+
3040 private:
3041+ float m_fGain;
3042+
3043 WaveformRenderer *m_pParent;
3044+ ControlObjectThreadMain *m_pGain;
3045+
3046 int m_iWidth, m_iHeight;
3047 QVector<QLineF> m_lines;
3048 TrackPointer m_pTrack;