Merge lp:~mixxxdevelopers/mixxx/features_hydra into lp:~mixxxdevelopers/mixxx/trunk

Proposed by RJ Skerry-Ryan
Status: Merged
Merged at revision: 2721
Proposed branch: lp:~mixxxdevelopers/mixxx/features_hydra
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 923 lines (+456/-204)
9 files modified
mixxx/build/depends.py (+0/-1)
mixxx/src/engine/enginechannel.cpp (+0/-11)
mixxx/src/engine/enginechannel.h (+0/-3)
mixxx/src/engine/enginemaster.cpp (+210/-98)
mixxx/src/engine/enginemaster.h (+57/-9)
mixxx/src/engine/enginevolume.cpp (+0/-42)
mixxx/src/engine/enginevolume.h (+0/-36)
mixxx/src/sampleutil.cpp (+149/-0)
mixxx/src/sampleutil.h (+40/-4)
To merge this branch: bzr merge lp:~mixxxdevelopers/mixxx/features_hydra
Reviewer Review Type Date Requested Status
Albert Santoni Approve
Review via email: mp+55693@code.launchpad.net

Description of the change

Threw together some quick engine changes, but I'd like a review on them because they're substantial:

* Delete EngineVolume -- it was a trivial wrapper around a ControlLogpotmeter

* Make per-channel volume a gain factor when mixing the master output. This eliminates some hackyness w/ EngineChannel::isPFL that has been bugging me and saves 1 pass over every channel buffer.

* Eliminate use of QList in EngineMaster::process(). I noticed from gprof and some of Jus's traces that time spent in the QList in EngineMaster::process was higher than I had expected. Furthermore, allocating a QList on the stack malloc's every callback. It's not desirable if we can get around it. I removed that in favor of a bitvector approach, and I made way for some hard-coding of the mixing of higher #s of decks, because Mixxx will by default come with 6 or 7 EngineChannels turned on in 1.10.0, this is going to be more important than before.

To post a comment you must log in.
Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

BTW, the EngineMaster tests still pass, so I'm pretty confident this is fine for our uses. Something should be done about the 16-deck limit. With an int64, we could support 64-decks. We could also use two to get 128, but those aren't realistic cases for Mixxx right now so I didn't bother.

2721. By RJ Skerry-Ryan

More improvements to EngineMaster. Headphone mixing now goes through the fast-path of mixing channels instead of being added one by one (this has the added benefit of unifying the mixing code used for both the master and headphone channels). Added special-casing for the mixing of up to 7 channels. Folded the master volume into the master mixing step so we save a pass over the buffer there.

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

Many more improvements now. I think this tightens up the EngineMaster code quite a bit. Headphone and master mixing are both done through the same codepath now. I also shaved another pass off by folding the master volume into the master mixing step. Headphone mixing is now faster because it uses the hard-coded mixing steps instead of the N-pass adding.

2722. By RJ Skerry-Ryan

Forgot a return value, and for some braindead reason GCC compiled it.

Revision history for this message
Albert Santoni (gamegod) wrote :

I checked the coefficients and looked over the code, didn't see anything that looks out of place. Though we haven't measured the performance gains, this is definitely a step in the right direction.

