Merge lp:~kabelfrickler/mixxx/modplug into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Stefan Nürnberger
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
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.

Description of the change

Add module tracker support through libmodplug

This branch adds a soundsource for files supported by modplug (MOD/MED/IT/STM/S3M/XM/OKT/...)
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

To post a comment you must log in.
Revision history for this message
Stefan Nürnberger (kabelfrickler) wrote : Posted in a previous version of this proposal

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.

review: Needs Fixing
lp:~kabelfrickler/mixxx/modplug updated
3302. By Stefan Nuernberger <email address hidden>

Correct typo in features.py

Revision history for this message
Stefan Nürnberger (kabelfrickler) wrote :

There is a typo in features.py in the description for the modplug switch.
s/libmbodplug/libmodplug

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.

Revision history for this message
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 ;-)

review: Approve
Revision history for this message
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://www.mixxx.org/wiki/doku.php/coding_guidelines.
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::reserve() instead of allocating it step by step by the append() calls.
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

review: Needs Fixing
Revision history for this message
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://wiki.openmpt.org/Manual:_Setup/Player

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://www.mixxx.org/wiki/doku.php/coding_guidelines.
> 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::reserve() instead of allocating it step by step by the
> 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).

Revision history for this message
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://wiki.openmpt.org/Manual:_Setup/Player

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://www.bochon.net/_/mixxx/doc/1.10/index.html

to 5)
If you want to measure your code performance that can be easyly be done by adding
    ScopedTimer t("SoundSourceModPlug::open()");

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.

lp:~kabelfrickler/mixxx/modplug updated
3303. By Stefan Nuernberger <email address hidden>

Refactoring SoundSourceModPlug. Obey coding styleguide. Estimate sample buffer size. Get rid of temp buffer in open().

Revision history for this message
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("SoundSourceModPlug::open()","count=24,sum=9.99435e+09ns,average=4.16431e+08ns,min=2.94204e+07ns,max=1.25165e+09ns,variance=1.62411e+17ns^2,stddev=4.03002e+08ns")

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...

Revision history for this message
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://bazaar.launchpad.net/~kabelfrickler/mixxx/modplug/view/head:/mixxx/src/cachingreader.cpp#L280
http://bazaar.launchpad.net/~kabelfrickler/mixxx/modplug/view/head:/mixxx/src/analyserqueue.cpp#L187

Just filed a Bug #1156569 ;-)

lp:~kabelfrickler/mixxx/modplug updated
3304. By Stefan Nuernberger <email address hidden>

Use memcpy for SoundSourceModPlug::read(). Add ScopedTimer for SoundSourceModPlug::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.

Revision history for this message
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.

Revision history for this message
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://docs.google.com/a/mixxx.org/spreadsheet/viewform?formkey=dEpYN2NkVEFnWWQzbkFfM0ZYYUZ5X2c6MQ

Let us know when you have.

review: Needs Information
Revision history for this message
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.)

Revision history for this message
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.

Revision history for this message
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?

review: Approve
Revision history for this message
Stefan Nürnberger (kabelfrickler) wrote :

Yes, that's my name :)
Thank you.

Revision history for this message
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.)

review: Abstain
Revision history for this message
Daniel Schürmann (daschuer) wrote :

Merged to lp:mixxx revision 3348.
Thank you Stefan!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 &lt;a href=&quot;http://wiki.openmpt.org/Manual:_Setup/Player&quot;&gt;OpenMPT manual&lt;/a&gt;.</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;