Merge lp:~kabelfrickler/mixxx/modplug into lp:~mixxxdevelopers/mixxx/trunk
- modplug
- Merge into trunk
Status: | Merged |
---|---|
Merge reported by: | Daniel Schürmann |
Merged at revision: | not available |
Proposed branch: | lp:~kabelfrickler/mixxx/modplug |
Merge into: | lp:~mixxxdevelopers/mixxx/trunk |
Diff against target: |
1255 lines (+1095/-0) 11 files modified
mixxx/SConstruct (+1/-0) mixxx/build/features.py (+34/-0) mixxx/src/dlgpreferences.cpp (+28/-0) mixxx/src/dlgpreferences.h (+9/-0) mixxx/src/dlgprefmodplug.cpp (+173/-0) mixxx/src/dlgprefmodplug.h (+36/-0) mixxx/src/dlgprefmodplugdlg.ui (+534/-0) mixxx/src/mixxx.cpp (+12/-0) mixxx/src/soundsourcemodplug.cpp (+198/-0) mixxx/src/soundsourcemodplug.h (+60/-0) mixxx/src/soundsourceproxy.cpp (+10/-0) |
To merge this branch: | bzr merge lp:~kabelfrickler/mixxx/modplug |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sean M. Pappalardo | Abstain | ||
Daniel Schürmann | Approve | ||
Stefan Nürnberger (community) | Approve | ||
Review via email: mp+153438@code.launchpad.net |
This proposal supersedes a proposal from 2013-03-14.
Commit message
Description of the change
Add module tracker support through libmodplug
This branch adds a soundsource for files supported by modplug (MOD/MED/
It also includes a preferences dialog through which the decoder can be configured.
Module tracker formats do not play well with seeking, therefore files are decoded to 16bit stereo samples on track load and kept in a QByteArray until the soundsource is destroyed. Maximum size of this buffer per track (i.e. per soundsource instance) is also configurable through the preferences dialog.
Building this requires libmodplug and the modplug.h header file. Most linux distributions offer modplug packages. I have not attempted a build under Mac or Windows.
Modplug support is activated by appending "modplug=1" to the scons command. This will also add the __MODPLUG__ define to the compiler flags. All code concerning modplug is guarded by this define.
For modplug to work correctly the settings which are stored in the mixxx.cfg need to be applied once before loading a module file. Currently this is done in mixxx.cpp when the program loads by creating an instance of the preferences dialog and letting it deal with the configuration. Maybe there is a more elegant way?
--
v1 -> v2
* reverted all unrelated changes
* clean up some code
* merged with latest trunk to prevent conflicts
Stefan Nürnberger (kabelfrickler) wrote : Posted in a previous version of this proposal | # |
- 3302. By Stefan Nuernberger <email address hidden>
-
Correct typo in features.py
Stefan Nürnberger (kabelfrickler) wrote : | # |
There is a typo in features.py in the description for the modplug switch.
s/libmbodplug/
I just fixed that in the branch. Otherwise it looks fine for me now.
The modplug feature is disabled by default so this should not break anything.
Stefan Nürnberger (kabelfrickler) wrote : | # |
Ok, so a resubmit was technically superfluous since changes in the branch are tracked directly instead of merge requests that rely on a specific revision. I'm just learning how to handle launchpad. Sorry for the noise ;-)
Daniel Schürmann (daschuer) wrote : | # |
Hi Stefan,
first of all thank you very much for your great work.
I have just tested it with Ubuntu Precise 32 bit and it works well.
IMO it is good enough to be part of an upcoming Mixxx 1.12.
However I have some comments and minor issues:
1)
I have noticed a significant delay between loading a track and display the
waveform. I assume that this is caused by buffering the track. Although the
track seems to be playable before, Mixxx should always give a feedback what
it is doing.
I have made lately some changes to visualize the load progress of a track for
poor hardware. Maybe we could integrate the buffering period into it somehow.
2)
I am not familiar with the modlib options but IMO the preference page looks a
little overengineered. Are all options intended to be set by the user?
If not, you should consider to only display them when starting Mixxx with the
--developer option.
3)
When are the options adopted? A brief hint about it could be useful.
4)
Some parts of the new code is not complaint to our coding guidelines.
http://
Mixxx code does not complete fulfill these guidelines yet, but the work is
in progress.
5)
Performance of QByteArray: It might be possible to tweak your code for performance
if you use .at() instead of [] and if you could foresee the required amount of memory and
use QByteArray:
It should also be possible to get rid of the tempbuf and save one copy instruction, if you allow ModPlug_Read to write directly to QByteArray.
Kind regards,
Daniel
Stefan Nürnberger (kabelfrickler) wrote : | # |
Thanks for the feedback. I would really like to see this in 1.12.
> 1)
> I have noticed a significant delay between loading a track and display the
> waveform. I assume that this is caused by buffering the track. Although the
> track seems to be playable before, Mixxx should always give a feedback what
> it is doing.
>
> I have made lately some changes to visualize the load progress of a track for
> poor hardware. Maybe we could integrate the buffering period into it somehow.
I'll investigate. It seemed pretty fluent on my hardware. I realized mixxx is saving the waveform of already analyzed tracks, so it will probably only affect the first load of a track. That was a little annoying when playing around with the "stereo separation" and other settings of modplug, since the waveform is not adapted accordingly. Is there a way to force re-analyzing the waveform?
> 2)
> I am not familiar with the modlib options but IMO the preference page looks a
> little overengineered. Are all options intended to be set by the user?
> If not, you should consider to only display them when starting Mixxx with the
> --developer option.
Yes it is a little messy ;-)
They are all options to be set by the user, however most shouldn't be touched. The OpenMPT wiki has some hints about this.
http://
I could hide those options e.g. with a "show advanced settings" checkbox. I don't think using the --developer switch is very intuitive for that.
> 3)
> When are the options adopted? A brief hint about it could be useful.
Most options take effect "immediately" after apply (pressing the "Ok" button), i.e. they are affecting tracks that are "played" (buffered) in this instant. For simplicity, let's say they take effect on next track load. A hint will be added.
> 4)
> Some parts of the new code is not complaint to our coding guidelines.
> http://
> Mixxx code does not complete fulfill these guidelines yet, but the work is
> in progress.
I mainly tried to be consistent with the other soundsource files. I will refactor according to the guidelines. I usually use doxygen comment style but as you are not using doxygen I will switch to plain C++ style as requested by the styleguide.
> 5)
> Performance of QByteArray: It might be possible to tweak your code for
> performance
> if you use .at() instead of [] and if you could foresee the required amount of
> memory and
The subscript operator of QByteArray has a performance drawback over .at()? That's curious. I guess I have to look at the source of that sometime ;)
> use QByteArray:
> append() calls.
Yes, I know about reserve() and the semantics of append(). I see if I can do something to improve the situation. But as song length prediction is not very reliable with modules, I do not know if much can be gained.
> It should also be possible to get rid of the tempbuf and save one copy
> instruction, if you allow ModPlug_Read to write directly to QByteArray.
Yes, I don't like this additional copying either (apart from being incredibly easy).
Daniel Schürmann (daschuer) wrote : | # |
to 2)
A "show advanced settings" checkbox is a good idea.
Maybe we should add the wiki link to the GUI as well.
http://
to 4)
Yes, the other soundsource files need a code cleanup to. But because of the risc of introduce bugs, no one has done it.
I like doxygen and I would appreciate if we start to add Doxgen tags.
This couls be done complient to Mixxx coding guidelines like this
//!
We have actual a doxygen doku online here http://
to 5)
If you want to measure your code performance that can be easyly be done by adding
ScopedTimer t("SoundSourceM
The at() vs [] issue is from the Qt doku.
The reserve() issue is from my own expereince.
It would be nice to see the measure results.
- 3303. By Stefan Nuernberger <email address hidden>
-
Refactoring SoundSourceModPlug. Obey coding styleguide. Estimate sample buffer size. Get rid of temp buffer in open().
Stefan Nürnberger (kabelfrickler) wrote : | # |
I integrated the ScopedTimer. Here are results from the latest version (using .at() and .reserve() and without the temp buffer):
Stat("SoundSour
I loaded tracks of varying length from 0:30 to 17:00 minutes (up to ~180MB of samples). The reported times look a little too optimistic for me (maximum of 1,2 seconds?). Loading long tracks does not feel that snappy. I'll experiment a little with "just in time"-style loading...
Daniel Schürmann (daschuer) wrote : | # |
Thank you again for your work.
I am not sure if you can gain more performance from the soundsource with reasonable effort.
I am afraid Mixxx itselfe waste time else where.
For example here:
http://
http://
Just filed a Bug #1156569 ;-)
- 3304. By Stefan Nuernberger <email address hidden>
-
Use memcpy for SoundSourceModP
lug::read( ). Add ScopedTimer for SoundSourceModP lug::open( ). - 3305. By Stefan Nuernberger <email address hidden>
-
Refactored modplug preferences dialog. Introduced advanced options switch. Adhere to coding style guide.
- 3306. By Stefan Nuernberger <email address hidden>
-
insert spaces in front of class scope identifiers as demanded by style guide.
- 3307. By Stefan Nuernberger <email address hidden>
-
Fix a comment about samplerate.
- 3308. By Stefan Nuernberger <email address hidden>
-
Changed placement of controls in modplug preferences dialog.
Stefan Nürnberger (kabelfrickler) wrote : | # |
Ok, I think the branch is ready now. I tried on-demand loading but dropped the idea since it sometimes caused sound distortions when more than one sound source was active.
Performance seems acceptable for me now. I changed the default interpolation to linear, which is a bit faster and still has good quality.
Sean M. Pappalardo (pegasus-renegadetech) wrote : | # |
Wow, nice work, Stefan! I haven't had a chance to review this at all yet, but I'm glad you put in the effort! Since it's a new SoundSource, it won't take too long to get it merged into trunk.
Before we do, though, please read and sign the Mixxx Contributor Agreement found here: https:/
Let us know when you have.
Sean M. Pappalardo (pegasus-renegadetech) wrote : | # |
I do have one question in my initial glance: why do you choose 16 bit 44.1kHz if the native rendering is 32-bit? I would suggest leaving it at its native format and let Mixxx's mixing engine do any conversion. (This allows anyone running a high-end sound card at 32-bit sample size to get full quality.)
Stefan Nürnberger (kabelfrickler) wrote : | # |
I have just signed the agreement.
As for the 16 bit:
I was under the impression that mixxx requests 16 bit integer samples from all soundsources. The SAMPLE typedef at least is a 'short int'. Since I need to buffer the whole track, having 32 bit samples would require double the space, which in my opinion would be a real waste.
As for high quality sound output: I thought this would rather be a matter of the sampling frequency instead of the sample width (e.g. 96kHz soundcards). I don't know why modplug uses 32 bit integers internally in the first place.
If mixxx is ready to support 32 bit samples, I could of course make this configurable through the preferences dialog. But currently I don't know how to handle the soundsource::read() call then, since I assume the destination is still expecting 16 bit samples. And as I understand Bug #1156569 filed by Daniel (see previous comments), mixxx does some rather nasty conversions there.
Daniel Schürmann (daschuer) wrote : | # |
Thank you Stefan, for me it is ready for merge now.
I will commit the merge if no one has any objection in the next days.
I will put your name as contributor into the about box. Is "Stefan Nürnberger" OK?
Stefan Nürnberger (kabelfrickler) wrote : | # |
Yes, that's my name :)
Thank you.
Sean M. Pappalardo (pegasus-renegadetech) wrote : | # |
I'm no expert on the soundsource code, but I remember RJ saying that Mixxx mixes internally with 32-bit floats. But if the interface to the mixing engine is 16-bit int, there's nothing you can do at this point. Thanks for the clarification though. It will be helpful if we do decide to improve the SoundSource interface.
(As a result of my lack of expertise, I don't feel qualified to formally review this.)
Daniel Schürmann (daschuer) wrote : | # |
Merged to lp:mixxx revision 3348.
Thank you Stefan!
Preview Diff
1 | === modified file 'mixxx/SConstruct' |
2 | --- mixxx/SConstruct 2013-02-09 00:38:39 +0000 |
3 | +++ mixxx/SConstruct 2013-03-19 15:51:49 +0000 |
4 | @@ -48,6 +48,7 @@ |
5 | features.Optimize, |
6 | features.FAAD, |
7 | features.WavPack, |
8 | + features.ModPlug, |
9 | features.TestSuite, |
10 | features.LADSPA, |
11 | features.MSVCDebug, |
12 | |
13 | === modified file 'mixxx/build/features.py' |
14 | --- mixxx/build/features.py 2013-03-11 06:57:23 +0000 |
15 | +++ mixxx/build/features.py 2013-03-19 15:51:49 +0000 |
16 | @@ -484,6 +484,40 @@ |
17 | '%s/RealTime.cpp']) |
18 | return sources |
19 | |
20 | + |
21 | +class ModPlug(Feature): |
22 | + def description(self): |
23 | + return "Modplug module decoder plugin" |
24 | + |
25 | + def enabled(self, build): |
26 | + build.flags['modplug'] = util.get_flags(build.env, 'modplug', 0) |
27 | + if int(build.flags['modplug']): |
28 | + return True |
29 | + return False |
30 | + |
31 | + def add_options(self, build, vars): |
32 | + vars.Add('modplug', 'Set to 1 to enable libmodplug based module tracker support.', 0) |
33 | + |
34 | + def configure(self, build, conf): |
35 | + if not self.enabled(build): |
36 | + return |
37 | + |
38 | + build.env.Append(CPPDEFINES = '__MODPLUG__') |
39 | + |
40 | + have_modplug_h = conf.CheckHeader('libmodplug/modplug.h') |
41 | + have_modplug = conf.CheckLib(['modplug','libmodplug'], autoadd=True) |
42 | + |
43 | + if not have_modplug_h: |
44 | + raise Exception('Could not find libmodplug development headers.') |
45 | + |
46 | + if not have_modplug: |
47 | + raise Exception('Could not find libmodplug shared library.') |
48 | + |
49 | + def sources(self, build): |
50 | + build.env.Uic4('dlgprefmodplugdlg.ui') |
51 | + return ['soundsourcemodplug.cpp', 'dlgprefmodplug.cpp'] |
52 | + |
53 | + |
54 | class FAAD(Feature): |
55 | def description(self): |
56 | return "FAAD AAC audio file decoder plugin" |
57 | |
58 | === modified file 'mixxx/src/dlgpreferences.cpp' |
59 | --- mixxx/src/dlgpreferences.cpp 2012-11-27 16:19:52 +0000 |
60 | +++ mixxx/src/dlgpreferences.cpp 2013-03-19 15:51:49 +0000 |
61 | @@ -37,6 +37,10 @@ |
62 | #include "dlgprefbpm.h" |
63 | #endif |
64 | |
65 | +#ifdef __MODPLUG__ |
66 | + #include "dlgprefmodplug.h" |
67 | +#endif |
68 | + |
69 | #include "dlgpreferences.h" |
70 | #include "dlgprefsound.h" |
71 | #include "controllers/dlgprefmappablecontroller.h" |
72 | @@ -109,6 +113,10 @@ |
73 | m_wshoutcast = new DlgPrefShoutcast(this, config); |
74 | addPageWidget(m_wshoutcast); |
75 | #endif |
76 | +#ifdef __MODPLUG__ |
77 | + m_wmodplug = new DlgPrefModplug(this, config); |
78 | + addPageWidget(m_wmodplug); |
79 | +#endif |
80 | m_wNoControllers = new DlgPrefNoControllers(this, config); |
81 | addPageWidget(m_wNoControllers); |
82 | setupControllerWidgets(); |
83 | @@ -152,6 +160,10 @@ |
84 | connect(this, SIGNAL(showDlg()), m_wshoutcast,SLOT(slotUpdate())); |
85 | #endif |
86 | |
87 | +#ifdef __MODPLUG__ |
88 | + connect(this, SIGNAL(showDlg()), m_wmodplug,SLOT(slotUpdate())); |
89 | +#endif |
90 | + |
91 | #ifdef __VINYLCONTROL__ |
92 | connect(buttonBox, SIGNAL(accepted()), m_wvinylcontrol, SLOT(slotApply())); //It's important for this to be before the |
93 | //connect for wsound... |
94 | @@ -173,6 +185,9 @@ |
95 | #ifdef __SHOUTCAST__ |
96 | connect(buttonBox, SIGNAL(accepted()), m_wshoutcast,SLOT(slotApply())); |
97 | #endif |
98 | +#ifdef __MODPLUG__ |
99 | + connect(buttonBox, SIGNAL(accepted()), m_wmodplug,SLOT(slotApply())); |
100 | +#endif |
101 | |
102 | //Update the library when you change the options |
103 | /*if (m_pTrack && wplaylist) |
104 | @@ -276,6 +291,15 @@ |
105 | m_pShoutcastButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); |
106 | m_pShoutcastButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); |
107 | #endif |
108 | + |
109 | +#ifdef __MODPLUG__ |
110 | + m_pModplugButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); |
111 | + m_pModplugButton->setIcon(0, QIcon(":/images/preferences/ic_preferences_sampler.png")); |
112 | + m_pModplugButton->setText(0, tr("Modplug Decoder")); |
113 | + m_pModplugButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); |
114 | + m_pModplugButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); |
115 | +#endif |
116 | + |
117 | connect(contentsTreeWidget, |
118 | SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), |
119 | this, SLOT(changePage(QTreeWidgetItem *, QTreeWidgetItem*))); |
120 | @@ -321,6 +345,10 @@ |
121 | } else if (current == m_pShoutcastButton) { |
122 | pagesWidget->setCurrentWidget(m_wshoutcast->parentWidget()->parentWidget()); |
123 | #endif |
124 | +#ifdef __MODPLUG__ |
125 | + } else if (current == m_pModplugButton) { |
126 | + pagesWidget->setCurrentWidget(m_wmodplug->parentWidget()->parentWidget()); |
127 | +#endif |
128 | //Handle selection of controller items |
129 | } else if (m_controllerWindowLinks.indexOf(current) >= 0) { |
130 | int index = m_controllerWindowLinks.indexOf(current); |
131 | |
132 | === modified file 'mixxx/src/dlgpreferences.h' |
133 | --- mixxx/src/dlgpreferences.h 2012-04-25 15:40:15 +0000 |
134 | +++ mixxx/src/dlgpreferences.h 2013-03-19 15:51:49 +0000 |
135 | @@ -45,6 +45,9 @@ |
136 | class SkinLoader; |
137 | class PlayerManager; |
138 | class VinylControlManager; |
139 | +#ifdef __MODPLUG__ |
140 | +class DlgPrefModplug; |
141 | +#endif |
142 | |
143 | /** |
144 | *@author Tue & Ken Haste Andersen |
145 | @@ -93,6 +96,9 @@ |
146 | DlgPrefNoVinyl* m_wnovinylcontrol; |
147 | DlgPrefShoutcast* m_wshoutcast; |
148 | DlgPrefReplayGain* m_wreplaygain; |
149 | +#ifdef __MODPLUG__ |
150 | + DlgPrefModplug* m_wmodplug; |
151 | +#endif |
152 | |
153 | /* |
154 | QScrollArea* m_sasound; |
155 | @@ -120,6 +126,9 @@ |
156 | QTreeWidgetItem* m_pVinylControlButton; |
157 | QTreeWidgetItem* m_pShoutcastButton; |
158 | QTreeWidgetItem* m_pReplayGainButton; |
159 | +#ifdef __MODPLUG__ |
160 | + QTreeWidgetItem* m_pModplugButton; |
161 | +#endif |
162 | QTreeWidgetItem* m_pControllerTreeItem; |
163 | QList<QTreeWidgetItem*> m_controllerWindowLinks; |
164 | |
165 | |
166 | === added file 'mixxx/src/dlgprefmodplug.cpp' |
167 | --- mixxx/src/dlgprefmodplug.cpp 1970-01-01 00:00:00 +0000 |
168 | +++ mixxx/src/dlgprefmodplug.cpp 2013-03-19 15:51:49 +0000 |
169 | @@ -0,0 +1,173 @@ |
170 | + |
171 | +#include <QtDebug> |
172 | + |
173 | +#include "dlgprefmodplug.h" |
174 | +#include "ui_dlgprefmodplugdlg.h" |
175 | + |
176 | +#include "configobject.h" |
177 | +#include "soundsourcemodplug.h" |
178 | + |
179 | +#define CONFIG_KEY "[Modplug]" |
180 | + |
181 | +DlgPrefModplug::DlgPrefModplug(QWidget *parent, |
182 | + ConfigObject<ConfigValue> *_config) : |
183 | + QDialog(parent), |
184 | + m_pUi(new Ui::DlgPrefModplug), |
185 | + m_pConfig(_config) |
186 | +{ |
187 | + m_pUi->setupUi(this); |
188 | + m_pUi->advancedSettings->setVisible(m_pUi->showAdvanced->isChecked()); |
189 | +} |
190 | + |
191 | +DlgPrefModplug::~DlgPrefModplug() |
192 | +{ |
193 | + delete m_pUi; |
194 | +} |
195 | + |
196 | +void DlgPrefModplug::slotApply() |
197 | +{ |
198 | + applySettings(); |
199 | + saveSettings(); |
200 | +} |
201 | + |
202 | +void DlgPrefModplug::slotUpdate() |
203 | +{ |
204 | + loadSettings(); |
205 | +} |
206 | + |
207 | +void DlgPrefModplug::loadSettings() |
208 | +{ |
209 | + m_pUi->memoryLimit->setValue(m_pConfig->getValueString( |
210 | + ConfigKey(CONFIG_KEY,"PerTrackMemoryLimitMB"),"256").toInt()); |
211 | + m_pUi->oversampling->setChecked(m_pConfig->getValueString( |
212 | + ConfigKey(CONFIG_KEY,"OversamplingEnabled"),"1") == QString("1")); |
213 | + m_pUi->noiseReduction->setChecked(m_pConfig->getValueString( |
214 | + ConfigKey(CONFIG_KEY,"NoiseReductionEnabled"),"0") == QString("1")); |
215 | + m_pUi->stereoSeparation->setValue(m_pConfig->getValueString( |
216 | + ConfigKey(CONFIG_KEY,"StereoSeparation"),"1").toInt()); |
217 | + m_pUi->maxMixChannels->setValue(m_pConfig->getValueString( |
218 | + ConfigKey(CONFIG_KEY,"MaxMixChannels"),"128").toInt()); |
219 | + m_pUi->resampleMode->setCurrentIndex(m_pConfig->getValueString( |
220 | + ConfigKey(CONFIG_KEY,"ResamplingMode"),"1").toInt()); |
221 | + m_pUi->reverb->setChecked(m_pConfig->getValueString( |
222 | + ConfigKey(CONFIG_KEY,"ReverbEnabled"),"0") == QString("1")); |
223 | + m_pUi->reverbDepth->setValue(m_pConfig->getValueString( |
224 | + ConfigKey(CONFIG_KEY,"ReverbLevel"),"50").toInt()); |
225 | + m_pUi->reverbDelay->setValue(m_pConfig->getValueString( |
226 | + ConfigKey(CONFIG_KEY,"ReverbDelay"),"50").toInt()); |
227 | + m_pUi->megabass->setChecked(m_pConfig->getValueString( |
228 | + ConfigKey(CONFIG_KEY,"MegabassEnabled"),"0") == QString("1")); |
229 | + m_pUi->bassDepth->setValue(m_pConfig->getValueString( |
230 | + ConfigKey(CONFIG_KEY,"MegabassLevel"),"50").toInt()); |
231 | + m_pUi->bassCutoff->setValue(m_pConfig->getValueString( |
232 | + ConfigKey(CONFIG_KEY,"MegabassCutoff"),"50").toInt()); |
233 | + m_pUi->surround->setChecked(m_pConfig->getValueString( |
234 | + ConfigKey(CONFIG_KEY,"SurroundEnabled"),"0") == QString("1")); |
235 | + m_pUi->surroundDepth->setValue(m_pConfig->getValueString( |
236 | + ConfigKey(CONFIG_KEY,"SurroundLevel"),"50").toInt()); |
237 | + m_pUi->surroundDelay->setValue(m_pConfig->getValueString( |
238 | + ConfigKey(CONFIG_KEY,"SurroundDelay"),"50").toInt()); |
239 | +} |
240 | + |
241 | +void DlgPrefModplug::saveSettings() |
242 | +{ |
243 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"PerTrackMemoryLimitMB"), |
244 | + ConfigValue(m_pUi->memoryLimit->value())); |
245 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"OversamplingEnabled"), |
246 | + ConfigValue(m_pUi->oversampling->isChecked())); |
247 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"NoiseReductionEnabled"), |
248 | + ConfigValue(m_pUi->noiseReduction->isChecked())); |
249 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"StereoSeparation"), |
250 | + ConfigValue(m_pUi->stereoSeparation->value())); |
251 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"MaxMixChannels"), |
252 | + ConfigValue(m_pUi->maxMixChannels->value())); |
253 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"ResamplingMode"), |
254 | + ConfigValue(m_pUi->resampleMode->currentIndex())); |
255 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"ReverbEnabled"), |
256 | + ConfigValue(m_pUi->reverb->isChecked())); |
257 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"ReverbLevel"), |
258 | + ConfigValue(m_pUi->reverbDepth->value())); |
259 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"ReverbDelay"), |
260 | + ConfigValue(m_pUi->reverbDelay->value())); |
261 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"MegabassEnabled"), |
262 | + ConfigValue(m_pUi->megabass->isChecked())); |
263 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"MegabassLevel"), |
264 | + ConfigValue(m_pUi->bassDepth->value())); |
265 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"MegabassCutoff"), |
266 | + ConfigValue(m_pUi->bassCutoff->value())); |
267 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"SurroundEnabled"), |
268 | + ConfigValue(m_pUi->surround->isChecked())); |
269 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"SurroundLevel"), |
270 | + ConfigValue(m_pUi->surroundDepth->value())); |
271 | + m_pConfig->set(ConfigKey(CONFIG_KEY,"SurroundDelay"), |
272 | + ConfigValue(m_pUi->surroundDelay->value())); |
273 | +} |
274 | + |
275 | +void DlgPrefModplug::applySettings() |
276 | +{ |
277 | + // read ui parameters and configure soundsource |
278 | + unsigned int bufferSizeLimit = m_pUi->memoryLimit->value() << 20; |
279 | + ModPlug::ModPlug_Settings settings; |
280 | + |
281 | + // Note that ModPlug always decodes sound at 44.1kHz, 32 bit, stereo |
282 | + // and then down-mixes to the settings you choose. |
283 | + // Currently this is fixed to 16bit 44.1kHz stereo |
284 | + |
285 | + // Number of channels - 1 for mono or 2 for stereo |
286 | + settings.mChannels = 2; |
287 | + // Bits per sample - 8, 16, or 32 |
288 | + settings.mBits = 16; |
289 | + // Sampling rate - 11025, 22050, or 44100 |
290 | + settings.mFrequency = 44100; |
291 | + |
292 | + // enabled features flags |
293 | + settings.mFlags = 0; |
294 | + if (m_pUi->oversampling->isChecked()) |
295 | + settings.mFlags |= ModPlug::MODPLUG_ENABLE_OVERSAMPLING; |
296 | + if (m_pUi->noiseReduction->isChecked()) |
297 | + settings.mFlags |= ModPlug::MODPLUG_ENABLE_NOISE_REDUCTION; |
298 | + if (m_pUi->reverb->isChecked()) |
299 | + settings.mFlags |= ModPlug::MODPLUG_ENABLE_REVERB; |
300 | + if (m_pUi->megabass->isChecked()) |
301 | + settings.mFlags |= ModPlug::MODPLUG_ENABLE_MEGABASS; |
302 | + if (m_pUi->surround->isChecked()) |
303 | + settings.mFlags |= ModPlug::MODPLUG_ENABLE_SURROUND; |
304 | + |
305 | + switch (m_pUi->resampleMode->currentIndex()) { |
306 | + case 0: // nearest neighbor |
307 | + settings.mResamplingMode = ModPlug::MODPLUG_RESAMPLE_NEAREST; |
308 | + break; |
309 | + case 1: // linear |
310 | + settings.mResamplingMode = ModPlug::MODPLUG_RESAMPLE_LINEAR; |
311 | + break; |
312 | + case 2: // cubic spline |
313 | + settings.mResamplingMode = ModPlug::MODPLUG_RESAMPLE_SPLINE; |
314 | + break; |
315 | + case 3: // 8 tap FIR (also default) |
316 | + default: |
317 | + settings.mResamplingMode = ModPlug::MODPLUG_RESAMPLE_FIR; |
318 | + break; |
319 | + } |
320 | + |
321 | + // stereo separation 1(joint)-256(fully separated channels) |
322 | + settings.mStereoSeparation = m_pUi->stereoSeparation->value(); |
323 | + // maximum number of mix channels (16-256) |
324 | + settings.mMaxMixChannels = m_pUi->maxMixChannels->value(); |
325 | + // Reverb level 0(quiet)-100(loud) |
326 | + settings.mReverbDepth = m_pUi->reverbDepth->value(); |
327 | + // Reverb delay in ms, usually 40-200ms |
328 | + settings.mReverbDelay = m_pUi->reverbDelay->value(); |
329 | + // XBass level 0(quiet)-100(loud) |
330 | + settings.mBassAmount = m_pUi->bassDepth->value(); |
331 | + // XBass cutoff in Hz 10-100 |
332 | + settings.mBassRange = m_pUi->bassCutoff->value(); |
333 | + // Surround level 0(quiet)-100(heavy) |
334 | + settings.mSurroundDepth = m_pUi->surroundDepth->value(); |
335 | + // Surround front-rear delay in ms, usually 5-40ms |
336 | + settings.mSurroundDelay = m_pUi->surroundDelay->value(); |
337 | + // Number of times to loop. Zero prevents looping. -1 loops forever. |
338 | + settings.mLoopCount = 0; |
339 | + |
340 | + // apply modplug settings |
341 | + SoundSourceModPlug::configure(bufferSizeLimit, settings); |
342 | +} |
343 | |
344 | === added file 'mixxx/src/dlgprefmodplug.h' |
345 | --- mixxx/src/dlgprefmodplug.h 1970-01-01 00:00:00 +0000 |
346 | +++ mixxx/src/dlgprefmodplug.h 2013-03-19 15:51:49 +0000 |
347 | @@ -0,0 +1,36 @@ |
348 | +// dlgprefmodplug.h - modplug settings dialog |
349 | +// created 2013 by Stefan Nuernberger <kabelfrickler@gmail.com> |
350 | + |
351 | +#ifndef DLGPREFMODPLUG_H |
352 | +#define DLGPREFMODPLUG_H |
353 | + |
354 | +#include <QDialog> |
355 | +#include "configobject.h" |
356 | + |
357 | +namespace Ui { |
358 | +class DlgPrefModplug; |
359 | +} |
360 | + |
361 | +class DlgPrefModplug : public QDialog |
362 | +{ |
363 | + Q_OBJECT |
364 | + |
365 | + public: |
366 | + explicit DlgPrefModplug(QWidget *parent, ConfigObject<ConfigValue> *_config); |
367 | + ~DlgPrefModplug(); |
368 | + |
369 | + public slots: |
370 | + /** Apply changes to widget */ |
371 | + void slotApply(); |
372 | + void slotUpdate(); |
373 | + |
374 | + void loadSettings(); |
375 | + void saveSettings(); |
376 | + void applySettings(); |
377 | + |
378 | + private: |
379 | + Ui::DlgPrefModplug *m_pUi; |
380 | + ConfigObject<ConfigValue> *m_pConfig; |
381 | +}; |
382 | + |
383 | +#endif // DLGPREFMODPLUG_H |
384 | |
385 | === added file 'mixxx/src/dlgprefmodplugdlg.ui' |
386 | --- mixxx/src/dlgprefmodplugdlg.ui 1970-01-01 00:00:00 +0000 |
387 | +++ mixxx/src/dlgprefmodplugdlg.ui 2013-03-19 15:51:49 +0000 |
388 | @@ -0,0 +1,534 @@ |
389 | +<?xml version="1.0" encoding="UTF-8"?> |
390 | +<ui version="4.0"> |
391 | + <class>DlgPrefModplug</class> |
392 | + <widget class="QDialog" name="DlgPrefModplug"> |
393 | + <property name="geometry"> |
394 | + <rect> |
395 | + <x>0</x> |
396 | + <y>0</y> |
397 | + <width>791</width> |
398 | + <height>549</height> |
399 | + </rect> |
400 | + </property> |
401 | + <property name="windowTitle"> |
402 | + <string>Modplug Preferences</string> |
403 | + </property> |
404 | + <layout class="QGridLayout" name="gridLayout"> |
405 | + <property name="spacing"> |
406 | + <number>10</number> |
407 | + </property> |
408 | + <item row="2" column="5"> |
409 | + <widget class="QSpinBox" name="memoryLimitSpin"> |
410 | + <property name="minimum"> |
411 | + <number>16</number> |
412 | + </property> |
413 | + <property name="maximum"> |
414 | + <number>512</number> |
415 | + </property> |
416 | + <property name="value"> |
417 | + <number>256</number> |
418 | + </property> |
419 | + </widget> |
420 | + </item> |
421 | + <item row="12" column="3"> |
422 | + <spacer name="verticalSpacer"> |
423 | + <property name="orientation"> |
424 | + <enum>Qt::Vertical</enum> |
425 | + </property> |
426 | + <property name="sizeHint" stdset="0"> |
427 | + <size> |
428 | + <width>20</width> |
429 | + <height>40</height> |
430 | + </size> |
431 | + </property> |
432 | + </spacer> |
433 | + </item> |
434 | + <item row="9" column="0"> |
435 | + <widget class="QCheckBox" name="showAdvanced"> |
436 | + <property name="text"> |
437 | + <string>Show advanced settings</string> |
438 | + </property> |
439 | + </widget> |
440 | + </item> |
441 | + <item row="0" column="0" colspan="6"> |
442 | + <widget class="QLabel" name="label_11"> |
443 | + <property name="text"> |
444 | + <string>Decoding options for libmodplug. Module files are decoded at once and kept in RAM to allow for seeking and smooth operation in Mixxx. About 10MB of RAM are required for 1 minute of audio.</string> |
445 | + </property> |
446 | + <property name="wordWrap"> |
447 | + <bool>true</bool> |
448 | + </property> |
449 | + </widget> |
450 | + </item> |
451 | + <item row="11" column="0" colspan="6"> |
452 | + <widget class="QLabel" name="label_12"> |
453 | + <property name="text"> |
454 | + <string>All settings take effect on next track load. Currently loaded tracks are not affected. For an explanation of these settings, see the <a href="http://wiki.openmpt.org/Manual:_Setup/Player">OpenMPT manual</a>.</string> |
455 | + </property> |
456 | + <property name="wordWrap"> |
457 | + <bool>true</bool> |
458 | + </property> |
459 | + <property name="openExternalLinks"> |
460 | + <bool>true</bool> |
461 | + </property> |
462 | + </widget> |
463 | + </item> |
464 | + <item row="10" column="0" colspan="6"> |
465 | + <widget class="QGroupBox" name="advancedSettings"> |
466 | + <property name="sizePolicy"> |
467 | + <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> |
468 | + <horstretch>0</horstretch> |
469 | + <verstretch>0</verstretch> |
470 | + </sizepolicy> |
471 | + </property> |
472 | + <layout class="QVBoxLayout" name="verticalLayout"> |
473 | + <item> |
474 | + <layout class="QGridLayout" name="gridLayout_2"> |
475 | + <property name="spacing"> |
476 | + <number>15</number> |
477 | + </property> |
478 | + <item row="4" column="5"> |
479 | + <widget class="QSlider" name="maxMixChannels"> |
480 | + <property name="minimum"> |
481 | + <number>32</number> |
482 | + </property> |
483 | + <property name="maximum"> |
484 | + <number>256</number> |
485 | + </property> |
486 | + <property name="value"> |
487 | + <number>128</number> |
488 | + </property> |
489 | + <property name="orientation"> |
490 | + <enum>Qt::Horizontal</enum> |
491 | + </property> |
492 | + </widget> |
493 | + </item> |
494 | + <item row="11" column="5"> |
495 | + <widget class="QSlider" name="surroundDelay"> |
496 | + <property name="minimum"> |
497 | + <number>5</number> |
498 | + </property> |
499 | + <property name="maximum"> |
500 | + <number>50</number> |
501 | + </property> |
502 | + <property name="value"> |
503 | + <number>10</number> |
504 | + </property> |
505 | + <property name="orientation"> |
506 | + <enum>Qt::Horizontal</enum> |
507 | + </property> |
508 | + </widget> |
509 | + </item> |
510 | + <item row="7" column="5"> |
511 | + <widget class="QSlider" name="bassCutoff"> |
512 | + <property name="minimum"> |
513 | + <number>10</number> |
514 | + </property> |
515 | + <property name="maximum"> |
516 | + <number>100</number> |
517 | + </property> |
518 | + <property name="value"> |
519 | + <number>80</number> |
520 | + </property> |
521 | + <property name="orientation"> |
522 | + <enum>Qt::Horizontal</enum> |
523 | + </property> |
524 | + </widget> |
525 | + </item> |
526 | + <item row="4" column="1"> |
527 | + <widget class="QSlider" name="stereoSeparation"> |
528 | + <property name="minimum"> |
529 | + <number>1</number> |
530 | + </property> |
531 | + <property name="maximum"> |
532 | + <number>256</number> |
533 | + </property> |
534 | + <property name="value"> |
535 | + <number>1</number> |
536 | + </property> |
537 | + <property name="orientation"> |
538 | + <enum>Qt::Horizontal</enum> |
539 | + </property> |
540 | + <property name="tickPosition"> |
541 | + <enum>QSlider::NoTicks</enum> |
542 | + </property> |
543 | + </widget> |
544 | + </item> |
545 | + <item row="7" column="1"> |
546 | + <widget class="QSlider" name="bassDepth"> |
547 | + <property name="maximum"> |
548 | + <number>100</number> |
549 | + </property> |
550 | + <property name="value"> |
551 | + <number>30</number> |
552 | + </property> |
553 | + <property name="orientation"> |
554 | + <enum>Qt::Horizontal</enum> |
555 | + </property> |
556 | + </widget> |
557 | + </item> |
558 | + <item row="9" column="1"> |
559 | + <widget class="QSlider" name="reverbDepth"> |
560 | + <property name="maximum"> |
561 | + <number>100</number> |
562 | + </property> |
563 | + <property name="value"> |
564 | + <number>30</number> |
565 | + </property> |
566 | + <property name="orientation"> |
567 | + <enum>Qt::Horizontal</enum> |
568 | + </property> |
569 | + </widget> |
570 | + </item> |
571 | + <item row="3" column="4" colspan="3"> |
572 | + <widget class="QLabel" name="label_5"> |
573 | + <property name="text"> |
574 | + <string>Maximum Number of Mixing Channels:</string> |
575 | + </property> |
576 | + </widget> |
577 | + </item> |
578 | + <item row="11" column="1"> |
579 | + <widget class="QSlider" name="surroundDepth"> |
580 | + <property name="maximum"> |
581 | + <number>100</number> |
582 | + </property> |
583 | + <property name="value"> |
584 | + <number>30</number> |
585 | + </property> |
586 | + <property name="orientation"> |
587 | + <enum>Qt::Horizontal</enum> |
588 | + </property> |
589 | + </widget> |
590 | + </item> |
591 | + <item row="7" column="0"> |
592 | + <widget class="QLabel" name="label_2"> |
593 | + <property name="text"> |
594 | + <string>Low</string> |
595 | + </property> |
596 | + </widget> |
597 | + </item> |
598 | + <item row="8" column="4" colspan="3"> |
599 | + <widget class="QLabel" name="label_7"> |
600 | + <property name="text"> |
601 | + <string>Reverb Delay:</string> |
602 | + </property> |
603 | + </widget> |
604 | + </item> |
605 | + <item row="9" column="0"> |
606 | + <widget class="QLabel" name="label_13"> |
607 | + <property name="text"> |
608 | + <string>Low</string> |
609 | + </property> |
610 | + </widget> |
611 | + </item> |
612 | + <item row="7" column="2"> |
613 | + <widget class="QLabel" name="label_6"> |
614 | + <property name="text"> |
615 | + <string>High</string> |
616 | + </property> |
617 | + </widget> |
618 | + </item> |
619 | + <item row="4" column="3"> |
620 | + <spacer name="horizontalSpacer"> |
621 | + <property name="orientation"> |
622 | + <enum>Qt::Horizontal</enum> |
623 | + </property> |
624 | + <property name="sizeType"> |
625 | + <enum>QSizePolicy::Preferred</enum> |
626 | + </property> |
627 | + <property name="sizeHint" stdset="0"> |
628 | + <size> |
629 | + <width>40</width> |
630 | + <height>20</height> |
631 | + </size> |
632 | + </property> |
633 | + </spacer> |
634 | + </item> |
635 | + <item row="4" column="0"> |
636 | + <widget class="QLabel" name="label_8"> |
637 | + <property name="text"> |
638 | + <string>None</string> |
639 | + </property> |
640 | + </widget> |
641 | + </item> |
642 | + <item row="6" column="0" colspan="3"> |
643 | + <widget class="QCheckBox" name="megabass"> |
644 | + <property name="text"> |
645 | + <string>Bass Expansion</string> |
646 | + </property> |
647 | + </widget> |
648 | + </item> |
649 | + <item row="9" column="5"> |
650 | + <widget class="QSlider" name="reverbDelay"> |
651 | + <property name="minimum"> |
652 | + <number>10</number> |
653 | + </property> |
654 | + <property name="maximum"> |
655 | + <number>250</number> |
656 | + </property> |
657 | + <property name="value"> |
658 | + <number>50</number> |
659 | + </property> |
660 | + <property name="orientation"> |
661 | + <enum>Qt::Horizontal</enum> |
662 | + </property> |
663 | + </widget> |
664 | + </item> |
665 | + <item row="6" column="4" colspan="3"> |
666 | + <widget class="QLabel" name="label_3"> |
667 | + <property name="text"> |
668 | + <string>Bass Range:</string> |
669 | + </property> |
670 | + </widget> |
671 | + </item> |
672 | + <item row="4" column="4"> |
673 | + <widget class="QLabel" name="label_18"> |
674 | + <property name="text"> |
675 | + <string>16</string> |
676 | + </property> |
677 | + </widget> |
678 | + </item> |
679 | + <item row="10" column="4" colspan="3"> |
680 | + <widget class="QLabel" name="label_9"> |
681 | + <property name="text"> |
682 | + <string>Front/Rear Delay:</string> |
683 | + </property> |
684 | + </widget> |
685 | + </item> |
686 | + <item row="11" column="2"> |
687 | + <widget class="QLabel" name="label_17"> |
688 | + <property name="text"> |
689 | + <string>High</string> |
690 | + </property> |
691 | + </widget> |
692 | + </item> |
693 | + <item row="10" column="0" colspan="3"> |
694 | + <widget class="QCheckBox" name="surround"> |
695 | + <property name="text"> |
696 | + <string>Pro-Logic Surround</string> |
697 | + </property> |
698 | + </widget> |
699 | + </item> |
700 | + <item row="4" column="2"> |
701 | + <widget class="QLabel" name="label_15"> |
702 | + <property name="text"> |
703 | + <string>Full</string> |
704 | + </property> |
705 | + </widget> |
706 | + </item> |
707 | + <item row="11" column="0"> |
708 | + <widget class="QLabel" name="label_14"> |
709 | + <property name="text"> |
710 | + <string>Low</string> |
711 | + </property> |
712 | + </widget> |
713 | + </item> |
714 | + <item row="8" column="0" colspan="3"> |
715 | + <widget class="QCheckBox" name="reverb"> |
716 | + <property name="text"> |
717 | + <string>Reverb</string> |
718 | + </property> |
719 | + </widget> |
720 | + </item> |
721 | + <item row="3" column="0" colspan="3"> |
722 | + <widget class="QLabel" name="label_4"> |
723 | + <property name="text"> |
724 | + <string>Stereo separation</string> |
725 | + </property> |
726 | + </widget> |
727 | + </item> |
728 | + <item row="9" column="2"> |
729 | + <widget class="QLabel" name="label_16"> |
730 | + <property name="text"> |
731 | + <string>High</string> |
732 | + </property> |
733 | + </widget> |
734 | + </item> |
735 | + <item row="7" column="4"> |
736 | + <widget class="QLabel" name="label_19"> |
737 | + <property name="text"> |
738 | + <string>10Hz</string> |
739 | + </property> |
740 | + </widget> |
741 | + </item> |
742 | + <item row="9" column="4"> |
743 | + <widget class="QLabel" name="label_20"> |
744 | + <property name="text"> |
745 | + <string>10ms</string> |
746 | + </property> |
747 | + </widget> |
748 | + </item> |
749 | + <item row="4" column="6"> |
750 | + <widget class="QLabel" name="label_22"> |
751 | + <property name="text"> |
752 | + <string>256</string> |
753 | + </property> |
754 | + </widget> |
755 | + </item> |
756 | + <item row="11" column="4"> |
757 | + <widget class="QLabel" name="label_21"> |
758 | + <property name="text"> |
759 | + <string>5ms</string> |
760 | + </property> |
761 | + </widget> |
762 | + </item> |
763 | + <item row="7" column="6"> |
764 | + <widget class="QLabel" name="label_23"> |
765 | + <property name="text"> |
766 | + <string>100Hz</string> |
767 | + </property> |
768 | + </widget> |
769 | + </item> |
770 | + <item row="9" column="6"> |
771 | + <widget class="QLabel" name="label_24"> |
772 | + <property name="text"> |
773 | + <string>250ms</string> |
774 | + </property> |
775 | + </widget> |
776 | + </item> |
777 | + <item row="11" column="6"> |
778 | + <widget class="QLabel" name="label_25"> |
779 | + <property name="text"> |
780 | + <string>50ms</string> |
781 | + </property> |
782 | + </widget> |
783 | + </item> |
784 | + <item row="5" column="0" colspan="3"> |
785 | + <widget class="QCheckBox" name="noiseReduction"> |
786 | + <property name="text"> |
787 | + <string>Noise reduction</string> |
788 | + </property> |
789 | + </widget> |
790 | + </item> |
791 | + </layout> |
792 | + </item> |
793 | + </layout> |
794 | + </widget> |
795 | + </item> |
796 | + <item row="3" column="2"> |
797 | + <widget class="QLabel" name="label"> |
798 | + <property name="text"> |
799 | + <string>Resampling mode (interpolation)</string> |
800 | + </property> |
801 | + </widget> |
802 | + </item> |
803 | + <item row="3" column="0"> |
804 | + <widget class="QCheckBox" name="oversampling"> |
805 | + <property name="text"> |
806 | + <string>Enable oversampling</string> |
807 | + </property> |
808 | + <property name="checked"> |
809 | + <bool>true</bool> |
810 | + </property> |
811 | + </widget> |
812 | + </item> |
813 | + <item row="3" column="3" colspan="3"> |
814 | + <widget class="QComboBox" name="resampleMode"> |
815 | + <property name="currentIndex"> |
816 | + <number>1</number> |
817 | + </property> |
818 | + <item> |
819 | + <property name="text"> |
820 | + <string>Nearest (very fast, extremely bad quality)</string> |
821 | + </property> |
822 | + </item> |
823 | + <item> |
824 | + <property name="text"> |
825 | + <string>Linear (fast, good quality)</string> |
826 | + </property> |
827 | + </item> |
828 | + <item> |
829 | + <property name="text"> |
830 | + <string>Cubic Spline (high quality)</string> |
831 | + </property> |
832 | + </item> |
833 | + <item> |
834 | + <property name="text"> |
835 | + <string>8-tap FIR (extremely high quality)</string> |
836 | + </property> |
837 | + </item> |
838 | + </widget> |
839 | + </item> |
840 | + <item row="2" column="0" colspan="2"> |
841 | + <widget class="QLabel" name="label_10"> |
842 | + <property name="text"> |
843 | + <string>Memory limit for single track (MB)</string> |
844 | + </property> |
845 | + </widget> |
846 | + </item> |
847 | + <item row="2" column="2" colspan="3"> |
848 | + <widget class="QSlider" name="memoryLimit"> |
849 | + <property name="toolTip"> |
850 | + <string extracomment="1 minute of decoded audio requires about 10MB of RAM."/> |
851 | + </property> |
852 | + <property name="minimum"> |
853 | + <number>16</number> |
854 | + </property> |
855 | + <property name="maximum"> |
856 | + <number>512</number> |
857 | + </property> |
858 | + <property name="value"> |
859 | + <number>256</number> |
860 | + </property> |
861 | + <property name="sliderPosition"> |
862 | + <number>256</number> |
863 | + </property> |
864 | + <property name="orientation"> |
865 | + <enum>Qt::Horizontal</enum> |
866 | + </property> |
867 | + </widget> |
868 | + </item> |
869 | + </layout> |
870 | + </widget> |
871 | + <resources/> |
872 | + <connections> |
873 | + <connection> |
874 | + <sender>memoryLimit</sender> |
875 | + <signal>valueChanged(int)</signal> |
876 | + <receiver>memoryLimitSpin</receiver> |
877 | + <slot>setValue(int)</slot> |
878 | + <hints> |
879 | + <hint type="sourcelabel"> |
880 | + <x>405</x> |
881 | + <y>58</y> |
882 | + </hint> |
883 | + <hint type="destinationlabel"> |
884 | + <x>556</x> |
885 | + <y>58</y> |
886 | + </hint> |
887 | + </hints> |
888 | + </connection> |
889 | + <connection> |
890 | + <sender>memoryLimitSpin</sender> |
891 | + <signal>valueChanged(int)</signal> |
892 | + <receiver>memoryLimit</receiver> |
893 | + <slot>setValue(int)</slot> |
894 | + <hints> |
895 | + <hint type="sourcelabel"> |
896 | + <x>556</x> |
897 | + <y>58</y> |
898 | + </hint> |
899 | + <hint type="destinationlabel"> |
900 | + <x>405</x> |
901 | + <y>58</y> |
902 | + </hint> |
903 | + </hints> |
904 | + </connection> |
905 | + <connection> |
906 | + <sender>showAdvanced</sender> |
907 | + <signal>toggled(bool)</signal> |
908 | + <receiver>advancedSettings</receiver> |
909 | + <slot>setVisible(bool)</slot> |
910 | + <hints> |
911 | + <hint type="sourcelabel"> |
912 | + <x>97</x> |
913 | + <y>154</y> |
914 | + </hint> |
915 | + <hint type="destinationlabel"> |
916 | + <x>395</x> |
917 | + <y>323</y> |
918 | + </hint> |
919 | + </hints> |
920 | + </connection> |
921 | + </connections> |
922 | +</ui> |
923 | |
924 | === modified file 'mixxx/src/mixxx.cpp' |
925 | --- mixxx/src/mixxx.cpp 2013-02-10 20:59:19 +0000 |
926 | +++ mixxx/src/mixxx.cpp 2013-03-19 15:51:49 +0000 |
927 | @@ -60,6 +60,10 @@ |
928 | #include "vinylcontrol/vinylcontrolmanager.h" |
929 | #endif |
930 | |
931 | +#ifdef __MODPLUG__ |
932 | +#include "dlgprefmodplug.h" |
933 | +#endif |
934 | + |
935 | extern "C" void crashDlg() |
936 | { |
937 | QMessageBox::critical(0, "Mixxx", |
938 | @@ -340,6 +344,14 @@ |
939 | m_pVCManager->init(); |
940 | #endif |
941 | |
942 | +#ifdef __MODPLUG__ |
943 | + // restore the configuration for the modplug library before trying to load a module |
944 | + DlgPrefModplug* pModplugPrefs = new DlgPrefModplug(0, m_pConfig); |
945 | + pModplugPrefs->loadSettings(); |
946 | + pModplugPrefs->applySettings(); |
947 | + delete pModplugPrefs; // not needed anymore |
948 | +#endif |
949 | + |
950 | m_pLibrary = new Library(this, m_pConfig, |
951 | bFirstRun || bUpgraded, |
952 | m_pRecordingManager); |
953 | |
954 | === added file 'mixxx/src/soundsourcemodplug.cpp' |
955 | --- mixxx/src/soundsourcemodplug.cpp 1970-01-01 00:00:00 +0000 |
956 | +++ mixxx/src/soundsourcemodplug.cpp 2013-03-19 15:51:49 +0000 |
957 | @@ -0,0 +1,198 @@ |
958 | + |
959 | +#include <stdlib.h> |
960 | +#include <unistd.h> |
961 | + |
962 | +#include <QFile> |
963 | +#include <QtDebug> |
964 | + |
965 | +#include "util/timer.h" |
966 | +#include "soundsourcemodplug.h" |
967 | + |
968 | +/* read files in 512k chunks */ |
969 | +#define CHUNKSIZE (1 << 19) |
970 | + |
971 | +// reserve some static space for settings... |
972 | +ModPlug::ModPlug_Settings SoundSourceModPlug::s_settings; |
973 | +int SoundSourceModPlug::s_bufferSizeLimit; |
974 | + |
975 | +SoundSourceModPlug::SoundSourceModPlug(QString qFilename) : |
976 | + SoundSource(qFilename) |
977 | +{ |
978 | + m_opened = false; |
979 | + m_fileLength = 0; |
980 | + m_pModFile = 0; |
981 | + |
982 | + qDebug() << "[ModPlug] Loading ModPlug module " << m_qFilename; |
983 | + |
984 | + // read module file to byte array |
985 | + QFile modFile(m_qFilename); |
986 | + modFile.open(QIODevice::ReadOnly); |
987 | + m_fileBuf = modFile.readAll(); |
988 | + modFile.close(); |
989 | + // get ModPlugFile descriptor for later access |
990 | + m_pModFile = ModPlug::ModPlug_Load(m_fileBuf.data(), m_fileBuf.length()); |
991 | +} |
992 | + |
993 | +SoundSourceModPlug::~SoundSourceModPlug() |
994 | +{ |
995 | + if (m_pModFile) { |
996 | + ModPlug::ModPlug_Unload(m_pModFile); |
997 | + m_pModFile = NULL; |
998 | + } |
999 | +} |
1000 | + |
1001 | +QList<QString> SoundSourceModPlug::supportedFileExtensions() |
1002 | +{ |
1003 | + QList<QString> list; |
1004 | + // ModPlug supports more formats but file name |
1005 | + // extensions are not always present with modules. |
1006 | + list.push_back("mod"); |
1007 | + list.push_back("med"); |
1008 | + list.push_back("okt"); |
1009 | + list.push_back("s3m"); |
1010 | + list.push_back("stm"); |
1011 | + list.push_back("xm"); |
1012 | + list.push_back("it"); |
1013 | + return list; |
1014 | +} |
1015 | + |
1016 | +void SoundSourceModPlug::configure(unsigned int bufferSizeLimit, |
1017 | + const ModPlug::ModPlug_Settings &settings) |
1018 | +{ |
1019 | + s_bufferSizeLimit = bufferSizeLimit; |
1020 | + s_settings = settings; |
1021 | + |
1022 | + ModPlug::ModPlug_SetSettings(&s_settings); |
1023 | +} |
1024 | + |
1025 | +int SoundSourceModPlug::open() { |
1026 | + ScopedTimer t("SoundSourceModPlug::open()"); |
1027 | + |
1028 | + if (m_pModFile == NULL) { |
1029 | + // an error occured |
1030 | + t.cancel(); |
1031 | + qDebug() << "[ModPlug] Could not load module file: " |
1032 | + << m_qFilename; |
1033 | + return ERR; |
1034 | + } |
1035 | + |
1036 | + // estimate size of sample buffer (for better performance) |
1037 | + // beware: module length estimation is unreliable due to loops |
1038 | + // song milliseconds * 2 (bytes per sample) |
1039 | + // * 2 (channels) |
1040 | + // * 44.1 (samples per millisecond) |
1041 | + // + some more to accomodate short loops etc. |
1042 | + // approximate and align with CHUNKSIZE yields: |
1043 | + // (((milliseconds << 2) >> 10 /* to seconds */) |
1044 | + // div 11 /* samples to chunksize ratio */) |
1045 | + // << 19 /* align to chunksize */ |
1046 | + int estimate = ((ModPlug::ModPlug_GetLength(m_pModFile) >> 8) / 11) << 19; |
1047 | + estimate = math_min(estimate, s_bufferSizeLimit); |
1048 | + m_sampleBuf.reserve(estimate); |
1049 | + qDebug() << "[ModPlug] Reserved " << m_sampleBuf.capacity() |
1050 | + << " bytes for samples"; |
1051 | + |
1052 | + // decode samples to sample buffer |
1053 | + int bytesRead = -1; |
1054 | + int currentSize = 0; |
1055 | + while((bytesRead != 0) && (m_sampleBuf.length() < s_bufferSizeLimit)) { |
1056 | + // reserve enough space in sample buffer |
1057 | + m_sampleBuf.resize(currentSize + CHUNKSIZE); |
1058 | + bytesRead = ModPlug::ModPlug_Read(m_pModFile, |
1059 | + m_sampleBuf.data() + currentSize, |
1060 | + CHUNKSIZE); |
1061 | + // adapt to actual size |
1062 | + currentSize += bytesRead; |
1063 | + if (bytesRead != CHUNKSIZE) { |
1064 | + m_sampleBuf.resize(currentSize); |
1065 | + bytesRead = 0; // we reached the end of the file |
1066 | + } |
1067 | + } |
1068 | + qDebug() << "[ModPlug] Filled Sample buffer with " << m_sampleBuf.length() |
1069 | + << " bytes."; |
1070 | + qDebug() << "[ModPlug] Sample buffer has " |
1071 | + << m_sampleBuf.capacity() - m_sampleBuf.length() |
1072 | + << " bytes unused capacity."; |
1073 | + |
1074 | + // The sample buffer holds 44.1kHz 16bit integer stereo samples. |
1075 | + // We count the number of samples by dividing number of |
1076 | + // bytes in m_sampleBuf by 2 (bytes per sample). |
1077 | + m_fileLength = m_sampleBuf.length() >> 1; |
1078 | + m_iSampleRate = 44100; // ModPlug always uses 44.1kHz |
1079 | + m_opened = true; |
1080 | + m_seekPos = 0; |
1081 | + return OK; |
1082 | +} |
1083 | + |
1084 | +long SoundSourceModPlug::seek(long filePos) |
1085 | +{ |
1086 | + if (m_fileLength > 0) { |
1087 | + m_seekPos = math_min((unsigned long)filePos, m_fileLength); |
1088 | + return m_seekPos; |
1089 | + } |
1090 | + return 0; |
1091 | +} |
1092 | + |
1093 | +unsigned SoundSourceModPlug::read(unsigned long size, |
1094 | + const SAMPLE* pDestination) |
1095 | +{ |
1096 | + unsigned maxLength = m_sampleBuf.length() >> 1; |
1097 | + unsigned copySamples = math_min(maxLength - m_seekPos, size); |
1098 | + |
1099 | + memcpy((unsigned char*) pDestination, |
1100 | + m_sampleBuf.constData() + (m_seekPos << 1), copySamples << 1); |
1101 | + |
1102 | + m_seekPos += copySamples; |
1103 | + return copySamples; |
1104 | +} |
1105 | + |
1106 | +int SoundSourceModPlug::parseHeader() |
1107 | +{ |
1108 | + if (m_pModFile == NULL) { |
1109 | + // an error occured |
1110 | + qDebug() << "Could not parse module header of " << m_qFilename; |
1111 | + return ERR; |
1112 | + } |
1113 | + |
1114 | + switch (ModPlug::ModPlug_GetModuleType(m_pModFile)) { |
1115 | + case NONE: |
1116 | + setType(QString("None")); |
1117 | + break; |
1118 | + case MOD: |
1119 | + setType(QString("Protracker")); |
1120 | + break; |
1121 | + case S3M: |
1122 | + setType(QString("Scream Tracker 3")); |
1123 | + break; |
1124 | + case XM: |
1125 | + setType(QString("FastTracker2")); |
1126 | + break; |
1127 | + case MED: |
1128 | + setType(QString("OctaMed")); |
1129 | + break; |
1130 | + case IT: |
1131 | + setType(QString("Impulse Tracker")); |
1132 | + break; |
1133 | + case STM: |
1134 | + setType(QString("Scream Tracker")); |
1135 | + break; |
1136 | + case OKT: |
1137 | + setType(QString("Oktalyzer")); |
1138 | + break; |
1139 | + default: |
1140 | + setType(QString("Module")); |
1141 | + break; |
1142 | + } |
1143 | + setComment(QString(ModPlug::ModPlug_GetMessage(m_pModFile))); |
1144 | + setTitle(QString(ModPlug::ModPlug_GetName(m_pModFile))); |
1145 | + setDuration(ModPlug::ModPlug_GetLength(m_pModFile) / 1000); |
1146 | + setBitrate(8); // not really, but fill in something... |
1147 | + setSampleRate(44100); |
1148 | + setChannels(2); |
1149 | + return OK; |
1150 | +} |
1151 | + |
1152 | +inline long unsigned SoundSourceModPlug::length() |
1153 | +{ |
1154 | + return m_fileLength; |
1155 | +} |
1156 | |
1157 | === added file 'mixxx/src/soundsourcemodplug.h' |
1158 | --- mixxx/src/soundsourcemodplug.h 1970-01-01 00:00:00 +0000 |
1159 | +++ mixxx/src/soundsourcemodplug.h 2013-03-19 15:51:49 +0000 |
1160 | @@ -0,0 +1,60 @@ |
1161 | +// soundsourcemodplug.h - modplug tracker support |
1162 | +// created 2012 by Stefan Nuernberger <kabelfrickler@gmail.com> |
1163 | + |
1164 | +#ifndef SOUNDSOURCEMODPLUG_H |
1165 | +#define SOUNDSOURCEMODPLUG_H |
1166 | + |
1167 | +#include <QByteArray> |
1168 | +#include <QList> |
1169 | +#include <QString> |
1170 | + |
1171 | +#include "soundsource.h" |
1172 | + |
1173 | +namespace ModPlug { |
1174 | +#include <libmodplug/modplug.h> |
1175 | +} |
1176 | + |
1177 | +// Class for reading tracker files using libmodplug. |
1178 | +// The whole file is decoded at once and saved |
1179 | +// in RAM to allow seeking and smooth operation in Mixxx. |
1180 | +class SoundSourceModPlug : public Mixxx::SoundSource |
1181 | +{ |
1182 | + public: |
1183 | + SoundSourceModPlug(QString qFilename); |
1184 | + ~SoundSourceModPlug(); |
1185 | + int open(); |
1186 | + long seek(long); |
1187 | + unsigned read(unsigned long size, const SAMPLE*); |
1188 | + inline long unsigned length(); |
1189 | + int parseHeader(); |
1190 | + static QList<QString> supportedFileExtensions(); |
1191 | + |
1192 | + // apply settings for decoding |
1193 | + static void configure(unsigned int bufferSizeLimit, |
1194 | + const ModPlug::ModPlug_Settings &settings); |
1195 | + |
1196 | + private: |
1197 | + static int s_bufferSizeLimit; // max track buffer length (bytes) |
1198 | + static ModPlug::ModPlug_Settings s_settings; // modplug decoder parameters |
1199 | + |
1200 | + bool m_opened; |
1201 | + unsigned long m_fileLength; // length of file in samples |
1202 | + unsigned long m_seekPos; // current read position |
1203 | + ModPlug::ModPlugFile *m_pModFile; // modplug file descriptor |
1204 | + QByteArray m_fileBuf; // original module file data |
1205 | + QByteArray m_sampleBuf; // 16bit stereo samples, 44.1kHz |
1206 | + |
1207 | + // identification of modplug module type |
1208 | + enum ModuleTypes { |
1209 | + NONE = 0x00, |
1210 | + MOD = 0x01, |
1211 | + S3M = 0x02, |
1212 | + XM = 0x04, |
1213 | + MED = 0x08, |
1214 | + IT = 0x20, |
1215 | + STM = 0x100, |
1216 | + OKT = 0x8000 |
1217 | + }; |
1218 | +}; |
1219 | + |
1220 | +#endif |
1221 | |
1222 | === modified file 'mixxx/src/soundsourceproxy.cpp' |
1223 | --- mixxx/src/soundsourceproxy.cpp 2012-11-20 00:40:18 +0000 |
1224 | +++ mixxx/src/soundsourceproxy.cpp 2013-03-19 15:51:49 +0000 |
1225 | @@ -31,6 +31,9 @@ |
1226 | #ifdef __FFMPEGFILE__ |
1227 | #include "soundsourceffmpeg.h" |
1228 | #endif |
1229 | +#ifdef __MODPLUG__ |
1230 | +#include "soundsourcemodplug.h" |
1231 | +#endif |
1232 | #include "soundsourceflac.h" |
1233 | |
1234 | #include "mixxx.h" |
1235 | @@ -139,6 +142,10 @@ |
1236 | } else if (SoundSourceCoreAudio::supportedFileExtensions().contains(extension)) { |
1237 | return new SoundSourceCoreAudio(qFilename); |
1238 | #endif |
1239 | +#ifdef __MODPLUG__ |
1240 | + } else if (SoundSourceModPlug::supportedFileExtensions().contains(extension)) { |
1241 | + return new SoundSourceModPlug(qFilename); |
1242 | +#endif |
1243 | } else if (m_extensionsSupportedByPlugins.contains(extension)) { |
1244 | getSoundSourceFunc getter = m_extensionsSupportedByPlugins.value(extension); |
1245 | if (getter) |
1246 | @@ -368,6 +375,9 @@ |
1247 | #ifdef __COREAUDIO__ |
1248 | supportedFileExtensions.append(SoundSourceCoreAudio::supportedFileExtensions()); |
1249 | #endif |
1250 | +#ifdef __MODPLUG__ |
1251 | + supportedFileExtensions.append(SoundSourceModPlug::supportedFileExtensions()); |
1252 | +#endif |
1253 | supportedFileExtensions.append(m_extensionsSupportedByPlugins.keys()); |
1254 | |
1255 | return supportedFileExtensions; |
Hmm, ok the branch is not completely clean. I will try to remove every change that is not directly concerned with modplug (whitespace fixes etc.) and the remaining reference to "plugin" in the SConstruct file. Then I will merge with latest trunk to resolve the conflict and resubmit the merge request.