Thanks RJ!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mixxx/build/depends.py'
2--- mixxx/build/depends.py 2011-03-29 08:45:42 +0000
3+++ mixxx/build/depends.py 2011-04-01 04:36:32 +0000
4@@ -358,7 +358,6 @@
5 "engine/enginefilter.cpp",
6 "engine/engineobject.cpp",
7 "engine/enginepregain.cpp",
8- "engine/enginevolume.cpp",
9 "engine/enginechannel.cpp",
10 "engine/enginemaster.cpp",
11 "engine/enginedelay.cpp",
12
13=== modified file 'mixxx/src/engine/enginechannel.cpp'
14--- mixxx/src/engine/enginechannel.cpp 2011-03-13 00:21:05 +0000
15+++ mixxx/src/engine/enginechannel.cpp 2011-04-01 04:36:32 +0000
16@@ -22,7 +22,6 @@
17 #include "enginechannel.h"
18 #include "engineclipping.h"
19 #include "enginepregain.h"
20-#include "enginevolume.h"
21 #include "engineflanger.h"
22 #include "enginefilterblock.h"
23 #include "enginevumeter.h"
24@@ -39,7 +38,6 @@
25 m_pClipping = new EngineClipping(group);
26 m_pBuffer = new EngineBuffer(group, pConfig);
27 m_pVinylSoundEmu = new EngineVinylSoundEmu(pConfig, group);
28- m_pVolume = new EngineVolume(ConfigKey(group, "volume"));
29 m_pVUMeter = new EngineVuMeter(group);
30 m_pPFL = new ControlPushButton(ConfigKey(group, "pfl"));
31 m_pPFL->setToggleButton(true);
32@@ -54,7 +52,6 @@
33 delete m_pFlanger;
34 delete m_pPregain;
35 delete m_pVinylSoundEmu;
36- delete m_pVolume;
37 delete m_pVUMeter;
38 delete m_pPFL;
39 }
40@@ -82,14 +79,6 @@
41 m_pClipping->process(pOut, pOut, iBufferSize);
42 // Update VU meter
43 m_pVUMeter->process(pOut, pOut, iBufferSize);
44- // Apply channel volume if we aren't PFL
45- if (!isPFL()) {
46- m_pVolume->process(pOut, pOut, iBufferSize);
47- }
48-}
49-
50-void EngineChannel::applyVolume(CSAMPLE *pBuff, const int iBufferSize) const {
51- m_pVolume->process(pBuff, pBuff, iBufferSize);
52 }
53
54 EngineBuffer* EngineChannel::getEngineBuffer() {
55
56=== modified file 'mixxx/src/engine/enginechannel.h'
57--- mixxx/src/engine/enginechannel.h 2011-03-13 00:21:05 +0000
58+++ mixxx/src/engine/enginechannel.h 2011-04-01 04:36:32 +0000
59@@ -27,7 +27,6 @@
60 class EngineBuffer;
61 class EngineFilterBlock;
62 class EngineClipping;
63-class EngineVolume;
64 class EngineFlanger;
65 class EngineVuMeter;
66 class EngineVinylSoundEmu;
67@@ -50,7 +49,6 @@
68 virtual const QString& getGroup();
69
70 virtual void process(const CSAMPLE *pIn, const CSAMPLE *pOut, const int iBufferSize);
71- virtual void applyVolume(CSAMPLE *pBuff, const int iBufferSize) const;
72
73 // TODO(XXX) This hack needs to be removed.
74 virtual EngineBuffer* getEngineBuffer();
75@@ -68,7 +66,6 @@
76 EngineFlanger* m_pFlanger;
77 EnginePregain* m_pPregain;
78 EngineVinylSoundEmu* m_pVinylSoundEmu;
79- EngineVolume* m_pVolume;
80 EngineVuMeter* m_pVUMeter;
81 };
82
83
84=== modified file 'mixxx/src/engine/enginemaster.cpp'
85--- mixxx/src/engine/enginemaster.cpp 2010-12-16 16:29:53 +0000
86+++ mixxx/src/engine/enginemaster.cpp 2011-04-01 04:36:32 +0000
87@@ -21,12 +21,12 @@
88
89 #include "controlpushbutton.h"
90 #include "configobject.h"
91+#include "controllogpotmeter.h"
92 #include "controlpotmeter.h"
93 #include "enginebuffer.h"
94 #include "enginemaster.h"
95 #include "engine/engineworkerscheduler.h"
96 #include "enginebuffer.h"
97-#include "enginevolume.h"
98 #include "enginechannel.h"
99 #include "engineclipping.h"
100 #include "enginevumeter.h"
101@@ -66,7 +66,7 @@
102 m_pBalance = new ControlPotmeter(ConfigKey(group, "balance"), -1., 1.);
103
104 // Master volume
105- volume = new EngineVolume(ConfigKey(group,"volume"), 5.);
106+ m_pMasterVolume = new ControlLogpotmeter(ConfigKey(group, "volume"), 5.);
107
108 // Clipping
109 clipping = new EngineClipping(group);
110@@ -75,7 +75,7 @@
111 vumeter = new EngineVuMeter(group);
112
113 // Headphone volume
114- head_volume = new EngineVolume(ConfigKey(group, "headVolume"), 5.);
115+ m_pHeadVolume = new ControlLogpotmeter(ConfigKey(group, "headVolume"), 5.);
116
117 // Headphone mix (left/right)
118 head_mix = new ControlPotmeter(ConfigKey(group, "headMix"),-1.,1.);
119@@ -105,34 +105,24 @@
120 delete crossfader;
121 delete m_pBalance;
122 delete head_mix;
123- delete volume;
124- delete head_volume;
125+ delete m_pMasterVolume;
126+ delete m_pHeadVolume;
127 delete clipping;
128 delete head_clipping;
129 delete sidechain;
130
131-
132 SampleUtil::free(m_pHead);
133 SampleUtil::free(m_pMaster);
134
135-
136- QMutableListIterator<CSAMPLE*> buffer_it(m_channelBuffers);
137- while (buffer_it.hasNext()) {
138- CSAMPLE* buffer = buffer_it.next();
139- buffer_it.remove();
140- SampleUtil::free(buffer);
141- }
142-
143-
144- QMutableListIterator<EngineChannel*> channel_it(m_channels);
145+ QMutableListIterator<ChannelInfo*> channel_it(m_channels);
146 while (channel_it.hasNext()) {
147- EngineChannel* channel = channel_it.next();
148+ ChannelInfo* pChannelInfo = channel_it.next();
149 channel_it.remove();
150- delete channel;
151+ SampleUtil::free(pChannelInfo->m_pBuffer);
152+ delete pChannelInfo->m_pChannel;
153+ delete pChannelInfo->m_pVolumeControl;
154+ delete pChannelInfo;
155 }
156-
157-
158-
159 }
160
161 const CSAMPLE* EngineMaster::getMasterBuffer() const
162@@ -145,6 +135,167 @@
163 return m_pHead;
164 }
165
166+void EngineMaster::mixChannels(unsigned int channelBitvector, unsigned int maxChannels,
167+ CSAMPLE* pOutput, unsigned int iBufferSize,
168+ GainCalculator* pGainCalculator) {
169+ // Common case: 2 decks, 4 samplers, 1 mic
170+ ChannelInfo* pChannel1 = NULL;
171+ ChannelInfo* pChannel2 = NULL;
172+ ChannelInfo* pChannel3 = NULL;
173+ ChannelInfo* pChannel4 = NULL;
174+ ChannelInfo* pChannel5 = NULL;
175+ ChannelInfo* pChannel6 = NULL;
176+ ChannelInfo* pChannel7 = NULL;
177+
178+ unsigned int totalActive = 0;
179+ for (unsigned int i = 0; i < maxChannels; ++i) {
180+ if ((channelBitvector & (1 << i)) == 0) {
181+ continue;
182+ }
183+
184+ ++totalActive;
185+
186+ if (pChannel1 == NULL) {
187+ pChannel1 = m_channels[i];
188+ } else if (pChannel2 == NULL) {
189+ pChannel2 = m_channels[i];
190+ } else if (pChannel3 == NULL) {
191+ pChannel3 = m_channels[i];
192+ } else if (pChannel4 == NULL) {
193+ pChannel4 = m_channels[i];
194+ } else if (pChannel5 == NULL) {
195+ pChannel5 = m_channels[i];
196+ } else if (pChannel6 == NULL) {
197+ pChannel6 = m_channels[i];
198+ } else if (pChannel7 == NULL) {
199+ pChannel7 = m_channels[i];
200+ }
201+ }
202+
203+ if (totalActive == 0) {
204+ SampleUtil::applyGain(pOutput, 0.0f, iBufferSize);
205+ } else if (totalActive == 1) {
206+ CSAMPLE* pBuffer1 = pChannel1->m_pBuffer;
207+ double gain1 = pGainCalculator->getGain(pChannel1);
208+ SampleUtil::copyWithGain(pOutput,
209+ pBuffer1, gain1,
210+ iBufferSize);
211+ } else if (totalActive == 2) {
212+ CSAMPLE* pBuffer1 = pChannel1->m_pBuffer;
213+ double gain1 = pGainCalculator->getGain(pChannel1);
214+ CSAMPLE* pBuffer2 = pChannel2->m_pBuffer;
215+ double gain2 = pGainCalculator->getGain(pChannel2);
216+ SampleUtil::copy2WithGain(pOutput,
217+ pBuffer1, gain1,
218+ pBuffer2, gain2,
219+ iBufferSize);
220+ } else if (totalActive == 3) {
221+ CSAMPLE* pBuffer1 = pChannel1->m_pBuffer;
222+ double gain1 = pGainCalculator->getGain(pChannel1);
223+ CSAMPLE* pBuffer2 = pChannel2->m_pBuffer;
224+ double gain2 = pGainCalculator->getGain(pChannel2);
225+ CSAMPLE* pBuffer3 = pChannel3->m_pBuffer;
226+ double gain3 = pGainCalculator->getGain(pChannel3);
227+
228+ SampleUtil::copy3WithGain(pOutput,
229+ pBuffer1, gain1,
230+ pBuffer2, gain2,
231+ pBuffer3, gain3,
232+ iBufferSize);
233+ } else if (totalActive == 4) {
234+ CSAMPLE* pBuffer1 = pChannel1->m_pBuffer;
235+ double gain1 = pGainCalculator->getGain(pChannel1);
236+ CSAMPLE* pBuffer2 = pChannel2->m_pBuffer;
237+ double gain2 = pGainCalculator->getGain(pChannel2);
238+ CSAMPLE* pBuffer3 = pChannel3->m_pBuffer;
239+ double gain3 = pGainCalculator->getGain(pChannel3);
240+ CSAMPLE* pBuffer4 = pChannel4->m_pBuffer;
241+ double gain4 = pGainCalculator->getGain(pChannel4);
242+ SampleUtil::copy4WithGain(pOutput,
243+ pBuffer1, gain1,
244+ pBuffer2, gain2,
245+ pBuffer3, gain3,
246+ pBuffer4, gain4,
247+ iBufferSize);
248+ } else if (totalActive == 5) {
249+ CSAMPLE* pBuffer1 = pChannel1->m_pBuffer;
250+ double gain1 = pGainCalculator->getGain(pChannel1);
251+ CSAMPLE* pBuffer2 = pChannel2->m_pBuffer;
252+ double gain2 = pGainCalculator->getGain(pChannel2);
253+ CSAMPLE* pBuffer3 = pChannel3->m_pBuffer;
254+ double gain3 = pGainCalculator->getGain(pChannel3);
255+ CSAMPLE* pBuffer4 = pChannel4->m_pBuffer;
256+ double gain4 = pGainCalculator->getGain(pChannel4);
257+ CSAMPLE* pBuffer5 = pChannel5->m_pBuffer;
258+ double gain5 = pGainCalculator->getGain(pChannel5);
259+
260+ SampleUtil::copy5WithGain(pOutput,
261+ pBuffer1, gain1,
262+ pBuffer2, gain2,
263+ pBuffer3, gain3,
264+ pBuffer4, gain4,
265+ pBuffer5, gain5,
266+ iBufferSize);
267+ } else if (totalActive == 6) {
268+ CSAMPLE* pBuffer1 = pChannel1->m_pBuffer;
269+ double gain1 = pGainCalculator->getGain(pChannel1);
270+ CSAMPLE* pBuffer2 = pChannel2->m_pBuffer;
271+ double gain2 = pGainCalculator->getGain(pChannel2);
272+ CSAMPLE* pBuffer3 = pChannel3->m_pBuffer;
273+ double gain3 = pGainCalculator->getGain(pChannel3);
274+ CSAMPLE* pBuffer4 = pChannel4->m_pBuffer;
275+ double gain4 = pGainCalculator->getGain(pChannel4);
276+ CSAMPLE* pBuffer5 = pChannel5->m_pBuffer;
277+ double gain5 = pGainCalculator->getGain(pChannel5);
278+ CSAMPLE* pBuffer6 = pChannel6->m_pBuffer;
279+ double gain6 = pGainCalculator->getGain(pChannel6);
280+ SampleUtil::copy6WithGain(pOutput,
281+ pBuffer1, gain1,
282+ pBuffer2, gain2,
283+ pBuffer3, gain3,
284+ pBuffer4, gain4,
285+ pBuffer5, gain5,
286+ pBuffer6, gain6,
287+ iBufferSize);
288+ } else if (totalActive == 7) {
289+ CSAMPLE* pBuffer1 = pChannel1->m_pBuffer;
290+ double gain1 = pGainCalculator->getGain(pChannel1);
291+ CSAMPLE* pBuffer2 = pChannel2->m_pBuffer;
292+ double gain2 = pGainCalculator->getGain(pChannel2);
293+ CSAMPLE* pBuffer3 = pChannel3->m_pBuffer;
294+ double gain3 = pGainCalculator->getGain(pChannel3);
295+ CSAMPLE* pBuffer4 = pChannel4->m_pBuffer;
296+ double gain4 = pGainCalculator->getGain(pChannel4);
297+ CSAMPLE* pBuffer5 = pChannel5->m_pBuffer;
298+ double gain5 = pGainCalculator->getGain(pChannel5);
299+ CSAMPLE* pBuffer6 = pChannel6->m_pBuffer;
300+ double gain6 = pGainCalculator->getGain(pChannel6);
301+ CSAMPLE* pBuffer7 = pChannel7->m_pBuffer;
302+ double gain7 = pGainCalculator->getGain(pChannel7);
303+ SampleUtil::copy7WithGain(pOutput,
304+ pBuffer1, gain1,
305+ pBuffer2, gain2,
306+ pBuffer3, gain3,
307+ pBuffer4, gain4,
308+ pBuffer5, gain5,
309+ pBuffer6, gain6,
310+ pBuffer7, gain7,
311+ iBufferSize);
312+ } else {
313+ // Set pOutput to all 0s
314+ SampleUtil::applyGain(pOutput, 0.0f, iBufferSize);
315+
316+ for (unsigned int i = 0; i < maxChannels; ++i) {
317+ if (channelBitvector & (1 << i)) {
318+ ChannelInfo* pChannelInfo = m_channels[i];
319+ CSAMPLE* pBuffer = pChannelInfo->m_pBuffer;
320+ double gain = pGainCalculator->getGain(pChannelInfo);
321+ SampleUtil::addWithGain(pOutput, pBuffer, gain, iBufferSize);
322+ }
323+ }
324+ }
325+}
326+
327 void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize)
328 {
329 CSAMPLE **pOutput = (CSAMPLE**)pOut;
330@@ -152,7 +303,10 @@
331
332 // Prepare each channel for output
333
334- QList<QPair<CSAMPLE*, EngineChannel::ChannelOrientation> > masterChannels;
335+ // Bitvector of enabled channels
336+ const unsigned int maxChannels = 32;
337+ unsigned int masterOutput = 0;
338+ unsigned int headphoneOutput = 0;
339
340 // Compute headphone mix
341 // Head phone left/right mix
342@@ -162,88 +316,43 @@
343 // qDebug() << "head val " << cf_val << ", head " << chead_gain
344 // << ", master " << cmaster_gain;
345
346- // we have to copy PFL channels to the headphone buffer here before we
347- // process the master mix, as PFL channels don't have their fader volume
348- // applied but the master channels do -- bkgood
349- SampleUtil::applyGain(m_pHead, 0.0f, iBufferSize);
350-
351- for (int channel_number = 0; channel_number < m_channels.size(); ++channel_number) {
352- EngineChannel* channel = m_channels[channel_number];
353-
354- if (!channel->isActive()) {
355+ QList<ChannelInfo*>::iterator it = m_channels.begin();
356+ for (unsigned int channel_number = 0;
357+ it != m_channels.end(); ++it, ++channel_number) {
358+ ChannelInfo* pChannelInfo = *it;
359+ EngineChannel* pChannel = pChannelInfo->m_pChannel;
360+
361+ if (!pChannel->isActive()) {
362 continue;
363 }
364
365- CSAMPLE* buffer = m_channelBuffers[channel_number];
366- channel->process(NULL, buffer, iBufferSize);
367+ masterOutput |= (1 << channel_number);
368+
369+ // Process the buffer
370+ pChannel->process(NULL, pChannelInfo->m_pBuffer, iBufferSize);
371
372 // If the channel is enabled for previewing in headphones, copy it
373 // over to the headphone buffer
374- if (channel->isPFL()) {
375- SampleUtil::addWithGain(m_pHead, buffer, chead_gain, iBufferSize);
376- // EngineChannel doesn't apply the volume if it knows it's PFL,
377- // so apply it
378- channel->applyVolume(buffer, iBufferSize);
379+ if (pChannel->isPFL()) {
380+ headphoneOutput |= (1 << channel_number);
381 }
382-
383- // Add the channel to the list of master output channels.
384- masterChannels.push_back(
385- QPair<CSAMPLE*, EngineChannel::ChannelOrientation>(
386- buffer, channel->getOrientation()));
387 }
388
389- // Perform the master mix.
390+ // Mix all the enabled headphone channels together.
391+ m_headphoneGain.setGain(chead_gain);
392+ mixChannels(headphoneOutput, maxChannels, m_pHead, iBufferSize, &m_headphoneGain);
393
394- // Crossfader and Transform buttons
395- //set gain levels;
396+ // Calculate the crossfader gains for left and right side of the crossfader
397 float c1_gain, c2_gain;
398 EngineXfader::getXfadeGains(c1_gain, c2_gain,
399 crossfader->get(), xFaderCurve->get(),
400 xFaderCalibration->get());
401
402- if (masterChannels.size() == 0) {
403- SampleUtil::applyGain(m_pMaster, 0.0f, iBufferSize);
404- } else if (masterChannels.size() == 1) {
405- QPair<CSAMPLE*, EngineChannel::ChannelOrientation>& channel =
406- masterChannels[0];
407- CSAMPLE* buffer = channel.first;
408- EngineChannel::ChannelOrientation orientation = channel.second;
409-
410- // Apply gain
411- double gain = gainForOrientation(orientation, c1_gain, 1.0f, c2_gain);
412- SampleUtil::copyWithGain(m_pMaster, buffer, gain, iBufferSize);
413- } else if (masterChannels.size() == 2) {
414- QPair<CSAMPLE*, EngineChannel::ChannelOrientation> channel1 =
415- masterChannels[0];
416- QPair<CSAMPLE*, EngineChannel::ChannelOrientation> channel2 =
417- masterChannels[1];
418- CSAMPLE* buffer1 = channel1.first;
419- CSAMPLE* buffer2 = channel2.first;
420- EngineChannel::ChannelOrientation orientation1 = channel1.second;
421- EngineChannel::ChannelOrientation orientation2 = channel2.second;
422- double gain1 = gainForOrientation(orientation1, c1_gain, 1.0f, c2_gain);
423- double gain2 = gainForOrientation(orientation2, c1_gain, 1.0f, c2_gain);
424-
425- SampleUtil::copy2WithGain(m_pMaster,
426- buffer1, gain1,
427- buffer2, gain2,
428- iBufferSize);
429- } else {
430- // Set m_pMaster to all 0s
431- SampleUtil::applyGain(m_pMaster, 0.0f, iBufferSize);
432-
433- for (int i = 0; i < masterChannels.size(); ++i) {
434- QPair<CSAMPLE*, EngineChannel::ChannelOrientation> channel =
435- masterChannels[i];
436- CSAMPLE* buffer = channel.first;
437- EngineChannel::ChannelOrientation orientation = channel.second;
438- double gain = gainForOrientation(orientation, c1_gain, 1.0f, c2_gain);
439- SampleUtil::addWithGain(m_pMaster, buffer, gain, iBufferSize);
440- }
441- }
442-
443- // Master volume
444- volume->process(m_pMaster, m_pMaster, iBufferSize);
445+ // Now set the gains for overall volume and the left, center, right gains.
446+ m_masterGain.setGains(m_pMasterVolume->get(), c1_gain, 1.0, c2_gain);
447+
448+ // Perform the master mix
449+ mixChannels(masterOutput, maxChannels, m_pMaster, iBufferSize, &m_masterGain);
450
451 #ifdef __LADSPA__
452 // LADPSA master effects
453@@ -278,7 +387,7 @@
454 SampleUtil::addWithGain(m_pHead, m_pMaster, cmaster_gain, iBufferSize);
455
456 // Head volume and clipping
457- head_volume->process(m_pHead, m_pHead, iBufferSize);
458+ SampleUtil::applyGain(m_pHead, m_pHeadVolume->get(), iBufferSize);
459 head_clipping->process(m_pHead, m_pHead, iBufferSize);
460
461 //Master/headphones interleaving is now done in
462@@ -290,18 +399,21 @@
463 }
464
465 void EngineMaster::addChannel(EngineChannel* pChannel) {
466- CSAMPLE* pChannelBuffer = SampleUtil::alloc(MAX_BUFFER_LEN);
467- memset(pChannelBuffer, 0, sizeof(CSAMPLE) * MAX_BUFFER_LEN);
468- m_channelBuffers.push_back(pChannelBuffer);
469- m_channels.push_back(pChannel);
470- pChannel->getEngineBuffer()->bindWorkers(m_pWorkerScheduler);
471+ ChannelInfo* pChannelInfo = new ChannelInfo();
472+ pChannelInfo->m_pChannel = pChannel;
473+ pChannelInfo->m_pVolumeControl = new ControlLogpotmeter(
474+ ConfigKey(pChannel->getGroup(), pChannel->getGroup()), 1.0);
475+ pChannelInfo->m_pBuffer = SampleUtil::alloc(MAX_BUFFER_LEN);
476+ memset(pChannelInfo->m_pBuffer, 0, sizeof(CSAMPLE) * MAX_BUFFER_LEN);
477+ m_channels.push_back(pChannelInfo);
478+ pChannelInfo->m_pChannel->getEngineBuffer()->bindWorkers(m_pWorkerScheduler);
479
480 // TODO(XXX) WARNING HUGE HACK ALERT In the case of 2-decks, this code hooks
481 // the two EngineBuffers together so they can beat-sync off of each other.
482 // rryan 6/2010
483 if (m_channels.length() == 2) {
484- EngineBuffer *pBuffer1 = m_channels[0]->getEngineBuffer();
485- EngineBuffer *pBuffer2 = m_channels[1]->getEngineBuffer();
486+ EngineBuffer *pBuffer1 = m_channels[0]->m_pChannel->getEngineBuffer();
487+ EngineBuffer *pBuffer2 = m_channels[1]->m_pChannel->getEngineBuffer();
488 pBuffer1->setOtherEngineBuffer(pBuffer2);
489 pBuffer2->setOtherEngineBuffer(pBuffer1);
490 }
491@@ -313,7 +425,7 @@
492
493 const CSAMPLE* EngineMaster::getChannelBuffer(unsigned int i) const {
494 if (i < numChannels()) {
495- return m_channelBuffers[i];
496+ return m_channels[i]->m_pBuffer;
497 }
498 return NULL;
499 }
500
501=== modified file 'mixxx/src/engine/enginemaster.h'
502--- mixxx/src/engine/enginemaster.h 2010-10-27 18:28:54 +0000
503+++ mixxx/src/engine/enginemaster.h 2011-04-01 04:36:32 +0000
504@@ -18,12 +18,14 @@
505 #ifndef ENGINEMASTER_H
506 #define ENGINEMASTER_H
507
508+#include <QMap>
509+
510+#include "controlobject.h"
511 #include "engine/engineobject.h"
512 #include "engine/enginechannel.h"
513
514 class EngineWorkerScheduler;
515 class EngineBuffer;
516-class EngineVolume;
517 class EngineChannel;
518 class EngineClipping;
519 class EngineFlanger;
520@@ -33,7 +35,6 @@
521 class EngineVuMeter;
522 class ControlPotmeter;
523 class ControlPushButton;
524-class ControlObject;
525 class EngineVinylSoundEmu;
526 class EngineSideChain;
527
528@@ -55,20 +56,64 @@
529 // only call it before the engine has started mixing.
530 void addChannel(EngineChannel* pChannel);
531
532- static double gainForOrientation(EngineChannel::ChannelOrientation orientation,
533- double leftGain,
534- double centerGain,
535- double rightGain);
536+ static inline double gainForOrientation(EngineChannel::ChannelOrientation orientation,
537+ double leftGain,
538+ double centerGain,
539+ double rightGain);
540
541 private:
542- QList<EngineChannel*> m_channels;
543+ struct ChannelInfo {
544+ EngineChannel* m_pChannel;
545+ CSAMPLE* m_pBuffer;
546+ ControlObject* m_pVolumeControl;
547+ };
548+
549+ class GainCalculator {
550+ public:
551+ virtual double getGain(ChannelInfo* pChannelInfo) = 0;
552+ };
553+ class ConstantGainCalculator : public GainCalculator {
554+ public:
555+ inline double getGain(ChannelInfo* pChannelInfo) {
556+ return m_dGain;
557+ }
558+ inline double setGain(double dGain) {
559+ m_dGain = dGain;
560+ }
561+ private:
562+ double m_dGain;
563+ };
564+ class OrientationVolumeGainCalculator : public GainCalculator {
565+ public:
566+ inline double getGain(ChannelInfo* pChannelInfo) {
567+ double channelVolume = pChannelInfo->m_pVolumeControl->get();
568+ double orientationGain = EngineMaster::gainForOrientation(
569+ pChannelInfo->m_pChannel->getOrientation(),
570+ m_dLeftGain, m_dCenterGain, m_dRightGain);
571+ return m_dVolume * channelVolume * orientationGain;
572+ }
573+
574+ inline void setGains(double dVolume, double leftGain, double centerGain, double rightGain) {
575+ m_dVolume = dVolume;
576+ m_dLeftGain = leftGain;
577+ m_dCenterGain = centerGain;
578+ m_dRightGain = rightGain;
579+ }
580+ private:
581+ double m_dVolume, m_dLeftGain, m_dCenterGain, m_dRightGain;
582+ };
583+
584+ void mixChannels(unsigned int channelBitvector, unsigned int maxChannels,
585+ CSAMPLE* pOutput, unsigned int iBufferSize, GainCalculator* pGainCalculator);
586+
587+
588+ QList<ChannelInfo*> m_channels;
589
590 CSAMPLE *m_pMaster, *m_pHead;
591- QList<CSAMPLE*> m_channelBuffers;
592
593 EngineWorkerScheduler *m_pWorkerScheduler;
594
595- EngineVolume *volume, *head_volume;
596+ ControlObject *m_pMasterVolume, *m_pHeadVolume;
597 EngineClipping *clipping, *head_clipping;
598 #ifdef __LADSPA__
599 EngineLADSPA *ladspa;
600@@ -78,6 +123,9 @@
601
602 ControlPotmeter *crossfader, *head_mix,
603 *m_pBalance, *xFaderCurve, *xFaderCalibration;
604+
605+ ConstantGainCalculator m_headphoneGain;
606+ OrientationVolumeGainCalculator m_masterGain;
607 };
608
609 #endif
610
611=== removed file 'mixxx/src/engine/enginevolume.cpp'
612--- mixxx/src/engine/enginevolume.cpp 2010-06-02 21:26:29 +0000
613+++ mixxx/src/engine/enginevolume.cpp 1970-01-01 00:00:00 +0000
614@@ -1,42 +0,0 @@
615-/***************************************************************************
616- enginevolume.cpp - description
617- -------------------
618- copyright : (C) 2002 by Tue and Ken Haste Andersen
619- email :
620-***************************************************************************/
621-
622-/***************************************************************************
623-* *
624-* This program is free software; you can redistribute it and/or modify *
625-* it under the terms of the GNU General Public License as published by *
626-* the Free Software Foundation; either version 2 of the License, or *
627-* (at your option) any later version. *
628-* *
629-***************************************************************************/
630-
631-#include "enginevolume.h"
632-#include "controllogpotmeter.h"
633-#include "configobject.h"
634-#include "sampleutil.h"
635-
636-/*----------------------------------------------------------------
637- Volume effect.
638- ----------------------------------------------------------------*/
639-EngineVolume::EngineVolume(ConfigKey key, double maxval)
640-{
641- potmeter = new ControlLogpotmeter(key, maxval);
642-}
643-
644-EngineVolume::~EngineVolume()
645-{
646- delete potmeter;
647-}
648-
649-void EngineVolume::process(const CSAMPLE * pIn, const CSAMPLE * pOut, const int iBufferSize)
650-{
651- CSAMPLE * pOutput = (CSAMPLE *)pOut;
652- double volume = potmeter->get();
653-
654- // SampleUtil handles aliased buffers and gains of 1 or 0.
655- SampleUtil::copyWithGain(pOutput, pIn, volume, iBufferSize);
656-}
657
658=== removed file 'mixxx/src/engine/enginevolume.h'
659--- mixxx/src/engine/enginevolume.h 2009-01-24 04:39:32 +0000
660+++ mixxx/src/engine/enginevolume.h 1970-01-01 00:00:00 +0000
661@@ -1,36 +0,0 @@
662-/***************************************************************************
663- enginevolume.h - description
664- -------------------
665- copyright : (C) 2002 by Tue and Ken Haste Andersen
666- email :
667- ***************************************************************************/
668-
669-/***************************************************************************
670- * *
671- * This program is free software; you can redistribute it and/or modify *
672- * it under the terms of the GNU General Public License as published by *
673- * the Free Software Foundation; either version 2 of the License, or *
674- * (at your option) any later version. *
675- * *
676- ***************************************************************************/
677-
678-#ifndef ENGINEVOLUME_H
679-#define ENGINEVOLUME_H
680-
681-#include "engineobject.h"
682-
683-class ControlLogpotmeter;
684-class ConfigKey;
685-
686-class EngineVolume : public EngineObject {
687-public:
688- EngineVolume(ConfigKey key, double maxval=1.);
689- ~EngineVolume();
690- void process(const CSAMPLE *pIn, const CSAMPLE *pOut, const int iBufferSize);
691-
692-private:
693- CSAMPLE *buffer;
694- ControlLogpotmeter *potmeter;
695-};
696-
697-#endif
698
699=== modified file 'mixxx/src/sampleutil.cpp'
700--- mixxx/src/sampleutil.cpp 2010-06-02 11:16:22 +0000
701+++ mixxx/src/sampleutil.cpp 2011-04-01 04:36:32 +0000
702@@ -489,6 +489,155 @@
703 }
704
705 // static
706+void SampleUtil::copy4WithGain(CSAMPLE* pDest,
707+ const CSAMPLE* pSrc1, CSAMPLE gain1,
708+ const CSAMPLE* pSrc2, CSAMPLE gain2,
709+ const CSAMPLE* pSrc3, CSAMPLE gain3,
710+ const CSAMPLE* pSrc4, CSAMPLE gain4,
711+ int iNumSamples) {
712+ if (gain1 == 0.0f) {
713+ return copy3WithGain(pDest, pSrc2, gain2, pSrc3, gain3, pSrc4, gain4, iNumSamples);
714+ }
715+ if (gain2 == 0.0f) {
716+ return copy3WithGain(pDest, pSrc1, gain1, pSrc3, gain3, pSrc4, gain4, iNumSamples);
717+ }
718+ if (gain3 == 0.0f) {
719+ return copy3WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc4, gain4, iNumSamples);
720+ }
721+ if (gain4 == 0.0f) {
722+ return copy3WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3, iNumSamples);
723+ }
724+ if (m_sOptimizationsOn) {
725+ // TODO(rryan) implement SSE for this? worth it?
726+ }
727+ for (int i = 0; i < iNumSamples; ++i) {
728+ pDest[i] = pSrc1[i] * gain1 + pSrc2[i] * gain2 + pSrc3[i] * gain3 + pSrc4[i] * gain4;
729+ }
730+}
731+
732+// static
733+void SampleUtil::copy5WithGain(CSAMPLE* pDest,
734+ const CSAMPLE* pSrc1, CSAMPLE gain1,
735+ const CSAMPLE* pSrc2, CSAMPLE gain2,
736+ const CSAMPLE* pSrc3, CSAMPLE gain3,
737+ const CSAMPLE* pSrc4, CSAMPLE gain4,
738+ const CSAMPLE* pSrc5, CSAMPLE gain5,
739+ int iNumSamples) {
740+ if (gain1 == 0.0f) {
741+ return copy4WithGain(pDest, pSrc2, gain2, pSrc3, gain3, pSrc4, gain4, pSrc5, gain5, iNumSamples);
742+ }
743+ if (gain2 == 0.0f) {
744+ return copy4WithGain(pDest, pSrc1, gain1, pSrc3, gain3, pSrc4, gain4, pSrc5, gain5, iNumSamples);
745+ }
746+ if (gain3 == 0.0f) {
747+ return copy4WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc4, gain4, pSrc5, gain5, iNumSamples);
748+ }
749+ if (gain4 == 0.0f) {
750+ return copy4WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3, pSrc5, gain5, iNumSamples);
751+ }
752+ if (gain5 == 0.0f) {
753+ return copy4WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3, pSrc4, gain4, iNumSamples);
754+ }
755+
756+ if (m_sOptimizationsOn) {
757+ // TODO(rryan) implement SSE for this? worth it?
758+ }
759+
760+ for (int i = 0; i < iNumSamples; ++i) {
761+ pDest[i] = pSrc1[i] * gain1 + pSrc2[i] * gain2 + pSrc3[i] * gain3 + pSrc4[i] * gain4 + pSrc5[i] * gain5;
762+ }
763+}
764+
765+// static
766+void SampleUtil::copy6WithGain(CSAMPLE* pDest,
767+ const CSAMPLE* pSrc1, CSAMPLE gain1,
768+ const CSAMPLE* pSrc2, CSAMPLE gain2,
769+ const CSAMPLE* pSrc3, CSAMPLE gain3,
770+ const CSAMPLE* pSrc4, CSAMPLE gain4,
771+ const CSAMPLE* pSrc5, CSAMPLE gain5,
772+ const CSAMPLE* pSrc6, CSAMPLE gain6,
773+ int iNumSamples) {
774+ if (gain1 == 0.0f) {
775+ return copy5WithGain(pDest, pSrc2, gain2, pSrc3, gain3, pSrc4, gain4,
776+ pSrc5, gain5, pSrc6, gain6, iNumSamples);
777+ }
778+ if (gain2 == 0.0f) {
779+ return copy5WithGain(pDest, pSrc1, gain1, pSrc3, gain3, pSrc4, gain4,
780+ pSrc5, gain5, pSrc6, gain6, iNumSamples);
781+ }
782+ if (gain3 == 0.0f) {
783+ return copy5WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc4, gain4,
784+ pSrc5, gain5, pSrc6, gain6, iNumSamples);
785+ }
786+ if (gain4 == 0.0f) {
787+ return copy5WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3,
788+ pSrc5, gain5, pSrc6, gain6, iNumSamples);
789+ }
790+ if (gain5 == 0.0f) {
791+ return copy5WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3,
792+ pSrc4, gain4, pSrc6, gain6, iNumSamples);
793+ }
794+ if (gain6 == 0.0f) {
795+ return copy5WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3,
796+ pSrc4, gain4, pSrc5, gain5, iNumSamples);
797+ }
798+ if (m_sOptimizationsOn) {
799+ // TODO(rryan) implement SSE for this? worth it?
800+ }
801+ for (int i = 0; i < iNumSamples; ++i) {
802+ pDest[i] = pSrc1[i] * gain1 + pSrc2[i] * gain2 + pSrc3[i] * gain3 +
803+ pSrc4[i] * gain4 + pSrc5[i] * gain5 + pSrc6[i] * gain6;
804+ }
805+}
806+
807+// static
808+void SampleUtil::copy7WithGain(CSAMPLE* pDest,
809+ const CSAMPLE* pSrc1, CSAMPLE gain1,
810+ const CSAMPLE* pSrc2, CSAMPLE gain2,
811+ const CSAMPLE* pSrc3, CSAMPLE gain3,
812+ const CSAMPLE* pSrc4, CSAMPLE gain4,
813+ const CSAMPLE* pSrc5, CSAMPLE gain5,
814+ const CSAMPLE* pSrc6, CSAMPLE gain6,
815+ const CSAMPLE* pSrc7, CSAMPLE gain7,
816+ int iNumSamples) {
817+ if (gain1 == 0.0f) {
818+ return copy6WithGain(pDest, pSrc2, gain2, pSrc3, gain3, pSrc4, gain4,
819+ pSrc5, gain5, pSrc6, gain6, pSrc7, gain7, iNumSamples);
820+ }
821+ if (gain2 == 0.0f) {
822+ return copy6WithGain(pDest, pSrc1, gain1, pSrc3, gain3, pSrc4, gain4,
823+ pSrc5, gain5, pSrc6, gain6, pSrc7, gain7, iNumSamples);
824+ }
825+ if (gain3 == 0.0f) {
826+ return copy6WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc4, gain4,
827+ pSrc5, gain5, pSrc6, gain6, pSrc7, gain7, iNumSamples);
828+ }
829+ if (gain4 == 0.0f) {
830+ return copy6WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3,
831+ pSrc5, gain5, pSrc6, gain6, pSrc7, gain7, iNumSamples);
832+ }
833+ if (gain5 == 0.0f) {
834+ return copy6WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3,
835+ pSrc4, gain4, pSrc6, gain6, pSrc7, gain7, iNumSamples);
836+ }
837+ if (gain6 == 0.0f) {
838+ return copy6WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3,
839+ pSrc4, gain4, pSrc5, gain5, pSrc7, gain7, iNumSamples);
840+ }
841+ if (gain7 == 0.0f) {
842+ return copy6WithGain(pDest, pSrc1, gain1, pSrc2, gain2, pSrc3, gain3,
843+ pSrc4, gain4, pSrc5, gain5, pSrc6, gain6, iNumSamples);
844+ }
845+ if (m_sOptimizationsOn) {
846+ // TODO(rryan) implement SSE for this? worth it?
847+ }
848+ for (int i = 0; i < iNumSamples; ++i) {
849+ pDest[i] = pSrc1[i] * gain1 + pSrc2[i] * gain2 + pSrc3[i] * gain3 +
850+ pSrc4[i] * gain4 + pSrc5[i] * gain5 + pSrc6[i] * gain6 + pSrc7[i] * gain7;
851+ }
852+}
853+
854+// static
855 void SampleUtil::convert(CSAMPLE* pDest, const SAMPLE* pSrc,
856 int iNumSamples) {
857 if (m_sOptimizationsOn) {
858
859=== modified file 'mixxx/src/sampleutil.h'
860--- mixxx/src/sampleutil.h 2010-06-01 07:23:52 +0000
861+++ mixxx/src/sampleutil.h 2011-04-01 04:36:32 +0000
862@@ -73,21 +73,57 @@
863 static void copyWithGain(CSAMPLE* pDest, const CSAMPLE* pSrc,
864 CSAMPLE gain, int iNumSamples);
865
866- // Copy to pDest, each sample of pSrc1 multiplied by gain1 plus pSrc2
867- // multiplied by gain2
868+ // Copies the sum of each channel, multiplied by its gain into pDest
869 static void copy2WithGain(CSAMPLE* pDest,
870 const CSAMPLE* pSrc1, CSAMPLE gain1,
871 const CSAMPLE* pSrc2, CSAMPLE gain2,
872 int iNumSamples);
873
874- // Copy to pDest, each sample of pSrc1 multiplied by gain1 plus pSrc2
875- // multiplied by gain2 plus pSrc3 multiplied by gain3
876+ // Copies the sum of each channel, multiplied by its gain into pDest
877 static void copy3WithGain(CSAMPLE* pDest,
878 const CSAMPLE* pSrc1, CSAMPLE gain1,
879 const CSAMPLE* pSrc2, CSAMPLE gain2,
880 const CSAMPLE* pSrc3, CSAMPLE gain3,
881 int iNumSamples);
882
883+ // Copies the sum of each channel, multiplied by its gain into pDest
884+ static void copy4WithGain(CSAMPLE* pDest,
885+ const CSAMPLE* pSrc1, CSAMPLE gain1,
886+ const CSAMPLE* pSrc2, CSAMPLE gain2,
887+ const CSAMPLE* pSrc3, CSAMPLE gain3,
888+ const CSAMPLE* pSrc4, CSAMPLE gain4,
889+ int iNumSamples);
890+
891+ // Copies the sum of each channel, multiplied by its gain into pDest
892+ static void copy5WithGain(CSAMPLE* pDest,
893+ const CSAMPLE* pSrc1, CSAMPLE gain1,
894+ const CSAMPLE* pSrc2, CSAMPLE gain2,
895+ const CSAMPLE* pSrc3, CSAMPLE gain3,
896+ const CSAMPLE* pSrc4, CSAMPLE gain4,
897+ const CSAMPLE* pSrc5, CSAMPLE gain5,
898+ int iNumSamples);
899+
900+ // Copies the sum of each channel, multiplied by its gain into pDest
901+ static void copy6WithGain(CSAMPLE* pDest,
902+ const CSAMPLE* pSrc1, CSAMPLE gain1,
903+ const CSAMPLE* pSrc2, CSAMPLE gain2,
904+ const CSAMPLE* pSrc3, CSAMPLE gain3,
905+ const CSAMPLE* pSrc4, CSAMPLE gain4,
906+ const CSAMPLE* pSrc5, CSAMPLE gain5,
907+ const CSAMPLE* pSrc6, CSAMPLE gain6,
908+ int iNumSamples);
909+
910+ // Copies the sum of each channel, multiplied by its gain into pDest
911+ static void copy7WithGain(CSAMPLE* pDest,
912+ const CSAMPLE* pSrc1, CSAMPLE gain1,
913+ const CSAMPLE* pSrc2, CSAMPLE gain2,
914+ const CSAMPLE* pSrc3, CSAMPLE gain3,
915+ const CSAMPLE* pSrc4, CSAMPLE gain4,
916+ const CSAMPLE* pSrc5, CSAMPLE gain5,
917+ const CSAMPLE* pSrc6, CSAMPLE gain6,
918+ const CSAMPLE* pSrc7, CSAMPLE gain7,
919+ int iNumSamples);
920+
921 // Convert a buffer of SAMPLEs to a buffer of CSAMPLEs. Does not work
922 // in-place! pDest and pSrc must not be aliased.
923 static void convert(CSAMPLE* pDest, const SAMPLE* pSrc, int iNumSamples);