Merge lp:~l0rdt/mixxx/features_replaygain into lp:~mixxxdevelopers/mixxx/trunk
- features_replaygain
- Merge into trunk
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 | ||||||||||||
Related bugs: |
|
||||||||||||
Related blueprints: |
ReplayGain Support
(Medium)
|
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.
Commit message
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.
Sean M. Pappalardo (pegasus-renegadetech) wrote : | # |
- 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 ;)
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?
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
- 2469. By Vittorio Colao
-
Fixed a typo in dlgprefreplayga
in.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 createPrepareVi
ewAnalyserQueue 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
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' |
35 | Binary 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><ul> |
1423 | +<li>Increase your latency if you hear pops during playback</li> |
1424 | +<li>Reduce your latency to improve Mixxx's responsiveness</li> |
1425 | +</ul></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; |
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.