Merge lp:~mixxxdevelopers/mixxx/atomic-co into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Daniel Schürmann
Status: Merged
Merged at revision: 3373
Proposed branch: lp:~mixxxdevelopers/mixxx/atomic-co
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 5303 lines (+2417/-1518)
63 files modified
mixxx/build/depends.py (+2/-3)
mixxx/res/controllers/Hercules DJ Console RMX 2.midi.xml (+963/-0)
mixxx/res/controllers/Hercules-DJ-Console-RMX-2-scripts.js (+128/-0)
mixxx/src/control/control.cpp (+132/-0)
mixxx/src/control/control.h (+95/-0)
mixxx/src/control/controlbehavior.cpp (+194/-0)
mixxx/src/control/controlbehavior.h (+97/-0)
mixxx/src/control/controlvalue.h (+157/-0)
mixxx/src/controlbeat.cpp (+0/-85)
mixxx/src/controlbeat.h (+0/-59)
mixxx/src/controllers/controller.h (+0/-2)
mixxx/src/controllers/controllerengine.cpp (+6/-11)
mixxx/src/controllers/controllermanager.cpp (+0/-4)
mixxx/src/controllers/controllermanager.h (+0/-1)
mixxx/src/controllers/midi/midicontroller.cpp (+5/-16)
mixxx/src/controllers/midi/midioutputhandler.cpp (+11/-20)
mixxx/src/controllers/midi/midioutputhandler.h (+2/-2)
mixxx/src/controllers/softtakeover.cpp (+1/-1)
mixxx/src/controllinpotmeter.cpp (+5/-24)
mixxx/src/controllinpotmeter.h (+1/-10)
mixxx/src/controllogpotmeter.cpp (+11/-87)
mixxx/src/controllogpotmeter.h (+3/-21)
mixxx/src/controlnull.cpp (+0/-27)
mixxx/src/controlnull.h (+0/-34)
mixxx/src/controlobject.cpp (+73/-317)
mixxx/src/controlobject.h (+49/-117)
mixxx/src/controlobjectthread.cpp (+40/-69)
mixxx/src/controlobjectthread.h (+33/-35)
mixxx/src/controlobjectthreadmain.cpp (+14/-14)
mixxx/src/controlobjectthreadmain.h (+9/-24)
mixxx/src/controlobjectthreadwidget.cpp (+33/-46)
mixxx/src/controlobjectthreadwidget.h (+7/-10)
mixxx/src/controlpotmeter.cpp (+68/-135)
mixxx/src/controlpotmeter.h (+10/-12)
mixxx/src/controlpushbutton.cpp (+22/-50)
mixxx/src/controlpushbutton.h (+1/-7)
mixxx/src/controlttrotary.cpp (+9/-27)
mixxx/src/controlttrotary.h (+2/-12)
mixxx/src/dlgprefeq.cpp (+4/-2)
mixxx/src/dlgprefeq.h (+1/-1)
mixxx/src/dlgtrackinfo.h (+9/-1)
mixxx/src/engine/bpmcontrol.cpp (+1/-1)
mixxx/src/engine/enginemaster.cpp (+0/-6)
mixxx/src/engine/enginemaster.h (+0/-1)
mixxx/src/engine/loopingcontrol.cpp (+41/-22)
mixxx/src/engine/ratecontrol.cpp (+10/-5)
mixxx/src/engine/syncworker.cpp (+0/-37)
mixxx/src/engine/syncworker.h (+0/-23)
mixxx/src/mixxx.cpp (+0/-7)
mixxx/src/mixxx.h (+0/-1)
mixxx/src/mixxxkeyboard.cpp (+2/-2)
mixxx/src/playermanager.cpp (+73/-44)
mixxx/src/playermanager.h (+17/-3)
mixxx/src/skin/legacyskinparser.cpp (+0/-10)
mixxx/src/skin/propertybinder.cpp (+1/-2)
mixxx/src/sounddeviceportaudio.cpp (+3/-2)
mixxx/src/soundmanager.cpp (+0/-6)
mixxx/src/soundmanager.h (+0/-2)
mixxx/src/vinylcontrol/vinylcontrolmanager.cpp (+10/-10)
mixxx/src/waveform/renderers/waveformmarkset.cpp (+2/-2)
mixxx/src/waveform/renderers/waveformrendererendoftrack.cpp (+1/-1)
mixxx/src/widget/wpushbutton.cpp (+2/-1)
mixxx/src/widget/wslidercomposed.cpp (+57/-44)
To merge this branch: bzr merge lp:~mixxxdevelopers/mixxx/atomic-co
Reviewer Review Type Date Requested Status
Daniel Schürmann Needs Fixing
RJ Skerry-Ryan Needs Fixing
Review via email: mp+153696@code.launchpad.net

Description of the change

This is the first transition of the control object part of Mixxx for a proof of concept as described in http://mixxx.org/wiki/doku.php/revamped_control_system

The core change was made in http://bazaar.launchpad.net/~mixxxdevelopers/mixxx/atomic-co/view/head:/mixxx/src/controlobjectbase.h

The class ControlObjectValue provides a lock-free thread save access to the value of the control object. Relying on this, it was possible to remove the mirrored values in the control object proxies and all the ugly code for syncing it. The new concept should provide an instant access to the control object values without possible delays of sync tasks or overloaded queues.

I have tried to keep the interface of the control objects untouched. Defining a suitable interface, and allowing different value types will be issued in the next transition, when this one is approved.

When reviewing please, give a particularly critical look to ControlObjectValue for verify that it provides such an atomic access.

To post a comment you must log in.
lp:~mixxxdevelopers/mixxx/atomic-co updated
3350. By Daniel Schürmann

removed volatile qualifier for being QString compatible

3351. By Daniel Schürmann

merged from lp:mixxx

3352. By Daniel Schürmann

merged from lp:mixxx

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

Sorry for taking so long to get you feedback :( better late than never?

I've skimmed the whole thing once but for now will just comment on controlobjectbase.h:

* static const int cReaderSlotCnt
  - Should not be static.. if multiple object files include controlobjectbase.h then we will get duplicate symbols during linking. This builds fine now because only controlobject.o includes it. You could just leave it as a const int.

* sizeof(T) <= sizeof(void*) is not enough to determine atomicity -- for example, if the pointer to a value is not memory-aligned then that breaks atomicity guarantees. I think to be sure we need to use a compiler directive in the ATOMIC=true template to request m_value is aligned.

* Need comments on all methods declarations in controlobjectbase.h. This is such a crucial file it needs plenty of documentation for future developers :).

* ControlObjectRingValue::tryGet()
  - why return an int when it's functionally a bool (i.e. was it successful)
  - bool originalSlots = m_readerSlots.fetchAndAddAcquire(-1);
  Should instead be:
    bool originalSlots = m_readerSlots.fetchAndAddAcquire(-1) <= 0;

The reason is this: m_readerSlots can go negative if too many concurrent reads occur simultaneously. It can even go negative if a write is happening and two reads happen right after the write has swapped m_readerSlots with the value 0 (the second read will see -1). So to detect whether a read should be successful, you should check if the value is <= 0.

  - Why is cReaderSlotCnt the number of concurrent readers allowed also the size of the ring buffer? I can't think of a reason why those 2 values need to be the same.
  - How did you choose 7?

* ControlObjectValue::getValue
  - Can you use ARRAYSIZE on m_ring instead of hard-coding cReaderSlotCnt+1 ? It should be compile-time.
  - When getValue() fails to read a slot, it goes backward one. This means we sacrifice consistency for speed. We need to make sure people understand this is a trade-off that is part of the control system. Under high load, the control system will possibly serve a stale result when you get().

lp:~mixxxdevelopers/mixxx/atomic-co updated
3353. By Daniel Schürmann

merged from lp:mixxx

3354. By Daniel Schürmann

removed static keyword

3355. By Daniel Schürmann

reinterpret_cast to QAtomicPointer

3356. By Daniel Schürmann

added comments and removed unused template class

3357. By Daniel Schürmann

fixed ControlObjectRingValue::tryGet()

3358. By Daniel Schürmann

added more comments

3359. By Daniel Schürmann

cRingSize added + an assertion to check for valid values

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

Thank you for review!

> * static const int cReaderSlotCnt
OK

> * sizeof(T) <= sizeof(void*) is not enough to determine atomicity
OK, It should be save in any case, if we cast to an QAtomicPointer container
But that looks really ugly. But better ugly than a failure!

> * Need comments
OK

> * ControlObjectRingValue::tryGet()
Fixed.

> - Why is cReaderSlotCnt the number of concurrent readers allowed also the size of the ring buffer? I can't think of a reason why those 2 values need to be the same.

In worst case, each reader can consume a reader slot from a different ring element. In this case there is still one ring element available for writing.

> - How did you choose 7?
I want to have 8 ring element because it is a 2^x value.
To be sure we are lock free, we need on ring element for each accessing thread. I have not counted CO accessing threads but I think 8 should not be reached. We should verify this.
But It should also work with more tasks, because it is very unlikely that 8 threads are in the critical section at the same time. And when it relay happens, the additional threads are locked.

> - Can you use ARRAYSIZE
OK

> - When getValue() fails to read a slot, it goes backward one. This means we sacrifice consistency for speed. We need to make sure people understand this is a trade-off that is part of the control system. Under high load, the control system will possibly serve a stale result when you get().

If there are enough ring elements this should not happen. The value is not stale, because the more recent value is not entirely written.

Revision history for this message
William Good (bkgood) wrote :

There appear to be some issues with class ControlObjectValue<T, true>.

First, assuming sizeof(double) <= sizeof(void*) (which is the case on my machine), this class is used for ControlObjectValue<double>. This results in casts like reinterpret_case<void*>(x), where x is a double. These aren't legal casts (and as such, I can't actually compile the branch). To store a double in a QAtomicPointer<void*>, you have to do something like (assuming sizeof(void*)==sizeof(uint64_t) and sizeof(double)==sizeof(uint64_t)):

    QAtomicPointer<void*> qap;
    double x = 1.5;
    qap = reinterpret_cast<void**>(*reinterpret_cast<uint64_t*>(&x));

    void *qap_val = qap;
    double y = *reinterpret_cast<double*>(&qap_val);
    cout << y << endl; // prints 1.5

(some of the complexity exists because QAtomicPointer doesn't overload the deref (*) operator, it provides an implicit type conversion to T*, which means *qap here actually dereferences our stored "pointer", so we have to use void *p = gap which invokes the operator void*() without dereferencing anything)

But regardless, we're not really gaining anything from QAtomicPointer. It's storing a T* in a data member marked volatile (which doesn't even guarantee alignment afaik, which is something we need), and for store and retrieve operations it's not doing anything special (see src/corelib/thread/q{,basic}atomic.h in the qt source). We'd be better off just using a volatile void * in the class, just as a reduction in complexity.

Along these lines:
        return reinterpret_cast<T>(m_container);

As far as I can tell, this is actually trying to cast the QAtomicPointer<void*> to T (an illegal case). This is almost certainly not desirable, but we really want the thing we've stored in the QAP<void*>, cast to T (which requires some bit-fiddling like what I wrote above).

Revision history for this message
William Good (bkgood) wrote :

Happened to come across this:

Load a song.
Set a hotcue (if not already set).
Hold down the hotcue key (eg. z).
Press play/pause (either in the UI or on the keyboard).

In truck, it allows you to continue playing the track without holding the hotcue key; in the branch, it segfaults. It happens both with and without the QAtomicPointer code, so I guess it's another change. It seems easily repeatable, so I'm not attaching a backtrace, but I can if needed.

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

RE: My previous comment about atomicity.. I think we can actually get around it without QAtomicPointer, we just need to use compiler-specific alignment directives.

e.g. for GCC: __attribute__ ((aligned (__BIGGEST_ALIGNMENT__)))

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

Now for some comments on the broader changes in the branch outside of COBase:

I think COBase is a nice wait-free base for CO to be based on. Getting rid of the proxies (in this case, turning them into shims) is problematic though. The valueChanged() signal is dangerous and can cause infinite processing loops.

If you listen to valueChanged and some code path in your slot for valueChanged can set the control again then you can have an infinite loop. The proxies are an important part of preventing this because when they queue their change to the control system, they provide a pointer to themselves. CO::sync() then does not deliver the change to the proxy that changed the value.

We are also going to run into many logic errors as we track down the places in Mixxx that have encoded assumptions about the fact that there is an intermediary between setting a COT and the corresponding CO change taking effect.

I think a good solution is one where every control set()/get()s the actual CO value through a proxy that provides a pointer to the original changer. The COBase then emits this pointer in its valueChanged signal. Listening proxies can then check if they originated the set and not emit a valueChanged() in that case.

This brings me to valueChangedFromEngine(double). This signal exists in the first place to be able to differentiate between sets to a control from the current instance of a control or sets to a control from a proxy. What I described from the previous paragraph is just a generalization of valueChangedFromEngine().

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

> > - Why is cReaderSlotCnt the number of concurrent readers allowed also the size of the ring buffer? I can't think of a reason why those 2 values need to be the same.

> In worst case, each reader can consume a reader slot from a different ring element. In this case there is still one ring element available for writing.

If I understand this right, every slot in the ring buffer can support cReaderSlotCnt readers. (i.e. reads fail after cReaderSlotCnt readers are active). When reads fail, a reader moves backward one in the ring buffer to the previous value to read.

So, worst case scenario (for a write), cReaderSlotCnt*cReaderSlotCnt+1 concurrent reads are occurring and there are no available write slots. I don't see how anything related to cReaderSlotCnt preserves an extra slot for writing? We could just have more readers.

> I want to have 8 ring element because it is a 2^x value.
> To be sure we are lock free, we need on ring element for each accessing thread. I have not counted CO accessing threads but I think 8 should not be reached. We should verify this.
> But It should also work with more tasks, because it is very unlikely that 8 threads are in the critical section at the same time. And when it relay happens, the additional threads are locked.

So, to characterize the scenarios when a thread spin-locks:

When (cReaderCnt+1)*cReaderCnt concurrent reads are occurring, then a reader will be spin-locked.
When cReaderCnt+1 concurrent writes are occurring, then a reader will be spin-locked.

When cReaderCnt*cReaderCnt + 1 concurrent reads are occurring, then a writer will be spin-locked.
When cReaderCnt+1 concurrent writes are occurring, then a writer will be spin-locked.

(there is no actual locking, just busy-waiting).

> > - When getValue() fails to read a slot, it goes backward one. This means we sacrifice consistency for speed. We need to make sure people understand this is a trade-off that is part of the control system. Under high load, the control system will possibly serve a stale result when you get().

> If there are enough ring elements this should not happen. The value is not stale, because the more recent value is not entirely written.

This can happen under excessive read-pressure (> cReaderSlotCnt concurrent reads) alone so I don't think that's true... (unless I'm missing something about how this works).

I don't see why cReaderSlotCnt and the ring buffer size need to be related. You could just pick cReaderSlotCnt as INT_MAX and make the ring buffer size 8 and we would effectively never have inconsistent reads under read pressure alone. Isn't cReaderSlotCnt just an arbitrary value we count down to 0 from?

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

atomicity:

I think it should be really fine to simply revert #3355.
But RJ is right, the native alignment is not guarantied by the c++ standard but actually the compilers will align natively by default.
I have a look at the implementation of QtAtomicInt and they seems not care about alignment.

But it will not hurt to do something like this, just to be sure:

T m_value __attribute__ ((aligned(sizeof(void*)))); // GCC
T __declspec(align(sizeof(void*))) m_value; // MSVC

This is a nice thread:
http://stackoverflow.com/questions/5002046/atomicity-in-c-myth-or-reality

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

changes outside of COBase:

* Risk of infinite processing loops
Yes this is right. I have already fixed some of those conditions.
My Idea was to solve it in the calling code, to keep the CO core clean and performant.
The key benefit is to get rid of the sync thread.
Do you have a idea to solve it without it?

* intermediary between setting a COT and the corresponding CO change
This should be fixed in the calling code in any case, because this is was on of my goals.

>
> I think a good solution is one where every control set()/get()s the actual CO
> value through a proxy that provides a pointer to the original changer. The
> COBase then emits this pointer in its valueChanged signal. Listening proxies
> can then check if they originated the set and not emit a valueChanged() in
> that case.
>
> This brings me to valueChangedFromEngine(double). This signal exists in the
> first place to be able to differentiate between sets to a control from the
> current instance of a control or sets to a control from a proxy. What I
> described from the previous paragraph is just a generalization of
> valueChangedFromEngine().
>

We might introduce a
setFrom(double value, QObject* source)
function and emit the the source pointer along with the value change event.
Then the receiver can throw the event away if it is it's own.

But I would prefer not to introduce this overhead.
The current rule is very simple and easy to understand:
"If you change a CO you get the signal ... always"

But there is still work to do find all places where this causes issues.

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

Number of Reader slots:

RJ, you understand the concept correct. I am afraid better then me ;-)

You idea is a relay good workaround for the readers spin lock. I will change the cReaderSlots to Maximum.

But there is still a reader count limit:
Since a writer consumes all reader slots from a writer we have a writer spin-lock if one reader reads on every ring element (cRingSize).

I think this condition is really hard to achieve in real live. Do we have to deal with it?

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

Sometimes it's hard to tell that you're going to get an infinite loop. The loop could even span across multiple different CO changes. It's much easier to code and requires less of a "full view" of the system if the CO you set() does not emit a valueChanged for the set(). I have an idea for how to do this without sacrificing any performance. I'll hack it up in a branch based on this one and send you a merge request.

Revision history for this message
Owen Williams (ywwg) wrote :

In my master_sync work, the infinite-CO loop is a humongous pain in the ass. Setting something to master will set other things to slave, and setting other things to slave might set something else to master... If I had a simple call to ask "did I initiate this chain of events?" then I could avoid a lot of ugly code I've had to create.

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

Also, I just noticed the change to ControlObjectThreadMain -- that needs to be reverted. When you use a COTM/COTW in Mixxx code you are declaring that valueChanged signals are expected to be delivered by the GUI/main thread so we need to save this behavior or we will run into trouble.

Without posting the event to the Qt event queue as it was implemented there's no guarantee that a valueChanged signal coming from the ControlObject will be received on the GUI thread.

review: Needs Fixing
lp:~mixxxdevelopers/mixxx/atomic-co updated
3360. By Daniel Schürmann

merged rollback -r 3360 lp:~rryan/mixxx/atomic-co

3361. By Daniel Schürmann

set cReaderSlotCnt = std::numeric_limits<int>::max()

3362. By Daniel Schürmann

added alignement keywords

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

I added COTM main-thread proxying back in lp:~rryan/mixxx/atomic-co.

lp:~mixxxdevelopers/mixxx/atomic-co updated
3363. By Daniel Schürmann

merged from lp:~rryan/mixxx/atomic-co

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

Here some results:

* I think the CO is registered twice: In ControlObject::ControlObject() and in ControlDoublePrivate::getControl()
* it is not clear where the home of m_sqCOHash is, for me it should move to ControlDoublePrivate
* all static functions should have the leading comment // static
* control.cpp/h should be renamed to controldoubleprivate.cpp/h
* DRY violation in overloaded ControlObject::ControlObject()
* setMidiParameter needs a pSender
* gcc complains about the missing virtual destructors in the *Behaviour classes
* i do not like to distingish between valueChanged(v) and valueChangedByThis(v), because it is most likely that if an object wants to receive valueChangedByThis(v) it will also receive valueChanged(v).
For this propose we should implement a new proxy which sends out pSender, or allow to use ControlDoublePrivate directly

please not the merge request with additional changes

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

On May 16, 2013 7:46 PM, "Daniel Schürmann" <email address hidden> wrote:
>
> Review: Needs Fixing
>
> Here some results:
>
> * I think the CO is registered twice: In ControlObject::ControlObject()
and in ControlDoublePrivate::getControl()

On mobile - sorry for brevity.

We have to keep this until we get rid of CO::getControl

> * it is not clear where the home of m_sqCOHash is, for me it should move
to control double private

Agree but see above.

> * all static functions should have the leading comment // static

Thx will change

> * control.cpp/h should be renamed to controldoubleprivate.cpp/h

I think all control implementations should go in control.h so there's one
header to include. I could see this not being a public part of the control
system so maybe that makes sense.

> * DRY violation in overloaded ControlObject::ControlObject()

Will fix with a common init method

> * setMidiParameter needs a pSender

The sender in this case is the midi controller and the midi controller uses
raw COs instead of creating a COT so its not correct to do this how you did
... When MController gets changed to use COT then that should be the sender

> * gcc complains about the missing virtual destructors in the *Behaviour
classes

K can fix

> * i do not like to distingish between valueChanged(v) and
valueChangedByThis(v), because it is most likely that if an object wants to
receive valueChangedByThis(v) it will also receive valueChanged(v).

Yes but that's because by default code is not safe until carefully reviewed
for infinite loop prevention. Once it is reviewed it is natural we would
like to respond to both internal and external. There may be cases in the
future where you would only want to react to internally sourced changes.

> For this propose we should implement a new proxy which sends out pSender,
or allow to use ControlDoublePrivate directly

I've explained why we need this signal -- it's safer for writing code
because it avoids infinite feedback loops and it allows you to distinguish
between local and remote value changes (which is already used today in
mixxx).

Can you explain why you think we should not do this?

>
> please not the merge request with additional changes
> --
> https://code.launchpad.net/~mixxxdevelopers/mixxx/atomic-co/+merge/153696
> You are reviewing the proposed merge of
lp:~mixxxdevelopers/mixxx/atomic-co into lp:mixxx.

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

> > * i do not like to distingish between valueChanged(v) and
> valueChangedByThis(v), because it is most likely that if an object wants to
> receive valueChangedByThis(v) it will also receive valueChanged(v).
>
> Yes but that's because by default code is not safe until carefully reviewed
> for infinite loop prevention. Once it is reviewed it is natural we would
> like to respond to both internal and external. There may be cases in the
> future where you would only want to react to internally sourced changes.
>
> > For this propose we should implement a new proxy which sends out pSender,
> or allow to use ControlDoublePrivate directly
>
> I've explained why we need this signal -- it's safer for writing code
> because it avoids infinite feedback loops and it allows you to distinguish
> between local and remote value changes (which is already used today in
> mixxx).
>
> Can you explain why you think we should not do this?
>

I the meanwhile I am convinced that we don't need the loop back signal at all.
It is allays lower overhead to call the slot manually if required and it is
more easy to track when reading the code.

lp:~mixxxdevelopers/mixxx/atomic-co updated
3364. By RJ Skerry-Ryan

Static comments.

3365. By RJ Skerry-Ryan

Behavior implementations to cpp.

3366. By RJ Skerry-Ryan

Eliminate duplication in ControlObject constructor.

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 2013-05-15 06:09:40 +0000
3+++ mixxx/build/depends.py 2013-05-17 16:35:30 +0000
4@@ -420,18 +420,18 @@
5 sources = ["mixxxkeyboard.cpp",
6
7 "configobject.cpp",
8+ "control/control.cpp",
9+ "control/controlbehavior.cpp",
10 "controlobjectthread.cpp",
11 "controlobjectthreadwidget.cpp",
12 "controlobjectthreadmain.cpp",
13 "controlevent.cpp",
14 "controllogpotmeter.cpp",
15 "controlobject.cpp",
16- "controlnull.cpp",
17 "controlpotmeter.cpp",
18 "controllinpotmeter.cpp",
19 "controlpushbutton.cpp",
20 "controlttrotary.cpp",
21- "controlbeat.cpp",
22
23 "dlgpreferences.cpp",
24 "dlgprefsound.cpp",
25@@ -455,7 +455,6 @@
26
27 "engine/engineworker.cpp",
28 "engine/engineworkerscheduler.cpp",
29- "engine/syncworker.cpp",
30 "engine/enginebuffer.cpp",
31 "engine/enginebufferscale.cpp",
32 "engine/enginebufferscaledummy.cpp",
33
34=== added file 'mixxx/res/controllers/Hercules DJ Console RMX 2.midi.xml'
35--- mixxx/res/controllers/Hercules DJ Console RMX 2.midi.xml 1970-01-01 00:00:00 +0000
36+++ mixxx/res/controllers/Hercules DJ Console RMX 2.midi.xml 2013-05-17 16:35:30 +0000
37@@ -0,0 +1,963 @@
38+<MixxxMIDIPreset mixxxVersion="1.10.1+" schemaVersion="1">
39+ <info>
40+ <!-- In Geany, go to Document and then Fold All for easier format. -->
41+ <name>Hercules DJ Console RMX 2</name>
42+ <author>Circuitfry</author>
43+ <description>This console's mapping comes with a script.</description>
44+ </info>
45+ <controller id="DJConsole Rmx2">
46+ <scriptfiles>
47+ <file filename="Hercules-DJ-Console-RMX-2-scripts.js" functionprefix="DJCRMX2"/>
48+ </scriptfiles>
49+ <controls>
50+ <!-- General Controls -->
51+ <control><!-- Master Volume Pt 1 -->
52+ <status>0xb0</status>
53+ <midino>0x44</midino>
54+ <group>[Master]</group>
55+ <key>volume</key>
56+ <description></description>
57+ <options>
58+ <normal/>
59+ </options>
60+ </control>
61+ <control><!-- Master HeadMix Pt 1 -->
62+ <status>0xb0</status>
63+ <midino>0x46</midino>
64+ <group>[Master]</group>
65+ <key>headMix</key>
66+ <description></description>
67+ <options>
68+ <normal/>
69+ </options>
70+ </control>
71+ <control><!-- Master Crossfader Pt 1 -->
72+ <status>0xb0</status>
73+ <midino>0x48</midino>
74+ <group>[Master]</group>
75+ <key>crossfader</key>
76+ <description></description>
77+ <options>
78+ <normal/>
79+ </options>
80+ </control>
81+ <control><!-- Microphone -->
82+ <status>0x90</status> <!-- ??? -->
83+ <midino>0x48</midino>
84+ <group>[Microphone]</group>
85+ <key>DJCRMX2.micSwitch</key>
86+ <description>Utilize Microphone</description>
87+ <options>
88+ <Script-Binding/>
89+ </options>
90+ </control>
91+ <control><!-- Select UP -->
92+ <status>0x90</status>
93+ <midino>0x45</midino>
94+ <group>[Playlist]</group>
95+ <key>SelectPrevTrack</key>
96+ <description></description>
97+ <options>
98+ <normal/>
99+ </options>
100+ </control>
101+ <control><!-- Select DOWN -->
102+ <status>0x90</status>
103+ <midino>0x46</midino>
104+ <group>[Playlist]</group>
105+ <key>SelectNextTrack</key>
106+ <description></description>
107+ <options>
108+ <normal/>
109+ </options>
110+ </control>
111+ <control><!-- Select LEFT -->
112+ <status>0x90</status>
113+ <midino>0x44</midino>
114+ <group>[Playlist]</group>
115+ <key>SelectPrevPlaylist</key>
116+ <description></description>
117+ <options>
118+ <normal/>
119+ </options>
120+ </control>
121+ <control><!-- Select RIGHT -->
122+ <status>0x90</status>
123+ <midino>0x43</midino>
124+ <group>[Playlist]</group>
125+ <key>SelectNextPlaylist</key>
126+ <description></description>
127+ <options>
128+ <normal/>
129+ </options>
130+ </control>
131+ <!-- General Controls -->
132+ <!-- Deck Controls -->
133+ <control><!-- Left Load Song -->
134+ <status>0x90</status>
135+ <midino>0x24</midino>
136+ <group>[Channel1]</group>
137+ <key>LoadSelectedTrack</key>
138+ <description></description>
139+ <options>
140+ <normal/>
141+ </options>
142+ </control>
143+ <control><!-- Right Load Song -->
144+ <status>0x90</status>
145+ <midino>0x35</midino>
146+ <group>[Channel2]</group>
147+ <key>LoadSelectedTrack</key>
148+ <description></description>
149+ <options>
150+ <normal/>
151+ </options>
152+ </control>
153+ <control><!-- L Play -->
154+ <status>0x90</status>
155+ <midino>0x21</midino>
156+ <group>[Channel1]</group>
157+ <key>play</key>
158+ <description></description>
159+ <options>
160+ <normal/>
161+ </options>
162+ </control>
163+ <control><!-- R Play -->
164+ <status>0x90</status>
165+ <midino>0x32</midino>
166+ <group>[Channel2]</group>
167+ <key>play</key>
168+ <description></description>
169+ <options>
170+ <normal/>
171+ </options>
172+ </control>
173+ <control><!-- Left Headphones -->
174+ <status>0x90</status>
175+ <midino>0x2e</midino>
176+ <group>[Channel1]</group>
177+ <key>pfl</key>
178+ <description></description>
179+ <options>
180+ <normal/>
181+ </options>
182+ </control>
183+ <control><!-- Right Headphones -->
184+ <status>0x90</status>
185+ <midino>0x3F</midino>
186+ <group>[Channel2]</group>
187+ <key>pfl</key>
188+ <description></description>
189+ <options>
190+ <normal/>
191+ </options>
192+ </control>
193+ <control><!-- L Cue -->
194+ <status>0x90</status>
195+ <midino>0x22</midino>
196+ <group>[Channel1]</group>
197+ <key>cue_default</key>
198+ <description></description>
199+ <options>
200+ <normal/>
201+ </options>
202+ </control>
203+ <control><!-- R Cue -->
204+ <status>0x90</status>
205+ <midino>0x33</midino>
206+ <group>[Channel2]</group>
207+ <key>cue_default</key>
208+ <description></description>
209+ <options>
210+ <normal/>
211+ </options>
212+ </control>
213+ <control><!-- L Sync -->
214+ <status>0x90</status>
215+ <midino>0x23</midino>
216+ <group>[Channel1]</group>
217+ <key>beatsync</key>
218+ <description></description>
219+ <options>
220+ <normal/>
221+ </options>
222+ </control>
223+ <control><!-- R Sync -->
224+ <status>0x90</status>
225+ <midino>0x34</midino>
226+ <group>[Channel2]</group>
227+ <key>beatsync</key>
228+ <description></description>
229+ <options>
230+ <normal/>
231+ </options>
232+ </control>
233+ <control><!-- L Tempo Slider Pt 1 -->
234+ <status>0xb0</status>
235+ <midino>0x36</midino>
236+ <group>[Channel1]</group>
237+ <key>rate</key>
238+ <description></description>
239+ <options>
240+ <normal/>
241+ </options>
242+ </control>
243+ <control><!-- R Tempo Slider Pt 1 -->
244+ <status>0xb0</status>
245+ <midino>0x38</midino>
246+ <group>[Channel2]</group>
247+ <key>rate</key>
248+ <description></description>
249+ <options>
250+ <normal/>
251+ </options>
252+ </control>
253+ <control><!-- L Fader Pt 1 -->
254+ <status>0xb0</status>
255+ <midino>0x3a</midino>
256+ <group>[Channel1]</group>
257+ <key>volume</key>
258+ <description></description>
259+ <options>
260+ <normal/>
261+ </options>
262+ </control>
263+ <control><!-- R Fader Pt 1 -->
264+ <status>0xb0</status>
265+ <midino>0x4a</midino>
266+ <group>[Channel2]</group>
267+ <key>volume</key>
268+ <description></description>
269+ <options>
270+ <normal/>
271+ </options>
272+ </control>
273+ <control><!-- Left Fast Rewind -->
274+ <status>0x90</status>
275+ <midino>0x26</midino>
276+ <group>[Channel1]</group>
277+ <key>back</key>
278+ <description></description>
279+ <options>
280+ <normal/>
281+ </options>
282+ </control>
283+ <control><!-- Right Fast Rewind -->
284+ <status>0x90</status>
285+ <midino>0x37</midino>
286+ <group>[Channel2]</group>
287+ <key>back</key>
288+ <description></description>
289+ <options>
290+ <normal/>
291+ </options>
292+ </control>
293+ <control><!-- Left Fast Forward -->
294+ <status>0x90</status>
295+ <midino>0x27</midino>
296+ <group>[Channel1]</group>
297+ <key>fwd</key>
298+ <description></description>
299+ <options>
300+ <normal/>
301+ </options>
302+ </control>
303+ <control><!-- Right Fast Forward -->
304+ <status>0x90</status>
305+ <midino>0x38</midino>
306+ <group>[Channel2]</group>
307+ <key>fwd</key>
308+ <description></description>
309+ <options>
310+ <normal/>
311+ </options>
312+ </control>
313+ <control><!-- Left Pitch Down -->
314+ <status>0x90</status>
315+ <midino>0x2C</midino>
316+ <group>[Channel1]</group>
317+ <key>rate_temp_down_small</key>
318+ <description>L Pitch Down</description>
319+ <options>
320+ <normal/>
321+ </options>
322+ </control>
323+ <control><!-- Left Pitch Up -->
324+ <status>0x90</status>
325+ <midino>0x2D</midino>
326+ <group>[Channel1]</group>
327+ <key>rate_temp_up_small</key>
328+ <description>L Pitch Down</description>
329+ <options>
330+ <normal/>
331+ </options>
332+ </control>
333+ <control><!-- Right Pitch Down -->
334+ <status>0x90</status>
335+ <midino>0x3D</midino>
336+ <group>[Channel2]</group>
337+ <key>rate_temp_down_small</key>
338+ <description>R Pitch Down</description>
339+ <options>
340+ <normal/>
341+ </options>
342+ </control>
343+ <control><!-- Right Pitch Up -->
344+ <status>0x90</status>
345+ <midino>0x3E</midino>
346+ <group>[Channel2]</group>
347+ <key>rate_temp_up_small</key>
348+ <description>R Pitch Down</description>
349+ <options>
350+ <normal/>
351+ </options>
352+ </control>
353+ <!-- Deck Controls -->
354+ <!-- Jog Wheels -->
355+ <control><!-- Left Press Down -->
356+ <status>0x90</status>
357+ <midino>0x2F</midino>
358+ <group>[Channel1]</group>
359+ <key>DJCRMX2.wheelPress</key>
360+ <description>Jog Wheel L Pressed Down</description>
361+ <options>
362+ <Script-Binding/>
363+ </options>
364+ </control>
365+ <control><!-- Right Press Down -->
366+ <status>0x90</status>
367+ <midino>0x40</midino>
368+ <group>[Channel2]</group>
369+ <key>DJCRMX2.wheelPress</key>
370+ <description>Jog Wheel R Pressed Down</description>
371+ <options>
372+ <Script-Binding/>
373+ </options>
374+ </control>
375+ <control><!-- Left Spin -->
376+ <status>0xB0</status>
377+ <midino>0x30</midino>
378+ <group>[Channel1]</group>
379+ <key>DJCRMX2.wheelTurn</key>
380+ <description>Wheel Turning (regular)</description>
381+ <options>
382+ <Script-Binding/>
383+ </options>
384+ </control>
385+ <control><!-- Right Spin -->
386+ <status>0xB0</status>
387+ <midino>0x31</midino>
388+ <group>[Channel2]</group>
389+ <key>DJCRMX2.wheelTurn</key>
390+ <description>Wheel Turning (regular)</description>
391+ <options>
392+ <Script-Binding/>
393+ </options>
394+ </control>
395+ <control><!-- Left Scratch -->
396+ <status>0xB0</status>
397+ <midino>0x32</midino>
398+ <group>[Channel1]</group>
399+ <key>DJCRMX2.wheelTurn</key>
400+ <description>Wheel Turning (scratching)</description>
401+ <options>
402+ <Script-Binding/>
403+ </options>
404+ </control>
405+ <control><!-- Right Scratch -->
406+ <status>0xB0</status>
407+ <midino>0x33</midino>
408+ <group>[Channel2]</group>
409+ <key>DJCRMX2.wheelTurn</key>
410+ <description>Wheel Turning (scratching)</description>
411+ <options>
412+ <Script-Binding/>
413+ </options>
414+ </control>
415+ <!-- Jog Wheels -->
416+ <!-- EQ Controls -->
417+ <control><!-- Left Knob High 1 -->
418+ <status>0xb0</status>
419+ <midino>0x3c</midino>
420+ <group>[Channel1]</group>
421+ <key>filterHigh</key>
422+ <description></description>
423+ <options>
424+ <normal/>
425+ </options>
426+ </control>
427+ <control><!-- Left Knob Mid 1 -->
428+ <status>0xb0</status>
429+ <midino>0x3e</midino>
430+ <group>[Channel1]</group>
431+ <key>filterMid</key>
432+ <description></description>
433+ <options>
434+ <normal/>
435+ </options>
436+ </control>
437+ <control><!-- Left Knob Low 1 -->
438+ <status>0xb0</status>
439+ <midino>0x40</midino>
440+ <group>[Channel1]</group>
441+ <key>filterLow</key>
442+ <description></description>
443+ <options>
444+ <normal/>
445+ </options>
446+ </control>
447+ <control><!-- Left Kill High -->
448+ <status>0x90</status>
449+ <midino>0x28</midino>
450+ <group>[Channel1]</group>
451+ <key>filterHighKill</key>
452+ <description>High Left Kill</description>
453+ <options>
454+ <normal/>
455+ </options>
456+ </control>
457+ <control><!-- Left Kill Mid -->
458+ <status>0x90</status>
459+ <midino>0x29</midino>
460+ <group>[Channel1]</group>
461+ <key>filterMidKill</key>
462+ <description>Mid Left Kill</description>
463+ <options>
464+ <normal/>
465+ </options>
466+ </control>
467+ <control><!-- Left Kill Low -->
468+ <status>0x90</status>
469+ <midino>0x2A</midino>
470+ <group>[Channel1]</group>
471+ <key>filterLowKill</key>
472+ <description>Low Left Kill</description>
473+ <options>
474+ <normal/>
475+ </options>
476+ </control>
477+ <control><!-- Right Knob High 1 -->
478+ <status>0xb0</status>
479+ <midino>0x4c</midino>
480+ <group>[Channel2]</group>
481+ <key>filterHigh</key>
482+ <description></description>
483+ <options>
484+ <normal/>
485+ </options>
486+ </control>
487+ <control><!-- Right Knob Mid 1 -->
488+ <status>0xb0</status>
489+ <midino>0x4e</midino>
490+ <group>[Channel2]</group>
491+ <key>filterMid</key>
492+ <description></description>
493+ <options>
494+ <normal/>
495+ </options>
496+ </control>
497+ <control><!-- Right Knob Low 1 -->
498+ <status>0xb0</status>
499+ <midino>0x50</midino>
500+ <group>[Channel2]</group>
501+ <key>filterLow</key>
502+ <description></description>
503+ <options>
504+ <normal/>
505+ </options>
506+ </control>
507+ <control><!-- Right Kill High -->
508+ <status>0x90</status>
509+ <midino>0x39</midino>
510+ <group>[Channel2]</group>
511+ <key>filterHighKill</key>
512+ <description>High Right Kill</description>
513+ <options>
514+ <normal/>
515+ </options>
516+ </control>
517+ <control><!-- Right Kill Mid -->
518+ <status>0x90</status>
519+ <midino>0x3A</midino>
520+ <group>[Channel2]</group>
521+ <key>filterMidKill</key>
522+ <description>Mid Right Kill</description>
523+ <options>
524+ <normal/>
525+ </options>
526+ </control>
527+ <control><!-- Right Kill Low -->
528+ <status>0x90</status>
529+ <midino>0x3B</midino>
530+ <group>[Channel2]</group>
531+ <key>filterLowKill</key>
532+ <description>Low Right Kill</description>
533+ <options>
534+ <normal/>
535+ </options>
536+ </control>
537+ <!-- EQ Controls -->
538+ <!-- Samplers Controls [Sample Mode] 25% -->
539+ <control><!-- S1 Play -->
540+ <status>0x90</status>
541+ <midino>0x05</midino>
542+ <group>[Sampler1]</group>
543+ <key>play</key>
544+ <description></description>
545+ <options>
546+ <normal/>
547+ </options>
548+ </control>
549+ <control><!-- S2 Play -->
550+ <status>0x90</status>
551+ <midino>0x6</midino>
552+ <group>[Sampler2]</group>
553+ <key>play</key>
554+ <description></description>
555+ <options>
556+ <normal/>
557+ </options>
558+ </control>
559+ <control><!-- S3 Play -->
560+ <status>0x90</status>
561+ <midino>0x7</midino>
562+ <group>[Sampler3]</group>
563+ <key>play</key>
564+ <description></description>
565+ <options>
566+ <normal/>
567+ </options>
568+ </control>
569+ <control><!-- S4 Play -->
570+ <status>0x90</status>
571+ <midino>0x8</midino>
572+ <group>[Sampler4]</group>
573+ <key>play</key>
574+ <description></description>
575+ <options>
576+ <normal/>
577+ </options>
578+ </control>
579+ <control><!-- S1 Keylock -->
580+ <status>0x90</status>
581+ <midino>0x15</midino>
582+ <group>[Sampler1]</group>
583+ <key>keylock</key>
584+ <description></description>
585+ <options>
586+ <normal/>
587+ </options>
588+ </control>
589+ <control><!-- S2 Keylock -->
590+ <status>0x90</status>
591+ <midino>0x16</midino>
592+ <group>[Sampler2]</group>
593+ <key>keylock</key>
594+ <description></description>
595+ <options>
596+ <normal/>
597+ </options>
598+ </control>
599+ <control><!-- S3 Keylock -->
600+ <status>0x90</status>
601+ <midino>0x17</midino>
602+ <group>[Sampler3]</group>
603+ <key>keylock</key>
604+ <description></description>
605+ <options>
606+ <normal/>
607+ </options>
608+ </control>
609+ <control><!-- S4 Keylock -->
610+ <status>0x90</status>
611+ <midino>0x18</midino>
612+ <group>[Sampler4]</group>
613+ <key>keylock</key>
614+ <description></description>
615+ <options>
616+ <normal/>
617+ </options>
618+ </control>
619+ <control><!-- S1 Tempo Tap [UNIMPLEMENTED] -->
620+ <status>0xb0</status>
621+ <midino>0x15</midino>
622+ <group>[Sampler1]</group>
623+ <key>bpm</key>
624+ <description></description>
625+ <options>
626+ <normal/>
627+ </options>
628+ </control>
629+ <control><!-- S2 Tempo Tap [UNIMPLEMENTED] -->
630+ <status>0xb0</status>
631+ <midino>0x16</midino>
632+ <group>[Sampler2]</group>
633+ <key>bpm</key>
634+ <description></description>
635+ <options>
636+ <normal/>
637+ </options>
638+ </control>
639+ <control><!-- S3 Tempo Tap [UNIMPLEMENTED] -->
640+ <status>0xb0</status>
641+ <midino>0x17</midino>
642+ <group>[Sampler3]</group>
643+ <key>bpm</key>
644+ <description></description>
645+ <options>
646+ <normal/>
647+ </options>
648+ </control>
649+ <control><!-- S4 Tempo Tap [UNIMPLEMENTED] -->
650+ <status>0xb0</status>
651+ <midino>0x18</midino>
652+ <group>[Sampler4]</group>
653+ <key>bpm</key>
654+ <description></description>
655+ <options>
656+ <normal/>
657+ </options>
658+ </control>
659+ <control><!-- S1 Headphones [UNIMPLEMENTED] -->
660+ <status>0xb0</status>
661+ <midino>0x5</midino>
662+ <group>[Sampler1]</group>
663+ <key>pfl</key>
664+ <description></description>
665+ <options>
666+ <normal/>
667+ </options>
668+ </control>
669+ <control><!-- S2 Headphones [UNIMPLEMENTED] -->
670+ <status>0xb0</status>
671+ <midino>0x6</midino>
672+ <group>[Sampler2]</group>
673+ <key>pfl</key>
674+ <description></description>
675+ <options>
676+ <normal/>
677+ </options>
678+ </control>
679+ <control><!-- S3 Headphones [UNIMPLEMENTED] -->
680+ <status>0xb0</status>
681+ <midino>0x7</midino>
682+ <group>[Sampler3]</group>
683+ <key>pfl</key>
684+ <description></description>
685+ <options>
686+ <normal/>
687+ </options>
688+ </control>
689+ <control><!-- S4 Headphones [UNIMPLEMENTED] -->
690+ <status>0x80</status>
691+ <midino>0x30</midino>
692+ <group>[Sampler4]</group>
693+ <key>pfl</key>
694+ <description></description>
695+ <options>
696+ <normal/>
697+ </options>
698+ </control>
699+ <!-- Samplers Controls [Sample Mode] -->
700+ <!-- Hotcue Controls [Cue Mode] 0% -->
701+ <control><!-- LC1 Activate -->
702+ <status>0x90</status>
703+ <midino>0x9</midino>
704+ <group>[Channel1]</group>
705+ <key>hotcue_1_activate</key>
706+ <description></description>
707+ <options>
708+ <normal/>
709+ </options>
710+ </control>
711+ <control><!-- LC2 Activate -->
712+ <status>0x90</status>
713+ <midino>0x0A</midino>
714+ <group>[Channel1]</group>
715+ <key>hotcue_2_activate</key>
716+ <description></description>
717+ <options>
718+ <normal/>
719+ </options>
720+ </control>
721+ <control><!-- LC3 Activate -->
722+ <status>0x90</status>
723+ <midino>0x0b</midino>
724+ <group>[Channel1]</group>
725+ <key>hotcue_3_activate</key>
726+ <description></description>
727+ <options>
728+ <normal/>
729+ </options>
730+ </control>
731+ <control><!-- LC4 Activate -->
732+ <status>0x90</status>
733+ <midino>0x0c</midino>
734+ <group>[Channel1]</group>
735+ <key>hotcue_4_activate</key>
736+ <description></description>
737+ <options>
738+ <normal/>
739+ </options>
740+ </control>
741+ <control><!-- LC1 Clear -->
742+ <status>0xB0</status>
743+ <midino>0x09</midino>
744+ <group>[Channel1]</group>
745+ <key>hotcue_1_clear</key>
746+ <description></description>
747+ <options>
748+ <normal/>
749+ </options>
750+ </control>
751+ <control><!-- LC2 Clear -->
752+ <status>0xB0</status>
753+ <midino>0x0a</midino>
754+ <group>[Channel1]</group>
755+ <key>hotcue_2_clear</key>
756+ <description></description>
757+ <options>
758+ <normal/>
759+ </options>
760+ </control>
761+ <control><!-- LC3 Clear -->
762+ <status>0xB0</status>
763+ <midino>0x0b</midino>
764+ <group>[Channel1]</group>
765+ <key>hotcue_3_clear</key>
766+ <description></description>
767+ <options>
768+ <normal/>
769+ </options>
770+ </control>
771+ <control><!-- LC4 Clear -->
772+ <status>0xB0</status>
773+ <midino>0x0C</midino>
774+ <group>[Channel1]</group>
775+ <key>hotcue_4_clear</key>
776+ <description></description>
777+ <options>
778+ <normal/>
779+ </options>
780+ </control>
781+ <control><!-- RC1 Activate -->
782+ <status>0x90</status>
783+ <midino>0x19</midino>
784+ <group>[Channel2]</group>
785+ <key>hotcue_1_activate</key>
786+ <description></description>
787+ <options>
788+ <normal/>
789+ </options>
790+ </control>
791+ <control><!-- RC2 Activate -->
792+ <status>0x90</status>
793+ <midino>0x1a</midino>
794+ <group>[Channel2]</group>
795+ <key>hotcue_2_activate</key>
796+ <description></description>
797+ <options>
798+ <normal/>
799+ </options>
800+ </control>
801+ <control><!-- RC3 Activate -->
802+ <status>0x90</status>
803+ <midino>0x1b</midino>
804+ <group>[Channel2]</group>
805+ <key>hotcue_3_activate</key>
806+ <description></description>
807+ <options>
808+ <normal/>
809+ </options>
810+ </control>
811+ <control><!-- RC4 Activate -->
812+ <status>0x90</status>
813+ <midino>0x1c</midino>
814+ <group>[Channel2]</group>
815+ <key>hotcue_4_activate</key>
816+ <description></description>
817+ <options>
818+ <normal/>
819+ </options>
820+ </control>
821+ <control><!-- RC1 Clear [Should be Shift+RC1, v. 2.0] -->
822+ <status>0xB0</status>
823+ <midino>0x19</midino>
824+ <group>[Channel2]</group>
825+ <key>hotcue_1_clear</key>
826+ <description></description>
827+ <options>
828+ <normal/>
829+ </options>
830+ </control>
831+ <control><!-- RC2 Clear -->
832+ <status>0xB0</status>
833+ <midino>0x1a</midino>
834+ <group>[Channel2]</group>
835+ <key>hotcue_2_clear</key>
836+ <description></description>
837+ <options>
838+ <normal/>
839+ </options>
840+ </control>
841+ <control><!-- RC3 Clear -->
842+ <status>0xB0</status>
843+ <midino>0x1b</midino>
844+ <group>[Channel2]</group>
845+ <key>hotcue_3_clear</key>
846+ <description></description>
847+ <options>
848+ <normal/>
849+ </options>
850+ </control>
851+ <control><!-- RC4 Clear -->
852+ <status>0xB0</status>
853+ <midino>0x1c</midino>
854+ <group>[Channel2]</group>
855+ <key>hotcue_4_clear</key>
856+ <description></description>
857+ <options>
858+ <normal/>
859+ </options>
860+ </control>
861+ <!-- Hotcue Controls [Cue Mode] -->
862+ <!-- Effects Controls [Effect Mode] 70% -->
863+ <control><!-- LF On/Off x9 -->
864+ <status>0x90</status>
865+ <midino>0x1</midino>
866+ <group>[Channel1]</group>
867+ <key>flanger</key>
868+ <description></description>
869+ <options>
870+ <normal/>
871+ </options>
872+ </control>
873+ <control><!-- RF On/Off x9 -->
874+ <status>0x90</status>
875+ <midino>0x11</midino>
876+ <group>[Channel2]</group>
877+ <key>flanger</key>
878+ <description></description>
879+ <options>
880+ <normal/>
881+ </options>
882+ </control>
883+ <control><!-- Flanger Period L -->
884+ <status>0xA0</status>
885+ <midino>0x02</midino>
886+ <group>[Flanger]</group>
887+ <key>lfoPeriod</key>
888+ <description></description>
889+ <options>
890+ <normal/>
891+ </options>
892+ </control>
893+ <control><!-- Flanger Depth L -->
894+ <status>0xA0</status>
895+ <midino>0x03</midino>
896+ <group>[Flanger]</group>
897+ <key>lfoDepth</key>
898+ <description></description>
899+ <options>
900+ <normal/>
901+ </options>
902+ </control>
903+ <control><!-- Flanger Delay L -->
904+ <status>0xA0</status>
905+ <midino>0x04</midino>
906+ <group>[Flanger]</group>
907+ <key>lfoDelay</key>
908+ <description></description>
909+ <options>
910+ <normal/>
911+ </options>
912+ </control>
913+ <!-- Effects Controls [Effect Mode] -->
914+ <!-- Looping Controls [Loop Mode] 0% -->
915+ <control><!-- RLoop 4 -->
916+ <status>0x90</status>
917+ <midino>0x1d</midino>
918+ <group>[Channel2]</group>
919+ <key>beatloop_4_toggle</key>
920+ <description></description>
921+ <options>
922+ <normal/>
923+ </options>
924+ </control>
925+ <control><!-- RLoop 2 -->
926+ <status>0x90</status>
927+ <midino>0x1e</midino>
928+ <group>[Channel2]</group>
929+ <key>beatloop_2_toggle</key>
930+ <description></description>
931+ <options>
932+ <normal/>
933+ </options>
934+ </control>
935+ <control><!-- RLoop 1 -->
936+ <status>0x90</status>
937+ <midino>0x1f</midino>
938+ <group>[Channel2]</group>
939+ <key>beatloop_1_toggle</key>
940+ <description></description>
941+ <options>
942+ <normal/>
943+ </options>
944+ </control>
945+ <control><!-- RLoop 1/2 -->
946+ <status>0x90</status>
947+ <midino>0x20</midino>
948+ <group>[Channel2]</group>
949+ <key>beatloop_0.5_toggle</key>
950+ <description></description>
951+ <options>
952+ <normal/>
953+ </options>
954+ </control>
955+ <control><!-- LLoop 4 -->
956+ <status>0x90</status>
957+ <midino>0x0D</midino>
958+ <group>[Channel1]</group>
959+ <key>beatloop_4_toggle</key>
960+ <description></description>
961+ <options>
962+ <normal/>
963+ </options>
964+ </control>
965+ <control><!-- LLoop 2 -->
966+ <status>0x90</status>
967+ <midino>0x0e</midino>
968+ <group>[Channel1]</group>
969+ <key>beatloop_2_toggle</key>
970+ <description></description>
971+ <options>
972+ <normal/>
973+ </options>
974+ </control>
975+ <control><!-- LLoop 1 -->
976+ <status>0x90</status>
977+ <midino>0x0f</midino>
978+ <group>[Channel1]</group>
979+ <key>beatloop_1_toggle</key>
980+ <description></description>
981+ <options>
982+ <normal/>
983+ </options>
984+ </control>
985+ <control><!-- LLoop 1/2 -->
986+ <status>0x90</status>
987+ <midino>0x10</midino>
988+ <group>[Channel1]</group>
989+ <key>beatloop_0.5_toggle</key>
990+ <description></description>
991+ <options>
992+ <normal/>
993+ </options>
994+ </control>
995+ <!-- Looping Controls [Loop Mode] -->
996+ <!-- End of Controls -->
997+ </controls>
998+ <outputs/>
999+ </controller>
1000+</MixxxMIDIPreset>
1001
1002=== added file 'mixxx/res/controllers/Hercules-DJ-Console-RMX-2-scripts.js'
1003--- mixxx/res/controllers/Hercules-DJ-Console-RMX-2-scripts.js 1970-01-01 00:00:00 +0000
1004+++ mixxx/res/controllers/Hercules-DJ-Console-RMX-2-scripts.js 2013-05-17 16:35:30 +0000
1005@@ -0,0 +1,128 @@
1006+/*╔══:::Made Lovingly By Circuitfry:::═════════════════════════════════╗
1007+ ║ Hercules DJConsole RMX 2 Mapping Scripts v. 0.1.3 ║
1008+ ╚════════════════════════════════════════════════════════════════════╝
1009+ * Version 0.1.0: Basic Midi Wizard Mapping
1010+ * Version 0.1.1: Partially-Functional platters (version 1).
1011+ * Version 0.1.2: Functional platters (version 1)
1012+ * Version 0.1.3: Functional EQ Kill/Pitch Bending buttons
1013+ Functional Looping/Sample/Effect pads
1014+ Bugfix: Source 2 Gain knob doesn't load tracks.
1015+ Overhaul: MIDI Scripting file.
1016+ Worklog: Need to implement Microphone/Source1/Source2 input.
1017+ * Note 1: [DEP] Means the command is meant for Mixxx v1.10.x + below.
1018+ * Note 2: [FUT] Means the command is meant for Mixxx v1.11.x + above.
1019+*/
1020+function DJCRMX2(){}
1021+DJCRMX2.scratching = [];
1022+
1023+/* [ Function init ] - Version 0.1.3
1024+ * Initiates some global variables and assigns an ID. Required.
1025+*/
1026+DJCRMX2.init = function(id){
1027+ DJCRMX2.id = id;
1028+ DJCRMX2.scratching[1]=false;
1029+ DJCRMX2.scratching[2]=false;
1030+ engine.setValue("[Microphone]","enabled",0);
1031+ engine.setValue("[Microphone]","talkover",0);
1032+}
1033+
1034+/* [ Function wheelPress ] - Version 0.1.2
1035+ * Detects whether a jog wheel is pressed or not and sets a specific
1036+ * variable on and off accordingly.
1037+*/
1038+DJCRMX2.wheelPress = function (channel, control, value, status, group){
1039+ if (status == 0x90) // If status #144 is active (2 possibilities)
1040+ {
1041+ if (value == 0x7F) // And the jog wheel is pressed down:
1042+ { /* engine.scratchEnable(int,int,float,float,float,bool);
1043+ * [ int deck ] Which track/platter is playing?
1044+ * [ int intervalsPerRev ] # of MIDI signals sent in 1 spin.
1045+ * [ float rpm ] Imaginary vinyl rotation speed.
1046+ * [ float alpha ] Just a fine-tuning variable.
1047+ * [ float beta ] Just a fine-tuning variable.
1048+ * [ bool ramp ] As far as I know, nothing...
1049+ */
1050+ var alpha = 1.0/8;
1051+ var beta = alpha/32;
1052+ if(group=="[Channel1]")
1053+ {
1054+ engine.scratchEnable(1, 250, 50, alpha, beta);
1055+ DJCRMX2.scratching[1] = true; //[DEP]
1056+ }
1057+ if(group=="[Channel2]")
1058+ {
1059+ engine.scratchEnable(2, 250, 50, alpha, beta);
1060+ DJCRMX2.scratching[2] = true; //[DEP]
1061+ }
1062+
1063+ }
1064+ if (value == 0x00 ) // If the jog wheel is released:
1065+ {
1066+ if(group=="[Channel1]")
1067+ {
1068+ DJCRMX2.scratching[1] = false; // <- v1.10.x and below
1069+ engine.scratchDisable(1);
1070+ }
1071+ if(group=="[Channel2]")
1072+ {
1073+ DJCRMX2.scratching[2] = false; // <- v1.10.x and below
1074+ engine.scratchDisable(2);
1075+ }
1076+ }
1077+ }
1078+ else //Default setting where button is not held down.
1079+ {
1080+ DJCRMX2.scratching[1] = false; // Only for v1.10.x and below
1081+ DJCRMX2.scratching[2] = false; // Only for v1.10.x and below
1082+ engine.scratchDisable(1);
1083+ engine.scratchDisable(2);
1084+ }
1085+ return;
1086+}
1087+
1088+/* [ Function wheelTurn ] - Version 0.1.2
1089+ * Pays attention to the current deck, checks scratching, affects the
1090+ * song accordingly.
1091+*/
1092+DJCRMX2.wheelTurn = function (channel, control, value, status, group){
1093+ var newValue=0;
1094+ // Spinning backwards = 127 or less (less meaning faster)
1095+ // Spinning forwards = 1 or more (more meaning faster)
1096+ if (value-64 > 0) newValue = (value-128);
1097+ else newValue=value;
1098+ //if (!engine.isScratching(DJCRMX2.currentDeck)) // [FUT]
1099+ if(group=="[Channel1]")
1100+ {
1101+ if(DJCRMX2.scratching[1]==true) {engine.scratchTick(1,newValue);return;}
1102+ }
1103+ else if(group=="[Channel2]")
1104+ {
1105+ if(DJCRMX2.scratching[2]==true) {engine.scratchTick(2,newValue);return;}
1106+ }
1107+ engine.setValue(group, "jog", newValue);
1108+ return;
1109+}
1110+
1111+DJCRMX2.micSwitch = function (channel, control, value, status) //???
1112+{
1113+ if(status == 0x90 && control == 0x48 && value == 0x7F)
1114+ {
1115+ engine.setValue("[Microphone]","enabled",1);
1116+ engine.setValue("[Microphone]","talkover",1);
1117+ }
1118+ if(status == 0x90 && control == 0x48 && value == 0x00)
1119+ {
1120+ engine.setValue("[Microphone]","enabled",0);
1121+ engine.setValue("[Microphone]","talkover",0);
1122+ }
1123+}
1124+
1125+/* [ Function shutdown ] - Version 0.1.3
1126+ * Sets variables down for shutoff.
1127+*/
1128+DJCRMX2.shutdown = function(id){
1129+ DJCRMX2.scratching[1]=false;
1130+ DJCRMX2.scratching[2]=false;
1131+ engine.setValue("[Microphone]","enabled",0);
1132+ engine.setValue("[Microphone]","talkover",0);
1133+}
1134
1135=== added directory 'mixxx/src/control'
1136=== added file 'mixxx/src/control/control.cpp'
1137--- mixxx/src/control/control.cpp 1970-01-01 00:00:00 +0000
1138+++ mixxx/src/control/control.cpp 2013-05-17 16:35:30 +0000
1139@@ -0,0 +1,132 @@
1140+#include <QtDebug>
1141+#include <QMutexLocker>
1142+
1143+#include "control/control.h"
1144+
1145+#include "util/stat.h"
1146+#include "util/timer.h"
1147+
1148+// Static member variable definition
1149+QHash<ConfigKey, ControlDoublePrivate*> ControlDoublePrivate::m_sqCOHash;
1150+QMutex ControlDoublePrivate::m_sqCOHashMutex;
1151+
1152+ControlDoublePrivate::ControlDoublePrivate()
1153+ : m_bIgnoreNops(true),
1154+ m_bTrack(false) {
1155+ m_defaultValue.setValue(0);
1156+ m_value.setValue(0);
1157+}
1158+
1159+ControlDoublePrivate::ControlDoublePrivate(ConfigKey key,
1160+ bool bIgnoreNops, bool bTrack)
1161+ : m_key(key),
1162+ m_bIgnoreNops(bIgnoreNops),
1163+ m_bTrack(bTrack),
1164+ m_trackKey("control " + m_key.group + "," + m_key.item),
1165+ m_trackType(Stat::UNSPECIFIED),
1166+ m_trackFlags(Stat::COUNT | Stat::SUM | Stat::AVERAGE |
1167+ Stat::SAMPLE_VARIANCE | Stat::MIN | Stat::MAX) {
1168+ m_defaultValue.setValue(0);
1169+ m_value.setValue(0);
1170+
1171+ m_sqCOHashMutex.lock();
1172+ m_sqCOHash.insert(m_key, this);
1173+ m_sqCOHashMutex.unlock();
1174+
1175+ if (m_bTrack) {
1176+ // TODO(rryan): Make configurable.
1177+ Stat::track(m_trackKey, static_cast<Stat::StatType>(m_trackType),
1178+ static_cast<Stat::ComputeFlags>(m_trackFlags),
1179+ m_value.getValue());
1180+ }
1181+}
1182+
1183+ControlDoublePrivate::~ControlDoublePrivate() {
1184+ m_sqCOHashMutex.lock();
1185+ m_sqCOHash.remove(m_key);
1186+ m_sqCOHashMutex.unlock();
1187+}
1188+
1189+// static
1190+ControlDoublePrivate* ControlDoublePrivate::getControl(
1191+ const ConfigKey& key, bool bCreate, bool bIgnoreNops, bool bTrack) {
1192+ QMutexLocker locker(&m_sqCOHashMutex);
1193+ QHash<ConfigKey, ControlDoublePrivate*>::const_iterator it = m_sqCOHash.find(key);
1194+ if (it != m_sqCOHash.end()) {
1195+ return it.value();
1196+ }
1197+ locker.unlock();
1198+
1199+ ControlDoublePrivate* pControl = NULL;
1200+ if (bCreate) {
1201+ pControl = new ControlDoublePrivate(key, bIgnoreNops, bTrack);
1202+ locker.relock();
1203+ m_sqCOHash.insert(key, pControl);
1204+ locker.unlock();
1205+ }
1206+
1207+ if (pControl == NULL) {
1208+ qWarning() << "ControlDoublePrivate::getControl returning NULL for ("
1209+ << key.group << "," << key.item << ")";
1210+ }
1211+
1212+ return pControl;
1213+}
1214+
1215+double ControlDoublePrivate::get() const {
1216+ return m_value.getValue();
1217+}
1218+
1219+void ControlDoublePrivate::reset(QObject* pSender) {
1220+ double defaultValue = m_defaultValue.getValue();
1221+ set(defaultValue, pSender);
1222+}
1223+
1224+void ControlDoublePrivate::set(const double& value, QObject* pSender) {
1225+ if (m_bIgnoreNops && get() == value) {
1226+ return;
1227+ }
1228+
1229+ double dValue = value;
1230+ // The behavior says to ignore the set, ignore it.
1231+ ControlNumericBehavior* pBehavior = m_pBehavior;
1232+ if (pBehavior && !pBehavior->setFilter(&dValue)) {
1233+ return;
1234+ }
1235+ m_value.setValue(dValue);
1236+ emit(valueChanged(dValue, pSender));
1237+
1238+ if (m_bTrack) {
1239+ Stat::track(m_trackKey, static_cast<Stat::StatType>(m_trackType),
1240+ static_cast<Stat::ComputeFlags>(m_trackFlags), dValue);
1241+ }
1242+}
1243+
1244+ControlNumericBehavior* ControlDoublePrivate::setBehavior(ControlNumericBehavior* pBehavior) {
1245+ return m_pBehavior.fetchAndStoreRelaxed(pBehavior);
1246+}
1247+
1248+void ControlDoublePrivate::setWidgetParameter(double dParam, QObject* pSetter) {
1249+ ControlNumericBehavior* pBehavior = m_pBehavior;
1250+ set(pBehavior ? pBehavior->widgetParameterToValue(dParam) : dParam, pSetter);
1251+}
1252+
1253+double ControlDoublePrivate::getWidgetParameter() const {
1254+ ControlNumericBehavior* pBehavior = m_pBehavior;
1255+ return pBehavior ? pBehavior->valueToWidgetParameter(get()) : get();
1256+}
1257+
1258+void ControlDoublePrivate::setMidiParameter(MidiOpCode opcode, double dParam) {
1259+ ControlNumericBehavior* pBehavior = m_pBehavior;
1260+ if (pBehavior) {
1261+ pBehavior->setValueFromMidiParameter(opcode, dParam, this);
1262+ } else {
1263+ set(dParam, NULL);
1264+ }
1265+}
1266+
1267+double ControlDoublePrivate::getMidiParameter() const {
1268+ ControlNumericBehavior* pBehavior = m_pBehavior;
1269+ return pBehavior ? pBehavior->valueToMidiParameter(get()) : get();
1270+}
1271+
1272
1273=== added file 'mixxx/src/control/control.h'
1274--- mixxx/src/control/control.h 1970-01-01 00:00:00 +0000
1275+++ mixxx/src/control/control.h 2013-05-17 16:35:30 +0000
1276@@ -0,0 +1,95 @@
1277+#ifndef CONTROL_H
1278+#define CONTROL_H
1279+
1280+#include <QHash>
1281+#include <QMutex>
1282+#include <QString>
1283+#include <QObject>
1284+#include <QAtomicPointer>
1285+
1286+#include "control/controlbehavior.h"
1287+#include "control/controlvalue.h"
1288+#include "configobject.h"
1289+
1290+class ControlDoublePrivate : public QObject {
1291+ Q_OBJECT
1292+ public:
1293+ ControlDoublePrivate();
1294+ ControlDoublePrivate(ConfigKey key, bool bIgnoreNops, bool bTrack);
1295+ virtual ~ControlDoublePrivate();
1296+
1297+ // Gets the ControlDoublePrivate matching the given ConfigKey. If bCreate
1298+ // is true, allocates a new ControlDoublePrivate for the ConfigKey if one
1299+ // does not exist.
1300+ static ControlDoublePrivate* getControl(
1301+ const ConfigKey& key,
1302+ bool bCreate, bool bIgnoreNops=true, bool bTrack=false);
1303+ static inline ControlDoublePrivate* getControl(
1304+ const QString& group, const QString& item,
1305+ bool bCreate, bool bIgnoreNops=true, bool bTrack=false) {
1306+ ConfigKey key(group, item);
1307+ return getControl(key, bCreate, bIgnoreNops, bTrack);
1308+ }
1309+
1310+ // Sets the control value.
1311+ void set(const double& value, QObject* pSetter);
1312+ // Gets the control value.
1313+ double get() const;
1314+ // Resets the control value to its default.
1315+ void reset(QObject* pSetter);
1316+
1317+ // Set the behavior to be used when setting values and translating between
1318+ // parameter and value space. Returns the previously set behavior (if any).
1319+ // Caller must handle appropriate destruction of the previous behavior or
1320+ // memory will leak.
1321+ ControlNumericBehavior* setBehavior(ControlNumericBehavior* pBehavior);
1322+
1323+ void setWidgetParameter(double dParam, QObject* pSetter);
1324+ double getWidgetParameter() const;
1325+
1326+ void setMidiParameter(MidiOpCode opcode, double dParam);
1327+ double getMidiParameter() const;
1328+
1329+ inline bool ignoreNops() const {
1330+ return m_bIgnoreNops;
1331+ }
1332+
1333+ inline void setDefaultValue(double dValue) {
1334+ m_defaultValue.setValue(dValue);
1335+ }
1336+ inline double defaultValue() const {
1337+ double default_value = m_defaultValue.getValue();
1338+ return m_pBehavior ? m_pBehavior->defaultValue(default_value) : default_value;
1339+ }
1340+
1341+ signals:
1342+ // Emitted when the ControlDoublePrivate value changes. pSetter is a
1343+ // pointer to the setter of the value (potentially NULL).
1344+ void valueChanged(double value, QObject* pSetter);
1345+
1346+ private:
1347+ ConfigKey m_key;
1348+ // Whether to ignore sets which would have no effect.
1349+ bool m_bIgnoreNops;
1350+
1351+ // Whether to track value changes with the stats framework.
1352+ bool m_bTrack;
1353+ QString m_trackKey;
1354+ int m_trackType;
1355+ int m_trackFlags;
1356+
1357+ // The control value.
1358+ ControlValueAtomic<double> m_value;
1359+ // The default control value.
1360+ ControlValueAtomic<double> m_defaultValue;
1361+
1362+ QAtomicPointer<ControlNumericBehavior> m_pBehavior;
1363+
1364+ // Hash of ControlDoublePrivate instantiations.
1365+ static QHash<ConfigKey,ControlDoublePrivate*> m_sqCOHash;
1366+ // Mutex guarding access to the ControlDoublePrivate hash.
1367+ static QMutex m_sqCOHashMutex;
1368+};
1369+
1370+
1371+#endif /* CONTROL_H */
1372
1373=== added file 'mixxx/src/control/controlbehavior.cpp'
1374--- mixxx/src/control/controlbehavior.cpp 1970-01-01 00:00:00 +0000
1375+++ mixxx/src/control/controlbehavior.cpp 2013-05-17 16:35:30 +0000
1376@@ -0,0 +1,194 @@
1377+#include "control/controlbehavior.h"
1378+#include "control/control.h"
1379+
1380+bool ControlNumericBehavior::setFilter(double* dValue) {
1381+ Q_UNUSED(dValue);
1382+ return true;
1383+}
1384+
1385+double ControlNumericBehavior::defaultValue(double dDefault) const {
1386+ return dDefault;
1387+}
1388+
1389+double ControlNumericBehavior::valueToWidgetParameter(double dValue) {
1390+ return dValue;
1391+}
1392+
1393+double ControlNumericBehavior::widgetParameterToValue(double dParam) {
1394+ return dParam;
1395+}
1396+
1397+double ControlNumericBehavior::valueToMidiParameter(double dValue) {
1398+ return dValue;
1399+}
1400+
1401+void ControlNumericBehavior::setValueFromMidiParameter(MidiOpCode o, double dParam,
1402+ ControlDoublePrivate* pControl) {
1403+ Q_UNUSED(o);
1404+ pControl->set(dParam, NULL);
1405+}
1406+
1407+ControlPotmeterBehavior::ControlPotmeterBehavior(double dMinValue, double dMaxValue)
1408+ : m_dMinValue(dMinValue),
1409+ m_dMaxValue(dMaxValue),
1410+ m_dValueRange(m_dMaxValue - m_dMinValue),
1411+ m_dDefaultValue(m_dMinValue + 0.5 * m_dValueRange) {
1412+}
1413+
1414+ControlPotmeterBehavior::~ControlPotmeterBehavior() {
1415+}
1416+
1417+bool ControlPotmeterBehavior::setFilter(double* dValue) {
1418+ if (*dValue > m_dMaxValue) {
1419+ *dValue = m_dMaxValue;
1420+ } else if (*dValue < m_dMinValue) {
1421+ *dValue = m_dMinValue;
1422+ }
1423+ return true;
1424+}
1425+
1426+double ControlPotmeterBehavior::defaultValue(double dDefault) const {
1427+ Q_UNUSED(dDefault);
1428+ return m_dDefaultValue;
1429+}
1430+
1431+double ControlPotmeterBehavior::valueToWidgetParameter(double dValue) {
1432+ double dNorm = (dValue - m_dMinValue) / m_dValueRange;
1433+ return dNorm < 0.5 ? dNorm * 128.0 : dNorm * 126.0 + 1.0;
1434+}
1435+
1436+double ControlPotmeterBehavior::widgetParameterToValue(double dParam) {
1437+ double dNorm = dParam < 64 ? dParam / 128.0 : (dParam - 1.0) / 126.0;
1438+ return m_dMinValue + dNorm * m_dValueRange;
1439+}
1440+
1441+double ControlPotmeterBehavior::valueToMidiParameter(double dValue) {
1442+ return valueToWidgetParameter(dValue);
1443+}
1444+
1445+void ControlPotmeterBehavior::setValueFromMidiParameter(MidiOpCode o, double dParam,
1446+ ControlDoublePrivate* pControl) {
1447+ Q_UNUSED(o);
1448+ pControl->set(widgetParameterToValue(dParam), NULL);
1449+}
1450+
1451+#define maxPosition 127
1452+#define minPosition 0
1453+#define middlePosition ((maxPosition-minPosition)/2)
1454+#define positionrange (maxPosition-minPosition)
1455+
1456+ControlLogpotmeterBehavior::ControlLogpotmeterBehavior(double dMaxValue)
1457+ : ControlPotmeterBehavior(0, dMaxValue) {
1458+ m_dB1 = log10(2.0) / middlePosition;
1459+ m_dB2 = log10(dMaxValue) / (maxPosition - middlePosition);
1460+}
1461+
1462+ControlLogpotmeterBehavior::~ControlLogpotmeterBehavior() {
1463+}
1464+
1465+double ControlLogpotmeterBehavior::defaultValue(double dDefault) {
1466+ Q_UNUSED(dDefault);
1467+ return 1.0;
1468+}
1469+
1470+double ControlLogpotmeterBehavior::valueToWidgetParameter(double dValue) {
1471+ if (dValue > 1.0) {
1472+ return log10(dValue) / m_dB2 + middlePosition;
1473+ } else {
1474+ return log10(dValue + 1.0) / m_dB1;
1475+ }
1476+}
1477+
1478+double ControlLogpotmeterBehavior::widgetParameterToValue(double dParam) {
1479+ if (dParam <= middlePosition) {
1480+ return pow(10.0, m_dB1 * dParam) - 1;
1481+ } else {
1482+ return pow(10.0, m_dB2 * (dParam - middlePosition));
1483+ }
1484+}
1485+
1486+ControlLinPotmeterBehavior::ControlLinPotmeterBehavior(double dMinValue, double dMaxValue)
1487+ : ControlPotmeterBehavior(dMinValue, dMaxValue) {
1488+}
1489+
1490+ControlLinPotmeterBehavior::~ControlLinPotmeterBehavior() {
1491+}
1492+
1493+double ControlLinPotmeterBehavior::valueToWidgetParameter(double dValue) {
1494+ double dNorm = (dValue - m_dMinValue) / m_dValueRange;
1495+ return math_min(dNorm * 128, 127);
1496+}
1497+
1498+double ControlLinPotmeterBehavior::widgetParameterToValue(double dParam) {
1499+ double dNorm = dParam / 128.0;
1500+ return m_dMinValue + dNorm * m_dValueRange;
1501+}
1502+
1503+double ControlTTRotaryBehavior::valueToWidgetParameter(double dValue) {
1504+ return dValue * 200.0 + 64;
1505+}
1506+
1507+double ControlTTRotaryBehavior::widgetParameterToValue(double dParam) {
1508+ // Non-linear scaling
1509+ double temp = ((dParam - 64.0) * (dParam - 64.0)) / 64.0;
1510+ if (dParam - 64 < 0) {
1511+ temp = -temp;
1512+ }
1513+ return temp;
1514+}
1515+
1516+// static
1517+const int ControlPushButtonBehavior::kPowerWindowTimeMillis = 300;
1518+
1519+ControlPushButtonBehavior::ControlPushButtonBehavior(ButtonMode buttonMode,
1520+ int iNumStates)
1521+ : m_buttonMode(buttonMode),
1522+ m_iNumStates(iNumStates) {
1523+}
1524+
1525+void ControlPushButtonBehavior::setValueFromMidiParameter(
1526+ MidiOpCode o, double dParam, ControlDoublePrivate* pControl) {
1527+ // This block makes push-buttons act as power window buttons.
1528+ if (m_buttonMode == POWERWINDOW && m_iNumStates == 2) {
1529+ if (o == MIDI_NOTE_ON) {
1530+ if (dParam > 0.) {
1531+ double value = pControl->get();
1532+ pControl->set(!value, NULL);
1533+ m_pushTimer.setSingleShot(true);
1534+ m_pushTimer.start(kPowerWindowTimeMillis);
1535+ }
1536+ } else if (o == MIDI_NOTE_OFF) {
1537+ if (!m_pushTimer.isActive()) {
1538+ pControl->set(0.0, NULL);
1539+ }
1540+ }
1541+ } else if (m_buttonMode == TOGGLE) {
1542+ // This block makes push-buttons act as toggle buttons.
1543+ if (m_iNumStates > 2) { //multistate button
1544+ if (dParam > 0.) { //looking for NOTE_ON doesn't seem to work...
1545+ double value = pControl->get();
1546+ value++;
1547+ if (value >= m_iNumStates) {
1548+ pControl->set(0, NULL);
1549+ } else {
1550+ pControl->set(value, NULL);
1551+ }
1552+ }
1553+ } else {
1554+ if (o == MIDI_NOTE_ON) {
1555+ if (dParam > 0.) {
1556+ double value = pControl->get();
1557+ pControl->set(!value, NULL);
1558+ }
1559+ }
1560+ }
1561+ } else { //Not a toggle button (trigger only when button pushed)
1562+ if (o == MIDI_NOTE_ON) {
1563+ double value = pControl->get();
1564+ pControl->set(!value, NULL);
1565+ } else if (o == MIDI_NOTE_OFF) {
1566+ pControl->set(0.0, NULL);
1567+ }
1568+ }
1569+}
1570+
1571
1572=== added file 'mixxx/src/control/controlbehavior.h'
1573--- mixxx/src/control/controlbehavior.h 1970-01-01 00:00:00 +0000
1574+++ mixxx/src/control/controlbehavior.h 2013-05-17 16:35:30 +0000
1575@@ -0,0 +1,97 @@
1576+#ifndef CONTROLBEHAVIOR_H
1577+#define CONTROLBEHAVIOR_H
1578+
1579+#include <math.h>
1580+
1581+#include <QTimer>
1582+
1583+#include "controllers/midi/midimessage.h"
1584+#include "mathstuff.h"
1585+
1586+class ControlDoublePrivate;
1587+
1588+class ControlNumericBehavior {
1589+ public:
1590+ // Returns true if the set should occur. Mutates dValue if the value should
1591+ // be changed.
1592+ virtual bool setFilter(double* dValue);
1593+
1594+ virtual double defaultValue(double dDefault) const;
1595+ virtual double valueToWidgetParameter(double dValue);
1596+ virtual double widgetParameterToValue(double dParam);
1597+ virtual double valueToMidiParameter(double dValue);
1598+ virtual void setValueFromMidiParameter(MidiOpCode o, double dParam,
1599+ ControlDoublePrivate* pControl);
1600+};
1601+
1602+class ControlPotmeterBehavior : public ControlNumericBehavior {
1603+ public:
1604+ ControlPotmeterBehavior(double dMinValue, double dMaxValue);
1605+ virtual ~ControlPotmeterBehavior();
1606+
1607+ virtual bool setFilter(double* dValue);
1608+ virtual double defaultValue(double dDefault) const;
1609+ virtual double valueToWidgetParameter(double dValue);
1610+ virtual double widgetParameterToValue(double dParam);
1611+ virtual double valueToMidiParameter(double dValue);
1612+ virtual void setValueFromMidiParameter(MidiOpCode o, double dParam,
1613+ ControlDoublePrivate* pControl);
1614+
1615+ protected:
1616+ double m_dMinValue;
1617+ double m_dMaxValue;
1618+ double m_dValueRange;
1619+ double m_dDefaultValue;
1620+};
1621+
1622+class ControlLogpotmeterBehavior : public ControlPotmeterBehavior {
1623+ public:
1624+ ControlLogpotmeterBehavior(double dMaxValue);
1625+ virtual ~ControlLogpotmeterBehavior();
1626+
1627+ virtual double defaultValue(double dDefault);
1628+ virtual double valueToWidgetParameter(double dValue);
1629+ virtual double widgetParameterToValue(double dParam);
1630+
1631+ protected:
1632+ double m_dB1, m_dB2;
1633+};
1634+
1635+class ControlLinPotmeterBehavior : public ControlPotmeterBehavior {
1636+ public:
1637+ ControlLinPotmeterBehavior(double dMinValue, double dMaxValue);
1638+ virtual ~ControlLinPotmeterBehavior();
1639+
1640+ virtual double valueToWidgetParameter(double dValue);
1641+ virtual double widgetParameterToValue(double dParam);
1642+};
1643+
1644+class ControlTTRotaryBehavior : public ControlNumericBehavior {
1645+ public:
1646+ virtual double valueToWidgetParameter(double dValue);
1647+ virtual double widgetParameterToValue(double dParam);
1648+};
1649+
1650+class ControlPushButtonBehavior : public ControlNumericBehavior {
1651+ public:
1652+ static const int kPowerWindowTimeMillis;
1653+
1654+ // TODO(XXX) Duplicated from ControlPushButton. It's complicated and
1655+ // annoying to share them so I just copied them.
1656+ enum ButtonMode {
1657+ PUSH = 0,
1658+ TOGGLE,
1659+ POWERWINDOW
1660+ };
1661+
1662+ ControlPushButtonBehavior(ButtonMode buttonMode, int iNumStates);
1663+ virtual void setValueFromMidiParameter(MidiOpCode o, double dParam,
1664+ ControlDoublePrivate* pControl);
1665+
1666+ private:
1667+ ButtonMode m_buttonMode;
1668+ int m_iNumStates;
1669+ QTimer m_pushTimer;
1670+};
1671+
1672+#endif /* CONTROLBEHAVIOR_H */
1673
1674=== added file 'mixxx/src/control/controlvalue.h'
1675--- mixxx/src/control/controlvalue.h 1970-01-01 00:00:00 +0000
1676+++ mixxx/src/control/controlvalue.h 2013-05-17 16:35:30 +0000
1677@@ -0,0 +1,157 @@
1678+#ifndef CONTROLVALUE_H
1679+#define CONTROLVALUE_H
1680+
1681+#include <limits>
1682+
1683+#include <QAtomicInt>
1684+#include <QObject>
1685+
1686+// for look free access, this value has to be >= the number of value using threads
1687+// value must be a fraction of an integer
1688+const int cRingSize = 8;
1689+// there are basicly unlimited readers allowed at each ring element
1690+// but we have to count them so max() is just fine.
1691+const int cReaderSlotCnt = std::numeric_limits<int>::max();
1692+
1693+// A single instance of a value of type T along with an atomic integer which
1694+// tracks the current number of readers or writers of the slot. The value
1695+// m_readerSlots starts at cReaderSlotCnt and counts down to 0. If the value is
1696+// 0 or less then reads to the value fail because there are either too many
1697+// readers or a write is occurring. A write to the value will fail if
1698+// m_readerSlots is not equal to cReaderSlotCnt (e.g. there is an active
1699+// reader).
1700+template<typename T>
1701+class ControlRingValue {
1702+ public:
1703+ ControlRingValue()
1704+ : m_value(T()),
1705+ m_readerSlots(cReaderSlotCnt) {
1706+ }
1707+
1708+ bool tryGet(T* value) const {
1709+ // Read while consuming one readerSlot
1710+ bool hasSlot = (m_readerSlots.fetchAndAddAcquire(-1) > 0);
1711+ if (hasSlot) {
1712+ *value = m_value;
1713+ }
1714+ (void)m_readerSlots.fetchAndAddRelease(1);
1715+ return hasSlot;
1716+ }
1717+
1718+ bool trySet(const T& value) {
1719+ // try to lock this element entirely for reading
1720+ if (m_readerSlots.testAndSetAcquire(cReaderSlotCnt, 0)) {
1721+ m_value = value;
1722+ m_readerSlots.fetchAndAddRelease(cReaderSlotCnt);
1723+ return true;
1724+ }
1725+ return false;
1726+ }
1727+
1728+ private:
1729+ T m_value;
1730+ mutable QAtomicInt m_readerSlots;
1731+};
1732+
1733+// Ring buffer based implementation for all Types sizeof(T) > sizeof(void*)
1734+
1735+// An implementation of ControlValueAtomicBase for non-atomic types T. Uses a
1736+// ring-buffer of ControlRingValues and a read pointer and write pointer to
1737+// provide getValue()/setValue() methods which *sacrifice perfect consistency*
1738+// for the benefit of wait-free read/write access to a value.
1739+template<typename T, bool ATOMIC = false>
1740+class ControlValueAtomicBase {
1741+ public:
1742+ inline T getValue() const {
1743+ T value = T();
1744+ unsigned int index = (unsigned int)m_readIndex
1745+ % (cRingSize);
1746+ while (m_ring[index].tryGet(&value) == false) {
1747+ // We are here if
1748+ // 1) there are more then cReaderSlotCnt reader (get) reading the same value or
1749+ // 2) the formerly current value is locked by a writer
1750+ // Case 1 does not happen because we have enough (0x7fffffff) reader slots.
1751+ // Case 2 happens when the a reader is delayed after reading the
1752+ // m_currentIndex and in the mean while a reader locks the formaly current value
1753+ // because it has written cRingSize times. Reading the less recent value will fix
1754+ // it because it is now actualy the current value.
1755+ index = (index - 1) % (cRingSize);
1756+ }
1757+ return value;
1758+ }
1759+
1760+ inline void setValue(const T& value) {
1761+ // Test if we can read atomic
1762+ // This test is const and will be mad only at compile time
1763+ unsigned int index;
1764+ do {
1765+ index = (unsigned int)m_writeIndex.fetchAndAddAcquire(1)
1766+ % (cRingSize);
1767+ // This will be repeated if the value is locked
1768+ // 1) by an other writer writing at the same time or
1769+ // 2) a delayed reader is still blocking the formerly current value
1770+ // In both cases writing to the next value will fix it.
1771+ } while (!m_ring[index].trySet(value));
1772+ m_readIndex = (int)index;
1773+ }
1774+
1775+ protected:
1776+ ControlValueAtomicBase()
1777+ : m_readIndex(0),
1778+ m_writeIndex(1) {
1779+ Q_ASSERT((std::numeric_limits<unsigned int>::max() % cRingSize) == (cRingSize - 1));
1780+ }
1781+
1782+ private:
1783+ // In worst case, each reader can consume a reader slot from a different ring element.
1784+ // In this case there is still one ring element available for writing.
1785+ ControlRingValue<T> m_ring[cRingSize];
1786+ QAtomicInt m_readIndex;
1787+ QAtomicInt m_writeIndex;
1788+};
1789+
1790+// Specialized template for types that are deemed to be atomic on the target
1791+// architecture. Instead of using a read/write ring to guarantee atomicity,
1792+// direct assignment/read of an aligned member variable is used.
1793+template<typename T>
1794+class ControlValueAtomicBase<T, true> {
1795+ public:
1796+ inline T getValue() const {
1797+ return m_value;
1798+ }
1799+
1800+ inline void setValue(const T& value) {
1801+ m_value = value;
1802+ }
1803+
1804+ protected:
1805+ ControlValueAtomicBase()
1806+ : m_value(T()) {
1807+ }
1808+
1809+ private:
1810+#if defined(__GNUC__)
1811+ T m_value __attribute__ ((aligned(sizeof(void*))));
1812+#elif defined(_MSC_VER)
1813+ T __declspec(align(sizeof(void*))) m_value;
1814+#else
1815+ T m_value;
1816+#endif
1817+};
1818+
1819+// ControlValueAtomic is a wrapper around ControlValueAtomicBase which uses the
1820+// sizeof(T) to determine which underlying implementation of
1821+// ControlValueAtomicBase to use. For types where sizeof(T) <= sizeof(void*),
1822+// the specialized implementation of ControlValueAtomicBase for types that are
1823+// atomic on the architecture is used.
1824+template<typename T>
1825+class ControlValueAtomic
1826+ : public ControlValueAtomicBase<T, sizeof(T) <= sizeof(void*)> {
1827+ public:
1828+
1829+ ControlValueAtomic()
1830+ : ControlValueAtomicBase<T, sizeof(T) <= sizeof(void*)>() {
1831+ }
1832+};
1833+
1834+#endif /* CONTROLVALUE_H */
1835
1836=== removed file 'mixxx/src/controlbeat.cpp'
1837--- mixxx/src/controlbeat.cpp 2012-11-20 00:40:18 +0000
1838+++ mixxx/src/controlbeat.cpp 1970-01-01 00:00:00 +0000
1839@@ -1,85 +0,0 @@
1840-/***************************************************************************
1841- controlbeat.cpp - description
1842- -------------------
1843- begin : Mon Apr 7 2003
1844- copyright : (C) 2003 by Tue & Ken Haste Andersen
1845- email : haste@diku.dk
1846-***************************************************************************/
1847-
1848-/***************************************************************************
1849-* *
1850-* This program is free software; you can redistribute it and/or modify *
1851-* it under the terms of the GNU General Public License as published by *
1852-* the Free Software Foundation; either version 2 of the License, or *
1853-* (at your option) any later version. *
1854-* *
1855-***************************************************************************/
1856-
1857-#include "controlbeat.h"
1858-
1859-ControlBeat::ControlBeat(ConfigKey key, bool bMidiSimulateLatching) : ControlObject(key)
1860-{
1861- m_bMidiSimulateLatching = bMidiSimulateLatching;
1862- m_dValue = 0.;
1863- time.start();
1864- m_bPressed = false;
1865- m_iValidPresses = 0;
1866-
1867- // Filter buffer
1868- buffer = new CSAMPLE[filterLength];
1869- for (int i=0; i<filterLength; i++)
1870- buffer[i] = 0.;
1871-}
1872-
1873-ControlBeat::~ControlBeat()
1874-{
1875- delete [] buffer;
1876-}
1877-
1878-void ControlBeat::setValueFromMidi(MidiOpCode o, double v)
1879-{
1880- Q_UNUSED(o);
1881- Q_UNUSED(v);
1882- if (!m_bPressed || !m_bMidiSimulateLatching)
1883- {
1884- beatTap();
1885- m_bPressed = true;
1886- }
1887- else
1888- m_bPressed = false;
1889-}
1890-
1891-void ControlBeat::setValueFromThread(double dValue) {
1892- if (dValue > 0) {
1893- beatTap();
1894- }
1895-}
1896-
1897-void ControlBeat::beatTap()
1898-{
1899- int elapsed = time.restart();
1900-
1901- if (elapsed <= maxInterval) {
1902- // Move back in filter one sample
1903- for (int i = filterLength-1; i > 0; i--)
1904- buffer[i] = buffer[i-1];
1905-
1906- buffer[0] = 1000.*(60./elapsed);
1907- if (buffer[0] > maxBPM)
1908- buffer[0] = maxBPM;
1909-
1910- m_iValidPresses++;
1911- if (m_iValidPresses > filterLength)
1912- m_iValidPresses = filterLength;
1913-
1914- double temp = 0.;
1915- for (int i = 0; i < m_iValidPresses; ++i)
1916- temp += buffer[i];
1917- temp /= m_iValidPresses;
1918- m_dValue = temp;
1919-
1920- emit(valueChanged(m_dValue));
1921- } else {
1922- m_iValidPresses = 0;
1923- }
1924-}
1925
1926=== removed file 'mixxx/src/controlbeat.h'
1927--- mixxx/src/controlbeat.h 2012-03-17 17:42:44 +0000
1928+++ mixxx/src/controlbeat.h 1970-01-01 00:00:00 +0000
1929@@ -1,59 +0,0 @@
1930-/***************************************************************************
1931- controlbeat.h - description
1932- -------------------
1933- begin : Mon Apr 7 2003
1934- copyright : (C) 2003 by Tue & Ken Haste Andersen
1935- email : haste@diku.dk
1936- ***************************************************************************/
1937-
1938-/***************************************************************************
1939- * *
1940- * This program is free software; you can redistribute it and/or modify *
1941- * it under the terms of the GNU General Public License as published by *
1942- * the Free Software Foundation; either version 2 of the License, or *
1943- * (at your option) any later version. *
1944- * *
1945- ***************************************************************************/
1946-
1947-#ifndef CONTROLBEAT_H
1948-#define CONTROLBEAT_H
1949-
1950-#include "controlobject.h"
1951-#include "configobject.h"
1952-#include "defs.h"
1953-#include <qdatetime.h>
1954-
1955-/**
1956- * Takes impulses as input, and convert it to a BPM measure.
1957- *
1958- *@author Tue & Ken Haste Andersen
1959- */
1960-
1961-/** Minimum allowed Beat per minute (BPM) */
1962-const int minBPM = 30;
1963-/** Maximum allowed bpm */
1964-const int maxBPM = 240;
1965-/** Maximum allowed interval between beats in milli seconds (calculated from minBPM) */
1966-const int maxInterval = (int)(1000.*(60./(CSAMPLE)minBPM));
1967-/** Filter length */
1968-const int filterLength = 5;
1969-
1970-class ControlBeat : public ControlObject {
1971- public:
1972- ControlBeat(ConfigKey key, bool bMidiSimulateLatching=false);
1973- virtual ~ControlBeat();
1974-
1975- protected:
1976- void setValueFromMidi(MidiOpCode o, double v);
1977- void setValueFromThread(double dValue);
1978- private:
1979- void beatTap();
1980-
1981- QTime time;
1982- CSAMPLE *buffer;
1983- bool m_bMidiSimulateLatching;
1984- bool m_bPressed;
1985- int m_iValidPresses;
1986-};
1987-
1988-#endif
1989
1990=== modified file 'mixxx/src/controllers/controller.h'
1991--- mixxx/src/controllers/controller.h 2013-02-09 05:21:42 +0000
1992+++ mixxx/src/controllers/controller.h 2013-05-17 16:35:30 +0000
1993@@ -72,8 +72,6 @@
1994 // Emitted when a new preset is loaded. pPreset is a /clone/ of the loaded
1995 // preset, not a pointer to the preset itself.
1996 void presetLoaded(ControllerPresetPointer pPreset);
1997- // Emitted when the MIDI controller requests that the control system sync.
1998- void syncControlSystem();
1999
2000 // Making these slots protected/private ensures that other parts of Mixxx can
2001 // only signal them which allows us to use no locks.
2002
2003=== modified file 'mixxx/src/controllers/controllerengine.cpp'
2004--- mixxx/src/controllers/controllerengine.cpp 2013-04-27 22:06:33 +0000
2005+++ mixxx/src/controllers/controllerengine.cpp 2013-05-17 16:35:30 +0000
2006@@ -640,11 +640,11 @@
2007
2008 ControlObjectThread *cot = getControlObjectThread(group, name);
2009
2010- if (cot != NULL && !m_st.ignore(cot->getControlObject(), newValue)) {
2011- cot->slotSet(newValue);
2012- // We call emitValueChanged so that script functions connected to this
2013- // control will get updates.
2014- cot->emitValueChanged();
2015+ if (cot != NULL) {
2016+ ControlObject* pControl = ControlObject::getControl(cot->getKey());
2017+ if (pControl && !m_st.ignore(pControl, newValue)) {
2018+ cot->slotSet(newValue);
2019+ }
2020 }
2021 }
2022
2023@@ -815,12 +815,7 @@
2024 return;
2025 }
2026
2027- ControlObject* pSenderCO = senderCOT->getControlObject();
2028- if (pSenderCO == NULL) {
2029- qWarning() << "ControllerEngine::slotValueChanged() The sender's CO is NULL.";
2030- return;
2031- }
2032- ConfigKey key = pSenderCO->getKey();
2033+ ConfigKey key = senderCOT->getKey();
2034
2035 //qDebug() << "[Controller]: SlotValueChanged" << key.group << key.item;
2036
2037
2038=== modified file 'mixxx/src/controllers/controllermanager.cpp'
2039--- mixxx/src/controllers/controllermanager.cpp 2013-02-09 05:21:42 +0000
2040+++ mixxx/src/controllers/controllermanager.cpp 2013-05-17 16:35:30 +0000
2041@@ -288,8 +288,6 @@
2042 if (pController->isOpen()) {
2043 pController->close();
2044 }
2045- connect(pController, SIGNAL(syncControlSystem()),
2046- this, SIGNAL(syncControlSystem()));
2047 int result = pController->open();
2048 maybeStartOrStopPolling();
2049
2050@@ -308,8 +306,6 @@
2051 if (!pController) {
2052 return;
2053 }
2054- disconnect(pController, SIGNAL(syncControlSystem()),
2055- this, SIGNAL(syncControlSystem()));
2056 pController->close();
2057 maybeStartOrStopPolling();
2058 // Update configuration to reflect controller is disabled.
2059
2060=== modified file 'mixxx/src/controllers/controllermanager.h'
2061--- mixxx/src/controllers/controllermanager.h 2013-02-09 05:21:42 +0000
2062+++ mixxx/src/controllers/controllermanager.h 2013-05-17 16:35:30 +0000
2063@@ -42,7 +42,6 @@
2064 void requestSetUpDevices();
2065 void requestShutdown();
2066 void requestSave(bool onlyActive);
2067- void syncControlSystem();
2068
2069 public slots:
2070 void updateControllerList();
2071
2072=== modified file 'mixxx/src/controllers/midi/midicontroller.cpp'
2073--- mixxx/src/controllers/midi/midicontroller.cpp 2013-05-15 05:27:41 +0000
2074+++ mixxx/src/controllers/midi/midicontroller.cpp 2013-05-17 16:35:30 +0000
2075@@ -312,8 +312,6 @@
2076 return;
2077 }
2078
2079- double currMixxxControlValue = p->GetMidiValue();
2080-
2081 double newValue = value;
2082
2083 //qDebug() << "MIDI Options" << QString::number(options.all, 2).rightJustified(16,'0');
2084@@ -323,14 +321,11 @@
2085 int ivalue;
2086 ivalue = (value << 7) | control;
2087
2088- currMixxxControlValue = p->get();
2089-
2090 // Range is 0x0000..0x3FFF center @ 0x2000, i.e. 0..16383 center @ 8192
2091 if (options.invert) {
2092 newValue = 0x2000-ivalue;
2093 if (newValue < 0) newValue--;
2094- }
2095- else {
2096+ } else {
2097 newValue = ivalue-0x2000;
2098 if (newValue > 0) newValue++;
2099 }
2100@@ -339,6 +334,7 @@
2101
2102 // computeValue not (yet) done on pitch messages because it all assumes 7-bit numbers
2103 } else {
2104+ double currMixxxControlValue = p->getValueToMidi();
2105 newValue = computeValue(options, currMixxxControlValue, value);
2106 }
2107
2108@@ -360,22 +356,15 @@
2109 return;
2110 }
2111 }
2112- p->queueFromThread(newValue);
2113- }
2114- else {
2115+ p->setValueFromThread(newValue, NULL);
2116+ } else {
2117 if (options.soft_takeover) {
2118 if (m_st.ignore(p, newValue, true)) {
2119 return;
2120 }
2121 }
2122- p->queueFromMidi(static_cast<MidiOpCode>(opCode), newValue);
2123+ p->setValueFromMidi(static_cast<MidiOpCode>(opCode), newValue);
2124 }
2125-
2126- // If we got here then we queued a message for the control system. In the
2127- // interest of quickly processing this, we request a sync. Since we are
2128- // running in the controller thread, we broadcast the signal which is
2129- // proxied to the main thread and handled there.
2130- emit(syncControlSystem());
2131 }
2132
2133 double MidiController::computeValue(MidiOptions options, double _prevmidivalue, double _newmidivalue) {
2134
2135=== modified file 'mixxx/src/controllers/midi/midioutputhandler.cpp'
2136--- mixxx/src/controllers/midi/midioutputhandler.cpp 2012-09-26 00:57:08 +0000
2137+++ mixxx/src/controllers/midi/midioutputhandler.cpp 2013-05-17 16:35:30 +0000
2138@@ -6,10 +6,11 @@
2139 *
2140 */
2141
2142+#include <QtDebug>
2143+
2144 #include "controllers/midi/midioutputhandler.h"
2145 #include "controllers/midi/midicontroller.h"
2146-
2147-#include <QDebug>
2148+#include "controlobject.h"
2149
2150 MidiOutputHandler::MidiOutputHandler(QString group, QString key,
2151 MidiController *controller,
2152@@ -25,34 +26,24 @@
2153 m_on(on),
2154 m_off(off),
2155 m_lastVal(0) {
2156+ connect(&m_cobj, SIGNAL(valueChanged(double)),
2157+ this, SLOT(controlChanged(double)));
2158 }
2159
2160 MidiOutputHandler::~MidiOutputHandler() {
2161- if (m_cobj != NULL) {
2162- ConfigKey cKey = m_cobj->getKey();
2163- if (m_pController->debugging()) {
2164- qDebug() << QString("Destroying static MIDI output handler on %1 for %2,%3")
2165- .arg(m_pController->getName(), cKey.group, cKey.item);
2166- }
2167+ ConfigKey cKey = m_cobj.getKey();
2168+ if (m_pController->debugging()) {
2169+ qDebug() << QString("Destroying static MIDI output handler on %1 for %2,%3")
2170+ .arg(m_pController->getName(), cKey.group, cKey.item);
2171 }
2172 }
2173
2174 bool MidiOutputHandler::validate() {
2175- if (m_cobj == NULL) {
2176- return false;
2177- }
2178- connect(m_cobj, SIGNAL(valueChangedFromEngine(double)),
2179- this, SLOT(controlChanged(double)));
2180- connect(m_cobj, SIGNAL(valueChanged(double)),
2181- this, SLOT(controlChanged(double)));
2182- return true;
2183+ return m_cobj.valid();
2184 }
2185
2186 void MidiOutputHandler::update() {
2187- if (m_cobj == NULL) {
2188- return;
2189- }
2190- controlChanged(m_cobj->get());
2191+ controlChanged(m_cobj.get());
2192 }
2193
2194 void MidiOutputHandler::controlChanged(double value) {
2195
2196=== modified file 'mixxx/src/controllers/midi/midioutputhandler.h'
2197--- mixxx/src/controllers/midi/midioutputhandler.h 2012-04-24 05:51:32 +0000
2198+++ mixxx/src/controllers/midi/midioutputhandler.h 2013-05-17 16:35:30 +0000
2199@@ -11,7 +11,7 @@
2200 #ifndef MIDIOUTPUTHANDLER_H
2201 #define MIDIOUTPUTHANDLER_H
2202
2203-#include "controlobject.h"
2204+#include "controlobjectthread.h"
2205
2206 class MidiController; // forward declaration
2207
2208@@ -32,7 +32,7 @@
2209
2210 private:
2211 MidiController* m_pController;
2212- ControlObject* m_cobj;
2213+ ControlObjectThread m_cobj;
2214 float m_min;
2215 float m_max;
2216 unsigned char m_status;
2217
2218=== modified file 'mixxx/src/controllers/softtakeover.cpp'
2219--- mixxx/src/controllers/softtakeover.cpp 2013-04-27 22:06:33 +0000
2220+++ mixxx/src/controllers/softtakeover.cpp 2013-05-17 16:35:30 +0000
2221@@ -79,7 +79,7 @@
2222 threshold = scaleFactor*(threshold/128.0f);
2223 }
2224
2225- double currentValue = midiVal ? control->GetMidiValue() : control->get();
2226+ double currentValue = midiVal ? control->getValueToMidi() : control->get();
2227 double difference = currentValue - newValue;
2228 double prevDiff = 0;
2229 bool sameSide = false;
2230
2231=== modified file 'mixxx/src/controllinpotmeter.cpp'
2232--- mixxx/src/controllinpotmeter.cpp 2012-12-22 14:07:45 +0000
2233+++ mixxx/src/controllinpotmeter.cpp 2013-05-17 16:35:30 +0000
2234@@ -1,34 +1,15 @@
2235 #include "controllinpotmeter.h"
2236 #include "defs.h"
2237
2238-
2239 // This control has a linear link between the m_dValue and the Midi Value
2240 // limitation: m_dMaxValue represents the midi value of 128 and is never reached
2241 ControlLinPotmeter::ControlLinPotmeter(ConfigKey key, double dMinValue, double dMaxValue) :
2242 ControlPotmeter(key, dMinValue, dMaxValue) {
2243-
2244-}
2245-
2246-double ControlLinPotmeter::getValueToWidget(double dValue) {
2247- double out = (dValue - m_dMinValue) / m_dValueRange;
2248- return math_min(out * 128, 127);
2249-}
2250-
2251-double ControlLinPotmeter::GetMidiValue() {
2252- double out = (m_dValue-m_dMinValue)/m_dValueRange;
2253- return math_min(out * 128, 127);
2254-}
2255-
2256-double ControlLinPotmeter::getValueFromWidget(double dValue) {
2257- double out = dValue / 128;
2258- return m_dMinValue + out * m_dValueRange;
2259-}
2260-
2261-void ControlLinPotmeter::setValueFromMidi(MidiOpCode o, double v) {
2262- Q_UNUSED(o);
2263- double out = v / 128;
2264- m_dValue = m_dMinValue + out * m_dValueRange;
2265- emit(valueChanged(m_dValue));
2266+ if (m_pControl) {
2267+ ControlNumericBehavior* pOldBehavior = m_pControl->setBehavior(
2268+ new ControlLinPotmeterBehavior(dMinValue, dMaxValue));
2269+ delete pOldBehavior;
2270+ }
2271 }
2272
2273
2274
2275=== modified file 'mixxx/src/controllinpotmeter.h'
2276--- mixxx/src/controllinpotmeter.h 2012-12-22 14:07:45 +0000
2277+++ mixxx/src/controllinpotmeter.h 2013-05-17 16:35:30 +0000
2278@@ -3,19 +3,10 @@
2279
2280 #include "controlpotmeter.h"
2281
2282-class ControlLinPotmeter : public ControlPotmeter
2283-{
2284+class ControlLinPotmeter : public ControlPotmeter {
2285 Q_OBJECT
2286 public:
2287-
2288 ControlLinPotmeter(ConfigKey key, double dMinValue=0.0, double dMaxValue=1.0);
2289-
2290- double getValueToWidget(double dValue);
2291- double GetMidiValue();
2292- double getValueFromWidget(double dValue);
2293-
2294- protected:
2295- void setValueFromMidi(MidiOpCode o, double v);
2296 };
2297
2298 #endif // CONTROLLINPOTMETER_H
2299
2300=== modified file 'mixxx/src/controllogpotmeter.cpp'
2301--- mixxx/src/controllogpotmeter.cpp 2012-04-27 07:47:21 +0000
2302+++ mixxx/src/controllogpotmeter.cpp 2013-05-17 16:35:30 +0000
2303@@ -15,14 +15,8 @@
2304 * *
2305 ***************************************************************************/
2306
2307-#include <math.h>
2308 #include "controllogpotmeter.h"
2309
2310-#define maxPosition 127
2311-#define minPosition 0
2312-#define middlePosition ((maxPosition-minPosition)/2)
2313-#define positionrange (maxPosition-minPosition)
2314-
2315 /* -------- ------------------------------------------------------
2316 Purpose: Creates a new logarithmic potmeter, where the value is
2317 given by:
2318@@ -38,86 +32,16 @@
2319 midino - number of the midi controller.
2320 midicontroller - pointer to the midi controller.
2321 -------- ------------------------------------------------------ */
2322-ControlLogpotmeter::ControlLogpotmeter(ConfigKey key, double dMaxValue) : ControlPotmeter(key)
2323-{
2324- m_dMinValue = 0.;
2325- m_dMaxValue = dMaxValue;
2326-
2327- if (m_dMaxValue==1.)
2328- {
2329- m_bTwoState = false;
2330-
2331- m_fB1 = log10(2.)/maxPosition;
2332- }
2333- else
2334- {
2335- m_bTwoState = true;
2336-
2337- m_fB1 = log10(2.)/middlePosition;
2338- m_fB2 = log10(dMaxValue)/(maxPosition-middlePosition);
2339- }
2340-
2341- m_dValueRange = m_dMaxValue-m_dMinValue;
2342-
2343- m_dValue = 1.0;
2344- m_dDefaultValue = 1.0;
2345-}
2346-
2347-double ControlLogpotmeter::getValueFromWidget(double dValue)
2348-{
2349- double dResult = 0;
2350-
2351- // Calculate the value linearly:
2352- if (!m_bTwoState)
2353- {
2354- dResult = pow(10., (double)(m_fB1*dValue)) - 1;
2355- }
2356- else
2357- {
2358- if (dValue <= middlePosition)
2359- dResult = pow(10., m_fB1*dValue) - 1;
2360- else
2361- dResult = pow(10., m_fB2*(dValue - middlePosition));
2362- }
2363-
2364- //qDebug() << "Midi: " << dValue << " ValueFromWidget : " << m_dValue;
2365- return dResult;
2366-}
2367-
2368-double ControlLogpotmeter::getValueToWidget(double dValue)
2369-{
2370- double pos;
2371-
2372- if (!m_bTwoState)
2373- {
2374- pos = log10(dValue+1)/m_fB1;
2375- }
2376- else
2377- {
2378- if (m_dValue>1.)
2379- pos = log10(dValue)/m_fB2 + middlePosition;
2380- else
2381- pos = log10(dValue+1)/m_fB1;
2382- }
2383- //qDebug() << "GetValueToWidget : " << pos;
2384- return pos;
2385-}
2386-
2387-double ControlLogpotmeter::GetMidiValue()
2388-{
2389- double midival = 0.;
2390-
2391- midival = getValueToWidget(m_dValue);
2392- // midival = 127.*(midival-m_dMinValue)/m_dValueRange
2393- //qDebug() << "GetMidiValue : " << midival;
2394- return midival;
2395-}
2396-
2397-void ControlLogpotmeter::setValueFromMidi(MidiOpCode o, double v) {
2398- Q_UNUSED(o);
2399- // m_dValue = m_dMinValue + (v/127.)*m_dValueRange;
2400- m_dValue = getValueFromWidget(v);
2401- // qDebug() << "SetValueFromMidiValue : " << m_dValue;
2402- emit(valueChanged(m_dValue));
2403+ControlLogpotmeter::ControlLogpotmeter(ConfigKey key, double dMaxValue)
2404+ : ControlPotmeter(key, 0, dMaxValue) {
2405+ // Override ControlPotmeters default value of 0.5
2406+ setDefaultValue(1.0);
2407+ set(1.0);
2408+
2409+ if (m_pControl) {
2410+ ControlNumericBehavior* pOldBehavior = m_pControl->setBehavior(
2411+ new ControlLogpotmeterBehavior(dMaxValue));
2412+ delete pOldBehavior;
2413+ }
2414 }
2415
2416
2417=== modified file 'mixxx/src/controllogpotmeter.h'
2418--- mixxx/src/controllogpotmeter.h 2012-03-17 17:42:44 +0000
2419+++ mixxx/src/controllogpotmeter.h 2013-05-17 16:35:30 +0000
2420@@ -3,7 +3,7 @@
2421 -------------------
2422 begin : Wed Feb 20 2002
2423 copyright : (C) 2002 by Tue and Ken Haste Andersen
2424- email :
2425+ email :
2426 ***************************************************************************/
2427
2428 /***************************************************************************
2429@@ -26,28 +26,10 @@
2430 *@author Tue and Ken Haste Andersen
2431 */
2432
2433-class ControlLogpotmeter : public ControlPotmeter
2434-{
2435+class ControlLogpotmeter : public ControlPotmeter {
2436 Q_OBJECT
2437-public:
2438+ public:
2439 ControlLogpotmeter(ConfigKey key, double dMaxValue=5.);
2440-
2441- double getValueFromWidget(double dValue);
2442- double getValueToWidget(double dValue);
2443-
2444- double GetMidiValue();
2445-
2446- void setValueFromMidi(MidiOpCode o, double v);
2447-
2448-protected:
2449-
2450- // This is true, if the log potmeter is divided into two states, one from 0 to 1, and
2451- // the second from 1 to m_dMaxValue. Two states is often used with knobs where the first
2452- // half rotation is used to control a value between 0 and 1, and the second half between
2453- // 1 and some bigger value.
2454- bool m_bTwoState;
2455-
2456- double m_fB1, m_fB2;
2457 };
2458
2459 #endif
2460
2461=== removed file 'mixxx/src/controlnull.cpp'
2462--- mixxx/src/controlnull.cpp 2007-09-09 22:52:24 +0000
2463+++ mixxx/src/controlnull.cpp 1970-01-01 00:00:00 +0000
2464@@ -1,27 +0,0 @@
2465-/***************************************************************************
2466- controlnull.cpp - description
2467- -------------------
2468- begin : Sat Jun 15 2002
2469- copyright : (C) 2002 by Tue & Ken Haste Andersen
2470- email : haste@diku.dk
2471-***************************************************************************/
2472-
2473-/***************************************************************************
2474-* *
2475-* This program is free software; you can redistribute it and/or modify *
2476-* it under the terms of the GNU General Public License as published by *
2477-* the Free Software Foundation; either version 2 of the License, or *
2478-* (at your option) any later version. *
2479-* *
2480-***************************************************************************/
2481-
2482-#include "controlnull.h"
2483-
2484-ControlNull::ControlNull() : ControlObject()
2485-{
2486-}
2487-
2488-ControlNull::~ControlNull()
2489-{
2490-}
2491-
2492
2493=== removed file 'mixxx/src/controlnull.h'
2494--- mixxx/src/controlnull.h 2004-10-01 08:18:11 +0000
2495+++ mixxx/src/controlnull.h 1970-01-01 00:00:00 +0000
2496@@ -1,34 +0,0 @@
2497-/***************************************************************************
2498- controlnull.h - description
2499- -------------------
2500- begin : Sat Jun 15 2002
2501- copyright : (C) 2002 by Tue & Ken Haste Andersen
2502- email : haste@diku.dk
2503- ***************************************************************************/
2504-
2505-/***************************************************************************
2506- * *
2507- * This program is free software; you can redistribute it and/or modify *
2508- * it under the terms of the GNU General Public License as published by *
2509- * the Free Software Foundation; either version 2 of the License, or *
2510- * (at your option) any later version. *
2511- * *
2512- ***************************************************************************/
2513-
2514-#ifndef CONTROLNULL_H
2515-#define CONTROLNULL_H
2516-
2517-#include "controlobject.h"
2518-
2519-/**
2520- *@author Tue & Ken Haste Andersen
2521- */
2522-
2523-class ControlNull : public ControlObject {
2524- Q_OBJECT
2525-public:
2526- ControlNull();
2527- ~ControlNull();
2528-};
2529-
2530-#endif
2531
2532=== modified file 'mixxx/src/controlobject.cpp'
2533--- mixxx/src/controlobject.cpp 2012-12-31 23:06:59 +0000
2534+++ mixxx/src/controlobject.cpp 2013-05-17 16:35:30 +0000
2535@@ -22,6 +22,7 @@
2536
2537 #include "controlobject.h"
2538 #include "controlevent.h"
2539+#include "control/control.h"
2540 #include "util/stat.h"
2541 #include "util/timer.h"
2542
2543@@ -29,110 +30,62 @@
2544 QHash<ConfigKey,ControlObject*> ControlObject::m_sqCOHash;
2545 QMutex ControlObject::m_sqCOHashMutex;
2546
2547-QMutex ControlObject::m_sqQueueMutexMidi;
2548-QMutex ControlObject::m_sqQueueMutexThread;
2549-QMutex ControlObject::m_sqQueueMutexChanges;
2550-QQueue<QueueObjectMidi*> ControlObject::m_sqQueueMidi;
2551-QQueue<QueueObjectThread*> ControlObject::m_sqQueueThread;
2552-QQueue<ControlObject*> ControlObject::m_sqQueueChanges;
2553
2554 ControlObject::ControlObject()
2555- : m_dValue(0),
2556- m_dDefaultValue(0),
2557- m_bIgnoreNops(true) {
2558-}
2559-
2560-ControlObject::ControlObject(ConfigKey key, bool bIgnoreNops, bool track)
2561- : m_dValue(0),
2562- m_dDefaultValue(0),
2563- m_key(key),
2564- m_bIgnoreNops(bIgnoreNops),
2565- m_bTrack(track),
2566- m_trackKey("control " + m_key.group + "," + m_key.item),
2567- m_trackType(Stat::UNSPECIFIED),
2568- m_trackFlags(Stat::COUNT | Stat::SUM | Stat::AVERAGE |
2569- Stat::SAMPLE_VARIANCE | Stat::MIN | Stat::MAX) {
2570- m_sqCOHashMutex.lock();
2571- m_sqCOHash.insert(m_key, this);
2572- m_sqCOHashMutex.unlock();
2573-
2574- if (m_bTrack) {
2575- // TODO(rryan): Make configurable.
2576- Stat::track(m_trackKey, static_cast<Stat::StatType>(m_trackType),
2577- static_cast<Stat::ComputeFlags>(m_trackFlags), m_dValue);
2578- }
2579-}
2580-
2581-ControlObject::ControlObject(const QString& group, const QString& item, bool bIgnoreNops)
2582- : m_dValue(0),
2583- m_dDefaultValue(0),
2584- m_key(group, item),
2585- m_bIgnoreNops(bIgnoreNops) {
2586- m_sqCOHashMutex.lock();
2587- m_sqCOHash.insert(m_key, this);
2588- m_sqCOHashMutex.unlock();
2589+ : m_pControl(NULL) {
2590+}
2591+
2592+ControlObject::ControlObject(ConfigKey key, bool bIgnoreNops, bool bTrack)
2593+ : m_pControl(NULL) {
2594+ initialize(key, bIgnoreNops, bTrack);
2595+}
2596+
2597+ControlObject::ControlObject(const QString& group, const QString& item,
2598+ bool bIgnoreNops, bool bTrack)
2599+ : m_pControl(NULL) {
2600+ initialize(ConfigKey(group, item), bIgnoreNops, bTrack);
2601 }
2602
2603 ControlObject::~ControlObject() {
2604 m_sqCOHashMutex.lock();
2605 m_sqCOHash.remove(m_key);
2606 m_sqCOHashMutex.unlock();
2607-
2608- ControlObjectThread * obj;
2609- m_qProxyListMutex.lock();
2610- QListIterator<ControlObjectThread*> it(m_qProxyList);
2611- while (it.hasNext())
2612- {
2613- obj = it.next();
2614- obj->slotParentDead();
2615- }
2616- m_qProxyListMutex.unlock();
2617-
2618-
2619- m_sqQueueMutexThread.lock();
2620- QMutableListIterator<QueueObjectThread*> tit(m_sqQueueThread);
2621- while (tit.hasNext()) {
2622- QueueObjectThread* tobj = tit.next();
2623- if (tobj->pControlObject == this) {
2624- tit.remove();
2625- delete tobj;
2626- }
2627- }
2628- m_sqQueueMutexThread.unlock();
2629-
2630- m_sqQueueMutexMidi.lock();
2631- QMutableListIterator<QueueObjectMidi*> mit(m_sqQueueMidi);
2632- while (mit.hasNext()) {
2633- QueueObjectMidi* mobj = mit.next();
2634- if (mobj->pControlObject == this) {
2635- mit.remove();
2636- delete mobj;
2637- }
2638- }
2639- m_sqQueueMutexMidi.unlock();
2640-
2641- // Remove this control object from the changes queue, since we're being
2642- // deleted.
2643- m_sqQueueMutexChanges.lock();
2644- m_sqQueueChanges.removeAll(this);
2645- m_sqQueueMutexChanges.unlock();
2646-
2647-}
2648-
2649+}
2650+
2651+void ControlObject::initialize(ConfigKey key, bool bIgnoreNops, bool bTrack) {
2652+ m_key = key;
2653+ m_pControl = ControlDoublePrivate::getControl(m_key, true, bIgnoreNops, bTrack);
2654+ connect(m_pControl, SIGNAL(valueChanged(double, QObject*)),
2655+ this, SLOT(privateValueChanged(double, QObject*)),
2656+ Qt::DirectConnection);
2657+
2658+ m_sqCOHashMutex.lock();
2659+ m_sqCOHash.insert(m_key, this);
2660+ m_sqCOHashMutex.unlock();
2661+}
2662+
2663+void ControlObject::privateValueChanged(double dValue, QObject* pSetter) {
2664+ // Only emit valueChanged() if we did not originate this change.
2665+ if (pSetter != this) {
2666+ emit(valueChanged(dValue));
2667+ } else {
2668+ emit(valueChangedFromEngine(dValue));
2669+ }
2670+}
2671+
2672+/*
2673 bool ControlObject::connectControls(ConfigKey src, ConfigKey dest)
2674 {
2675 // Find src and dest objects
2676 ControlObject * pSrc = getControl(src);
2677 ControlObject * pDest = getControl(dest);
2678
2679- if (pSrc && pDest)
2680- {
2681+ if (pSrc && pDest) {
2682 connect(pSrc, SIGNAL(valueChanged(double)), pDest, SLOT(set(double)));
2683- connect(pSrc, SIGNAL(valueChangedFromEngine(double)), pDest, SLOT(set(double)));
2684 return true;
2685+ } else {
2686+ return false;
2687 }
2688- else
2689- return false;
2690 }
2691
2692 bool ControlObject::disconnectControl(ConfigKey key)
2693@@ -148,42 +101,9 @@
2694 else
2695 return false;
2696 }
2697-
2698-void ControlObject::addProxy(ControlObjectThread * pControlObjectThread)
2699-{
2700- m_qProxyListMutex.lock();
2701- m_qProxyList.append(pControlObjectThread);
2702- m_qProxyListMutex.unlock();
2703-}
2704-
2705-void ControlObject::removeProxy(ControlObjectThread * pControlObjectThread) {
2706- m_qProxyListMutex.lock();
2707- m_qProxyList.removeAll(pControlObjectThread);
2708- m_qProxyListMutex.unlock();
2709-}
2710-
2711-bool ControlObject::updateProxies(ControlObjectThread * pProxyNoUpdate)
2712-{
2713- ControlObjectThread * obj;
2714- bool bUpdateSuccess = true;
2715- // qDebug() << "updateProxies: Group" << m_key.group << "/ Item" << m_key.item;
2716- m_qProxyListMutex.lock();
2717- QList<ControlObjectThread*> proxyList = m_qProxyList;
2718- m_qProxyListMutex.unlock();
2719-
2720- QListIterator<ControlObjectThread*> it(proxyList);
2721- while (it.hasNext())
2722- {
2723- obj = it.next();
2724- if (obj!=pProxyNoUpdate)
2725- {
2726- // qDebug() << "upd" << this->getKey().item;
2727- bUpdateSuccess = bUpdateSuccess && obj->setExtern(m_dValue);
2728- }
2729- }
2730- return bUpdateSuccess;
2731-}
2732-
2733+*/
2734+
2735+// static
2736 void ControlObject::getControls(QList<ControlObject*>* pControlList) {
2737 m_sqCOHashMutex.lock();
2738 for (QHash<ConfigKey, ControlObject*>::const_iterator it = m_sqCOHash.begin();
2739@@ -193,6 +113,7 @@
2740 m_sqCOHashMutex.unlock();
2741 }
2742
2743+// static
2744 ControlObject* ControlObject::getControl(const ConfigKey& key) {
2745 //qDebug() << "ControlObject::getControl for (" << key.group << "," << key.item << ")";
2746 QMutexLocker locker(&m_sqCOHashMutex);
2747@@ -205,199 +126,34 @@
2748 return NULL;
2749 }
2750
2751-void ControlObject::queueFromThread(double dValue, ControlObjectThread * pControlObjectThread)
2752-{
2753- QueueObjectThread * p = new QueueObjectThread;
2754- p->pControlObjectThread = pControlObjectThread;
2755- p->pControlObject = this;
2756- p->value = dValue;
2757-
2758- m_sqQueueMutexThread.lock();
2759- m_sqQueueThread.enqueue(p);
2760- m_sqQueueMutexThread.unlock();
2761-}
2762-
2763-void ControlObject::queueFromMidi(MidiOpCode o, double v)
2764-{
2765- QueueObjectMidi * p = new QueueObjectMidi;
2766- p->pControlObject = this;
2767- p->opcode = o;
2768- p->value = v;
2769-
2770- m_sqQueueMutexMidi.lock();
2771- m_sqQueueMidi.enqueue(p);
2772- m_sqQueueMutexMidi.unlock();
2773-}
2774-
2775-void ControlObject::setValueFromEngine(double dValue)
2776-{
2777- m_dValue = dValue;
2778- if (m_bTrack) {
2779- Stat::track(m_trackKey, static_cast<Stat::StatType>(m_trackType),
2780- static_cast<Stat::ComputeFlags>(m_trackFlags), m_dValue);
2781- }
2782- emit(valueChangedFromEngine(m_dValue));
2783-}
2784-
2785-void ControlObject::setValueFromMidi(MidiOpCode o, double v)
2786-{
2787- Q_UNUSED(o);
2788- m_dValue = v;
2789- if (m_bTrack) {
2790- Stat::track(m_trackKey, static_cast<Stat::StatType>(m_trackType),
2791- static_cast<Stat::ComputeFlags>(m_trackFlags), m_dValue);
2792- }
2793- emit(valueChanged(m_dValue));
2794-}
2795-
2796-double ControlObject::GetMidiValue()
2797-{
2798- return m_dValue;
2799-}
2800-
2801-void ControlObject::setValueFromThread(double dValue)
2802-{
2803- if (m_bIgnoreNops && m_dValue == dValue)
2804- return;
2805-
2806- m_dValue = dValue;
2807- if (m_bTrack) {
2808- Stat::track(m_trackKey, static_cast<Stat::StatType>(m_trackType),
2809- static_cast<Stat::ComputeFlags>(m_trackFlags), m_dValue);
2810- }
2811- emit(valueChanged(m_dValue));
2812-}
2813-
2814-void ControlObject::set(double dValue)
2815-{
2816- if (m_bIgnoreNops && m_dValue == dValue)
2817- return;
2818-
2819- setValueFromEngine(dValue);
2820- m_sqQueueMutexChanges.lock();
2821- m_sqQueueChanges.enqueue(this);
2822- m_sqQueueMutexChanges.unlock();
2823-}
2824-
2825-void ControlObject::add(double dValue)
2826-{
2827- if (m_bIgnoreNops && !dValue)
2828- return;
2829-
2830- setValueFromEngine(m_dValue+dValue);
2831- m_sqQueueMutexChanges.lock();
2832- m_sqQueueChanges.enqueue(this);
2833- m_sqQueueMutexChanges.unlock();
2834-}
2835-
2836-void ControlObject::sub(double dValue)
2837-{
2838- if (m_bIgnoreNops && !dValue)
2839- return;
2840-
2841- setValueFromEngine(m_dValue-dValue);
2842- m_sqQueueMutexChanges.lock();
2843- m_sqQueueChanges.enqueue(this);
2844- m_sqQueueMutexChanges.unlock();
2845-}
2846-
2847-double ControlObject::getValueFromWidget(double v)
2848-{
2849- return v;
2850-}
2851-
2852-double ControlObject::getValueToWidget(double v)
2853-{
2854- return v;
2855-}
2856-
2857-void ControlObject::sync() {
2858- // Update control objects with values recieved from threads. We tryLock
2859- // because ControlObject::sync() is re-entrant (even though we just run
2860- // sync() in the main loop). A slot invoked by sync() can create a modal
2861- // dialog which effectively blocks sync() but continues spinning the Qt
2862- // event loop. When sync() runs again, it is re-entrant if the modal dialog
2863- // is still up.
2864- if (m_sqQueueMutexThread.tryLock()) {
2865-
2866- // We have to make a copy of the queue otherwise we can get deadlocks
2867- // since responding to a queued event via setValueFromThread can trigger
2868- // a slot which in turn could cause a lock of m_sqQueueMutexThread.
2869- QQueue<QueueObjectThread*> qQueueThread = m_sqQueueThread;
2870- m_sqQueueThread.clear();
2871- m_sqQueueMutexThread.unlock();
2872-
2873- while (!qQueueThread.isEmpty()) {
2874- QueueObjectThread* obj = qQueueThread.dequeue();
2875- if (obj == NULL) {
2876- continue;
2877- }
2878- if (obj->pControlObject) {
2879- obj->pControlObject->setValueFromThread(obj->value);
2880- obj->pControlObject->updateProxies(obj->pControlObjectThread);
2881- }
2882- delete obj;
2883- }
2884- }
2885-
2886- // Update control objects with values recieved from MIDI. We tryLock because
2887- // ControlObject::sync() is re-entrant (even though we just run sync() in
2888- // the main loop). A slot invoked by sync() can create a modal dialog which
2889- // effectively blocks sync() but continues spinning the Qt event loop. When
2890- // sync() runs again, it is re-entrant if the modal dialog is still up.
2891- if (m_sqQueueMutexMidi.tryLock()) {
2892- // We have to make a copy of the queue otherwise we can get deadlocks
2893- // since responding to a queued event via setValueFromMidi can trigger a
2894- // slot which in turn could cause a lock of m_sqQueueMutexMidi.
2895- QQueue<QueueObjectMidi*> qQueueMidi = m_sqQueueMidi;
2896- m_sqQueueMidi.clear();
2897- m_sqQueueMutexMidi.unlock();
2898-
2899- while (!qQueueMidi.isEmpty()) {
2900- QueueObjectMidi* obj = qQueueMidi.dequeue();
2901- if (obj == NULL) {
2902- continue;
2903- }
2904- if (obj->pControlObject) {
2905- obj->pControlObject->setValueFromMidi(obj->opcode, obj->value);
2906- obj->pControlObject->updateProxies(NULL);
2907- }
2908- delete obj;
2909- }
2910- }
2911-
2912- // Update app threads (ControlObjectThread derived objects) with changes in
2913- // the corresponding ControlObjects. These updates should only occour if no
2914- // changes has been in the object from widgets, midi or application threads.
2915- if (m_sqQueueMutexChanges.tryLock()) {
2916- ScopedTimer t("ControlObject::sync qQueueChanges");
2917- QSet<ControlObject*> setChanges = QSet<ControlObject*>::fromList(m_sqQueueChanges);
2918- Stat::track("ControlObject::sync qQueueChanges dupes", Stat::UNSPECIFIED,
2919- Stat::COUNT | Stat::SUM | Stat::AVERAGE | Stat::MIN | Stat::MAX,
2920- m_sqQueueChanges.size() - setChanges.size());
2921- m_sqQueueChanges.clear();
2922- m_sqQueueMutexChanges.unlock();
2923-
2924- QList<ControlObject*> failedUpdates;
2925- for (QSet<ControlObject*>::iterator it = setChanges.begin();
2926- it != setChanges.end(); ++it) {
2927- ControlObject* obj = *it;
2928- // If update is not successful, enqueue again
2929- if (!obj->updateProxies()) {
2930- failedUpdates.push_back(obj);
2931- Stat::track("ControlObject::sync qQueueChanges failed CO update",
2932- Stat::UNSPECIFIED, Stat::COUNT | Stat::SUM | Stat::AVERAGE, 1.0);
2933-
2934- }
2935- }
2936-
2937- // If we cannot lock the change mutex then we will just drop these
2938- // updates on the floor. We can't lock() since sync() is re-entrant
2939- // (potentially across multiple threads, though that should change going
2940- // forward).
2941- if (failedUpdates.size() > 0 && m_sqQueueMutexChanges.tryLock()) {
2942- m_sqQueueChanges.append(failedUpdates);
2943- m_sqQueueMutexChanges.unlock();
2944- }
2945+void ControlObject::setValueFromMidi(MidiOpCode o, double v) {
2946+ if (m_pControl) {
2947+ m_pControl->setMidiParameter(o, v);
2948+ }
2949+}
2950+
2951+double ControlObject::getValueToMidi() const {
2952+ return m_pControl ? m_pControl->getMidiParameter() : 0.0;
2953+}
2954+
2955+void ControlObject::setValueFromThread(double dValue, QObject* pSender) {
2956+ if (m_pControl) {
2957+ m_pControl->set(dValue, pSender);
2958+ }
2959+}
2960+
2961+double ControlObject::get() const {
2962+ return m_pControl ? m_pControl->get() : 0.0;
2963+}
2964+
2965+void ControlObject::reset() {
2966+ if (m_pControl) {
2967+ m_pControl->reset(this);
2968+ }
2969+}
2970+
2971+void ControlObject::set(const double& value) {
2972+ if (m_pControl) {
2973+ m_pControl->set(value, this);
2974 }
2975 }
2976
2977=== modified file 'mixxx/src/controlobject.h'
2978--- mixxx/src/controlobject.h 2012-12-30 18:29:15 +0000
2979+++ mixxx/src/controlobject.h 2013-05-17 16:35:30 +0000
2980@@ -23,51 +23,19 @@
2981 #include <QMutex>
2982
2983 #include "configobject.h"
2984-#include "controlobjectthread.h"
2985 #include "controllers/midi/midimessage.h"
2986-
2987-class QWidget;
2988-class ConfigKey;
2989-
2990-struct QueueObjectThread
2991-{
2992- ControlObjectThread *pControlObjectThread;
2993- ControlObject *pControlObject;
2994- double value;
2995-};
2996-
2997-struct QueueObjectMidi
2998-{
2999- ControlObject *pControlObject;
3000- MidiOpCode opcode;
3001- double value;
3002-};
3003-
3004-/**
3005- * ControlObjects is used as a way to share controller values between controllers, GUI and
3006- * the sound engine. Whenever the value is changed by either a connected widget or a ControlEventMidi
3007- * the emitValueChanged method is called which syncronizes the new value with the player thread
3008- * using the semaphore protected queue.
3009- *
3010- * The player thread has a corresponding ControlEngine object for each ControlObject. The
3011- * ControlEngine object updates the ControlObject by queueing the values, and sending them as an
3012- * event to the ControlObject.
3013- *
3014- *@author Tue and Ken Haste Andersen
3015- */
3016-
3017-class ControlObject : public QObject
3018-{
3019+#include "control/control.h"
3020+
3021+class ControlObject : public QObject {
3022 Q_OBJECT
3023-public:
3024+ public:
3025 ControlObject();
3026- ControlObject(ConfigKey key, bool bIgnoreNops=true, bool track=false);
3027- ControlObject(const QString& group, const QString& item, bool bIgnoreNops=true);
3028+ ControlObject(ConfigKey key,
3029+ bool bIgnoreNops=true, bool bTrack=false);
3030+ ControlObject(const QString& group, const QString& item,
3031+ bool bIgnoreNops=true, bool bTrack=false);
3032 virtual ~ControlObject();
3033- /** Connect two control objects dest and src, so each time src is updated, so is dest. */
3034- static bool connectControls(ConfigKey src, ConfigKey dest);
3035- /** Disonnect a control object. */
3036- static bool disconnectControl(ConfigKey key);
3037+
3038 /** Returns a pointer to the ControlObject matching the given ConfigKey */
3039 static ControlObject* getControl(const ConfigKey& key);
3040 static inline ControlObject* getControl(const QString& group, const QString& item) {
3041@@ -78,90 +46,54 @@
3042 // Adds all ControlObjects that currently exist to pControlList
3043 static void getControls(QList<ControlObject*>* pControlsList);
3044
3045- /** Used to add a pointer to the corresponding ControlObjectThread of this ControlObject */
3046- void addProxy(ControlObjectThread *pControlObjectThread);
3047- // To get rid of a proxy when the corresponding object is being deleted for example
3048- void removeProxy(ControlObjectThread *pControlObjectThread);
3049- /** Update proxies, execep the one given a pointer to. Returns true if all updates
3050- * happend, otherwise false. */
3051- bool updateProxies(ControlObjectThread *pProxyNoUpdate=0);
3052- /** Return the key of the object */
3053- inline ConfigKey getKey() { return m_key; }
3054- /** Return the value of the ControlObject */
3055- inline double get() { return m_dValue; }
3056- /** Add to value. Not thread safe. */
3057- void add(double dValue);
3058- /** Subtract from value. Not thread safe. */
3059- void sub(double dValue);
3060- /** Syncronizes all ControlObjects with their corresponding proxies. */
3061- static void sync();
3062- /** Queue a control change from a widget. Thread safe. Blocking. */
3063- void queueFromThread(double dValue, ControlObjectThread *pControlObjectThread=0);
3064- /** Queue a control change from MIDI. Thread safe. Blocking. */
3065- void queueFromMidi(MidiOpCode o, double v);
3066- /** Return a ControlObject value, corresponding to the widget input value. Thread safe. */
3067- virtual double getValueFromWidget(double dValue);
3068- /** Return a widget value corresponding to the ControlObject input value. Thread safe. */
3069- virtual double getValueToWidget(double dValue);
3070- /** get value (range 0..127) **/
3071- virtual double GetMidiValue();
3072- virtual void setDefaultValue(double dValue) {
3073- m_dDefaultValue = dValue;
3074- }
3075- virtual double defaultValue() const {
3076- return m_dDefaultValue;
3077- }
3078-
3079-public slots:
3080- /** Sets the value of the object and updates associated proxy objects. Not thread safe. */
3081- void set(double dValue);
3082-
3083-signals:
3084+ // Return the key of the object
3085+ inline ConfigKey getKey() const { return m_key; }
3086+ // Returns the value of the ControlObject
3087+ double get() const;
3088+ // Sets the ControlObject value
3089+ void set(const double& value);
3090+ // Sets the default value
3091+ void reset();
3092+
3093+ inline void setDefaultValue(double dValue) {
3094+ if (m_pControl) {
3095+ m_pControl->setDefaultValue(dValue);
3096+ }
3097+ }
3098+ inline double defaultValue() const {
3099+ return m_pControl ? m_pControl->defaultValue() : 0.0;
3100+ }
3101+
3102+ signals:
3103 void valueChanged(double);
3104 void valueChangedFromEngine(double);
3105
3106-protected:
3107- /** Sets the value of the object. Not thread safe. */
3108- virtual void setValueFromEngine(double dValue);
3109- /** Called when a widget has changed value. Not thread safe. */
3110+ public:
3111+ // DEPRECATED: Called to set the control value from the controller
3112+ // subsystem.
3113 virtual void setValueFromMidi(MidiOpCode o, double v);
3114- /** Called when another thread has changed value. Not thread safe. */
3115- virtual void setValueFromThread(double dValue);
3116+ virtual double getValueToMidi() const;
3117+ // DEPRECATED: Called to set the control value from another thread.
3118+ virtual void setValueFromThread(double dValue, QObject* pSetter);
3119
3120-protected:
3121- /** The actual value of the controller */
3122- double m_dValue;
3123- double m_dDefaultValue;
3124- /** Key of the object */
3125+ protected:
3126+ // Key of the object
3127 ConfigKey m_key;
3128-
3129-private:
3130- // Whether to ignore set/add/sub()'s which would have no effect
3131- bool m_bIgnoreNops;
3132- // Whether to track value changes with the stats framework.
3133- bool m_bTrack;
3134- QString m_trackKey;
3135- int m_trackType;
3136- int m_trackFlags;
3137- /** List of associated proxy objects */
3138- QList<ControlObjectThread*> m_qProxyList;
3139- /** Mutex for the proxy list */
3140- QMutex m_qProxyListMutex;
3141-
3142- /** Hash of ControlObject instantiations */
3143+ ControlDoublePrivate* m_pControl;
3144+
3145+ private slots:
3146+ void privateValueChanged(double value, QObject* pSetter);
3147+
3148+ private:
3149+ void initialize(ConfigKey key, bool bIgnoreNops, bool bTrack);
3150+ inline bool ignoreNops() const {
3151+ return m_pControl ? m_pControl->ignoreNops() : true;
3152+ }
3153+
3154+ // Hash of ControlObject instantiations
3155 static QHash<ConfigKey,ControlObject*> m_sqCOHash;
3156- /** Mutex guarding access to the ControlObject hash **/
3157+ // Mutex guarding access to the ControlObject hash
3158 static QMutex m_sqCOHashMutex;
3159- /** Mutex protecting access to the queues */
3160- static QMutex m_sqQueueMutexMidi, m_sqQueueMutexThread, m_sqQueueMutexChanges;
3161- /** Queue holding control changes from MIDI */
3162- static QQueue<QueueObjectMidi*> m_sqQueueMidi;
3163- /** Queues holding control changes from other application threads and from widgets */
3164- static QQueue<QueueObjectThread*> m_sqQueueThread;
3165- /** Queue holding ControlObjects that has changed, but not been syncronized with it's
3166- * associated ControlObjectProxy objects. */
3167- static QQueue<ControlObject*> m_sqQueueChanges;
3168 };
3169
3170-
3171 #endif
3172
3173=== modified file 'mixxx/src/controlobjectthread.cpp'
3174--- mixxx/src/controlobjectthread.cpp 2012-12-09 22:01:39 +0000
3175+++ mixxx/src/controlobjectthread.cpp 2013-05-17 16:35:30 +0000
3176@@ -20,92 +20,63 @@
3177
3178 #include "controlobjectthread.h"
3179 #include "controlobject.h"
3180+#include "control/control.h"
3181
3182 ControlObjectThread::ControlObjectThread(ControlObject* pControlObject, QObject* pParent)
3183 : QObject(pParent),
3184- m_dValue(0.0),
3185- m_pControlObject(pControlObject) {
3186- // Register with the associated ControlObject
3187- if (m_pControlObject != NULL) {
3188- m_pControlObject->addProxy(this);
3189- connect(m_pControlObject, SIGNAL(destroyed()),
3190- this, SLOT(slotParentDead()));
3191- // Initialize value
3192- m_dValue = m_pControlObject->get();
3193- }
3194- emitValueChanged();
3195+ m_key(pControlObject ? pControlObject->getKey() : ConfigKey()),
3196+ m_pControl(NULL) {
3197+ if (pControlObject) {
3198+ m_pControl = ControlDoublePrivate::getControl(pControlObject->getKey(), false);
3199+ }
3200+ if (m_pControl) {
3201+ connect(m_pControl, SIGNAL(valueChanged(double, QObject*)),
3202+ this, SLOT(slotValueChanged(double, QObject*)),
3203+ Qt::DirectConnection);
3204+ }
3205 }
3206
3207 ControlObjectThread::~ControlObjectThread() {
3208- if (m_pControlObject) {
3209- // Our parent is still around, make sure it doesn't send us any more events
3210- m_pControlObject->removeProxy(this);
3211- }
3212+}
3213+
3214+bool ControlObjectThread::valid() const {
3215+ return m_pControl != NULL;
3216 }
3217
3218 double ControlObjectThread::get() {
3219- m_dataMutex.lock();
3220- double v = m_dValue;
3221- m_dataMutex.unlock();
3222-
3223- return v;
3224+ return m_pControl ? m_pControl->get() : 0.0;
3225 }
3226
3227 void ControlObjectThread::slotSet(double v) {
3228- m_dataMutex.lock();
3229- m_dValue = v;
3230- m_dataMutex.unlock();
3231-
3232- updateControlObject(v);
3233-}
3234-
3235-bool ControlObjectThread::setExtern(double v) {
3236- bool result = false;
3237-
3238- if (m_dataMutex.tryLock()) {
3239- m_dValue = v;
3240- result = true;
3241- m_dataMutex.unlock();
3242- emitValueChanged();
3243- }
3244-
3245- return result;
3246+ set(v);
3247+}
3248+
3249+void ControlObjectThread::set(double v) {
3250+ if (m_pControl) {
3251+ m_pControl->set(v, this);
3252+ }
3253+}
3254+
3255+void ControlObjectThread::reset() {
3256+ if (m_pControl) {
3257+ // NOTE(rryan): This is important. The originator of this action does
3258+ // not know the resulting value so it makes sense that we should emit a
3259+ // general valueChanged() signal even though the change originated from
3260+ // us. For this reason, we provide NULL here so that the change is
3261+ // broadcast as valueChanged() and not valueChangedByThis().
3262+ m_pControl->reset(NULL);
3263+ }
3264 }
3265
3266 void ControlObjectThread::emitValueChanged() {
3267 emit(valueChanged(get()));
3268 }
3269
3270-void ControlObjectThread::add(double v) {
3271- m_dataMutex.lock();
3272- double newValue = m_dValue + v;
3273- m_dValue = newValue;
3274- m_dataMutex.unlock();
3275-
3276- updateControlObject(newValue);
3277-}
3278-
3279-void ControlObjectThread::sub(double v) {
3280- m_dataMutex.lock();
3281- double newValue = m_dValue - v;
3282- m_dValue = newValue;
3283- m_dataMutex.unlock();
3284-
3285- updateControlObject(newValue);
3286-}
3287-
3288-void ControlObjectThread::updateControlObject(double v) {
3289- if (m_pControlObject) {
3290- m_pControlObject->queueFromThread(v, this);
3291+void ControlObjectThread::slotValueChanged(double v, QObject* pSetter) {
3292+ if (pSetter != this) {
3293+ // This is base implementation of this function without scaling
3294+ emit(valueChanged(v));
3295+ } else {
3296+ emit(valueChangedByThis(v));
3297 }
3298 }
3299-
3300-void ControlObjectThread::slotParentDead() {
3301- // Now we've got a chance of avoiding segfaults with judicious
3302- // use of if(m_pControlObject)
3303- m_pControlObject = NULL;
3304-}
3305-
3306-ControlObject* ControlObjectThread::getControlObject() {
3307- return m_pControlObject;
3308-}
3309
3310=== modified file 'mixxx/src/controlobjectthread.h'
3311--- mixxx/src/controlobjectthread.h 2012-12-09 22:01:39 +0000
3312+++ mixxx/src/controlobjectthread.h 2013-05-17 16:35:30 +0000
3313@@ -24,6 +24,9 @@
3314 #include <qwaitcondition.h>
3315 #include <QQueue>
3316
3317+#include "configobject.h"
3318+
3319+class ControlDoublePrivate;
3320 class ControlObject;
3321
3322 class ControlObjectThread : public QObject {
3323@@ -32,46 +35,41 @@
3324 ControlObjectThread(ControlObject *pControlObject, QObject* pParent=NULL);
3325 virtual ~ControlObjectThread();
3326
3327- /** Returns the value of the object. Thread safe, blocking */
3328- virtual double get();
3329- /** Setting the value from an external controller. This happen when a ControlObject has
3330- * changed and its value is syncronized with this object. Thread safe, non blocking. Returns
3331- * true if successful, otherwise false. Thread safe, non blocking. */
3332- virtual bool setExtern(double v);
3333- /** Adds a value to the value property of the ControlEngine. Notification in a similar way
3334- * to set. Thread safe, blocking. */
3335- virtual void add(double v);
3336- /** Subtracts a value to the value property. Notification in a similar way
3337- * to set. Thread safe, blocking. */
3338- virtual void sub(double v);
3339 /** Called from update(); */
3340 void emitValueChanged();
3341
3342-
3343- // FIXME: Dangerous GED hack
3344- ControlObject* getControlObject();
3345-
3346-public slots:
3347- /** The value is changed by the engine, and the corresponding ControlObject is updated.
3348- * Thread safe, blocking. */
3349+ inline ConfigKey getKey() const {
3350+ return m_key;
3351+ }
3352+
3353+ // Returns the value of the object. Thread safe, non-blocking.
3354+ virtual double get();
3355+
3356+ bool valid() const;
3357+
3358+ public slots:
3359+ // Set the control to a new value. Non-blocking.
3360 virtual void slotSet(double v);
3361-
3362- // The danger signal! This is for safety in wierd shutdown scenarios where the
3363- // ControlObject dies to avoid segfaults.
3364- void slotParentDead();
3365-
3366-signals:
3367+ // Sets the control value to v. Thread safe, non-blocking.
3368+ virtual void set(double v);
3369+ // Resets the control to its default value. Thread safe, non-blocking.
3370+ virtual void reset();
3371+
3372+ signals:
3373 void valueChanged(double);
3374-
3375-protected:
3376- /** The actual value of the object */
3377- double m_dValue;
3378- /** Mutex controlling access to non-static members*/
3379- QMutex m_dataMutex;
3380- /** Pointer to corresponding ControlObject */
3381- ControlObject *m_pControlObject;
3382- /** Update corresponding ControlObject to the value */
3383- virtual void updateControlObject(double v);
3384+ // This means that the control value has changed as a result of a mutation
3385+ // (set/add/sub/reset) originating from this object.
3386+ void valueChangedByThis(double);
3387+
3388+ protected slots:
3389+ // Receives the value from the master control and re-emits either
3390+ // valueChanged(double) or valueChangedByThis(double) based on pSetter.
3391+ virtual void slotValueChanged(double v, QObject* pSetter);
3392+
3393+ protected:
3394+ ConfigKey m_key;
3395+ // Pointer to connected control.
3396+ ControlDoublePrivate* m_pControl;
3397 };
3398
3399 #endif
3400
3401=== modified file 'mixxx/src/controlobjectthreadmain.cpp'
3402--- mixxx/src/controlobjectthreadmain.cpp 2012-12-10 07:13:01 +0000
3403+++ mixxx/src/controlobjectthreadmain.cpp 2013-05-17 16:35:30 +0000
3404@@ -16,34 +16,34 @@
3405 installEventFilter(this);
3406 }
3407
3408-ControlObjectThreadMain::~ControlObjectThreadMain() {
3409-}
3410-
3411-bool ControlObjectThreadMain::eventFilter(QObject * o, QEvent * e)
3412-{
3413+bool ControlObjectThreadMain::eventFilter(QObject* o, QEvent* e) {
3414 // Handle events
3415 if (e && e->type() == MIXXXEVENT_CONTROL) {
3416 ControlEvent * ce = (ControlEvent *)e;
3417- //qDebug() << "ControlEvent " << ce->value();
3418- m_dValue = ce->value();
3419 emit(valueChanged(ce->value()));
3420 } else {
3421- // standard event processing
3422 return QObject::eventFilter(o,e);
3423 }
3424 return true;
3425 }
3426
3427-bool ControlObjectThreadMain::setExtern(double v)
3428-{
3429+void ControlObjectThreadMain::slotValueChanged(double, QObject* pSetter) {
3430+ // The value argument to this function is in value space, but for some of
3431+ // our subclasses (e.g. ControlObjectThreadWidget) emit valueChanged(double)
3432+ // should emit in parameter space. So we emit the value of get() instead
3433+ // which will get the correct value.
3434+
3435 // If we are already running in the main thread, then go ahead and update.
3436 if (QThread::currentThread() == QApplication::instance()->thread()) {
3437- m_dValue = v;
3438- emit(valueChanged(v));
3439+ if (pSetter != this) {
3440+ emit(valueChanged(get()));
3441+ } else {
3442+ emit(valueChangedByThis(get()));
3443+ }
3444 } else {
3445 // Otherwise, we have to post the event to the main thread event queue
3446 // and then catch the event via eventFilter.
3447- QApplication::postEvent(this, new ControlEvent(v));
3448+ QApplication::postEvent(this, new ControlEvent(get()));
3449 }
3450- return true;
3451+
3452 }
3453
3454=== modified file 'mixxx/src/controlobjectthreadmain.h'
3455--- mixxx/src/controlobjectthreadmain.h 2012-12-09 22:01:39 +0000
3456+++ mixxx/src/controlobjectthreadmain.h 2013-05-17 16:35:30 +0000
3457@@ -12,7 +12,7 @@
3458 // ControlObjectThreadMain is a variant of ControlObjectThread that should only
3459 // ever have its methods called by the main thread. The benefit is that the
3460 // valueChanged() signal is proxied to the main thread automatically and the
3461-// get()/set()/add()/sub() methods are lock-free and performant relative to
3462+// get()/set() methods are lock-free and performant relative to
3463 // ControlObjectThread since COT requires using a mutex to maintain the
3464 // integrity of the control value. If you create a COTM, you must make sure to
3465 // only call its methods from the main thread.
3466@@ -20,29 +20,14 @@
3467 Q_OBJECT
3468 public:
3469 ControlObjectThreadMain(ControlObject *pControlObject, QObject* pParent=NULL);
3470- virtual ~ControlObjectThreadMain();
3471- /** Event filter */
3472- bool eventFilter(QObject *o, QEvent *e);
3473- /** Notify this object through events */
3474- virtual bool setExtern(double v);
3475-
3476- // No lock is needed for get()/add()/sub()/slotSet(). See class comment above.
3477- virtual inline double get() { return m_dValue; }
3478- virtual inline void add(double v) {
3479- // Would not be safe if m_dValue were not safe for use in the main
3480- // thread!
3481- slotSet(m_dValue + v);
3482- }
3483- virtual inline void sub(double v) {
3484- // Would not be safe if m_dValue were not safe for use in the main
3485- // thread!
3486- slotSet(m_dValue - v);
3487- }
3488- public slots:
3489- virtual inline void slotSet(double v) {
3490- m_dValue = v;
3491- updateControlObject(v);
3492- }
3493+
3494+ bool eventFilter(QObject* o, QEvent* e);
3495+
3496+ protected slots:
3497+ // Receives the value from the master control, proxies it to the main
3498+ // thread, and re-emits either valueChanged(double) or
3499+ // valueChangedByThis(double) based on pSetter.
3500+ virtual void slotValueChanged(double v, QObject* pSetter);
3501 };
3502
3503 #endif
3504
3505=== modified file 'mixxx/src/controlobjectthreadwidget.cpp'
3506--- mixxx/src/controlobjectthreadwidget.cpp 2012-12-09 22:01:39 +0000
3507+++ mixxx/src/controlobjectthreadwidget.cpp 2013-05-17 16:35:30 +0000
3508@@ -6,13 +6,6 @@
3509
3510 ControlObjectThreadWidget::ControlObjectThreadWidget(ControlObject * pControlObject, QObject* pParent)
3511 : ControlObjectThreadMain(pControlObject, pParent) {
3512- // ControlObjectThread's constructor sets m_dValue to
3513- // m_pControlObject->get(). Since we represent the widget's value, we need
3514- // to reset m_dValue to be the result of getValueToWidget.
3515- if (m_pControlObject != NULL) {
3516- m_dValue = m_pControlObject->getValueToWidget(m_pControlObject->get());
3517- emitValueChanged();
3518- }
3519 }
3520
3521 ControlObjectThreadWidget::~ControlObjectThreadWidget() {
3522@@ -23,30 +16,44 @@
3523 EmitOption emitOption, Qt::MouseButton state) {
3524 if (connectValueFromWidget) {
3525 connect(widget, SIGNAL(valueReset()),
3526- this, SLOT(slotReset()));
3527+ this, SLOT(reset()));
3528
3529 if (emitOption & EMIT_ON_PRESS) {
3530- if (state == Qt::NoButton)
3531+ switch (state) {
3532+ case Qt::NoButton:
3533 connect(widget, SIGNAL(valueChangedDown(double)),
3534 this, SLOT(slotSet(double)));
3535- else if (state == Qt::LeftButton)
3536+ break;
3537+ case Qt::LeftButton:
3538 connect(widget, SIGNAL(valueChangedLeftDown(double)),
3539 this, SLOT(slotSet(double)));
3540- else if (state == Qt::RightButton)
3541+ break;
3542+ case Qt::RightButton:
3543 connect(widget, SIGNAL(valueChangedRightDown(double)),
3544 this, SLOT(slotSet(double)));
3545+ break;
3546+ default:
3547+ break;
3548+ }
3549 }
3550
3551 if (emitOption & EMIT_ON_RELEASE) {
3552- if (state == Qt::NoButton)
3553+ switch (state) {
3554+ case Qt::NoButton:
3555 connect(widget, SIGNAL(valueChangedUp(double)),
3556 this, SLOT(slotSet(double)));
3557- else if (state == Qt::LeftButton)
3558+ break;
3559+ case Qt::LeftButton:
3560 connect(widget, SIGNAL(valueChangedLeftUp(double)),
3561 this, SLOT(slotSet(double)));
3562- else if (state == Qt::RightButton)
3563+ break;
3564+ case Qt::RightButton:
3565 connect(widget, SIGNAL(valueChangedRightUp(double)),
3566 this, SLOT(slotSet(double)));
3567+ break;
3568+ default:
3569+ break;
3570+ }
3571 }
3572 }
3573
3574@@ -54,42 +61,22 @@
3575 connect(this, SIGNAL(valueChanged(double)),
3576 widget, SLOT(setValue(double)));
3577 }
3578- emitValueChanged();
3579+ emit(valueChanged(get()));
3580 }
3581
3582 void ControlObjectThreadWidget::setWidgetOnOff(QWidget* widget)
3583 {
3584 QApplication::connect(this, SIGNAL(valueChanged(double)),
3585 widget, SLOT(setOnOff(double)));
3586- emit(valueChanged(m_dValue));
3587-}
3588-
3589-void ControlObjectThreadWidget::slotReset() {
3590- if (m_pControlObject != NULL) {
3591- double defaultValue = m_pControlObject->defaultValue();
3592- // defaultValue is a control value. slotSet needs to be set with a widget
3593- // value since widget value-changed signals connect to it. setExtern needs
3594- // to be called with a control value since it is triggered by control
3595- // updates.
3596- slotSet(m_pControlObject->getValueToWidget(defaultValue));
3597- setExtern(defaultValue);
3598- }
3599-}
3600-
3601-void ControlObjectThreadWidget::updateControlObject(double v)
3602-{
3603- if (m_pControlObject != NULL) {
3604- m_pControlObject->queueFromThread(
3605- m_pControlObject->getValueFromWidget(v), this);
3606- }
3607-}
3608-
3609-bool ControlObjectThreadWidget::setExtern(double v) {
3610- //qDebug() << "set extern widget";
3611- if (m_pControlObject != NULL) {
3612- // COTM just emits v here. Instead we need to transform the value with
3613- // getValueToWidget.
3614- QApplication::postEvent(this, new ControlEvent(m_pControlObject->getValueToWidget(v)));
3615- }
3616- return true;
3617+ emit(valueChanged(get()));
3618+}
3619+
3620+double ControlObjectThreadWidget::get() {
3621+ return m_pControl ? m_pControl->getWidgetParameter() : 0.0;
3622+}
3623+
3624+void ControlObjectThreadWidget::set(double v) {
3625+ if (m_pControl) {
3626+ m_pControl->setWidgetParameter(v, this);
3627+ }
3628 }
3629
3630=== modified file 'mixxx/src/controlobjectthreadwidget.h'
3631--- mixxx/src/controlobjectthreadwidget.h 2012-12-09 22:01:39 +0000
3632+++ mixxx/src/controlobjectthreadwidget.h 2013-05-17 16:35:30 +0000
3633@@ -22,10 +22,9 @@
3634 @author Tue Haste Andersen
3635 */
3636
3637-class ControlObjectThreadWidget : public ControlObjectThreadMain
3638-{
3639+class ControlObjectThreadWidget : public ControlObjectThreadMain {
3640 Q_OBJECT
3641-public:
3642+ public:
3643
3644 enum EmitOption {
3645 EMIT_NEVER = 0x00,
3646@@ -42,13 +41,11 @@
3647 EmitOption emitOption=EMIT_ON_PRESS, Qt::MouseButton state=Qt::NoButton);
3648 /** Associates a the enabled/disabled state of a widget with the state of a ControlObject. */
3649 void setWidgetOnOff(QWidget *widget);
3650- bool setExtern(double v);
3651-
3652- private slots:
3653- void slotReset();
3654-
3655- private:
3656- virtual void updateControlObject(double v);
3657+
3658+ virtual double get();
3659+
3660+ public slots:
3661+ virtual void set(double v);
3662 };
3663
3664 #endif
3665
3666=== modified file 'mixxx/src/controlpotmeter.cpp'
3667--- mixxx/src/controlpotmeter.cpp 2012-04-27 07:47:21 +0000
3668+++ mixxx/src/controlpotmeter.cpp 2013-05-17 16:35:30 +0000
3669@@ -27,11 +27,11 @@
3670 potmeter is changed.
3671 midicontroller - pointer to the midi controller.
3672 -------- ------------------------------------------------------ */
3673-ControlPotmeter::ControlPotmeter(ConfigKey key, double dMinValue, double dMaxValue) : ControlObject(key)
3674-{
3675- setRange(dMinValue,dMaxValue);
3676- setStep(m_dValueRange/10.f);
3677- setSmallStep(m_dValueRange/100.f);
3678+ControlPotmeter::ControlPotmeter(ConfigKey key, double dMinValue, double dMaxValue)
3679+ : ControlObject(key) {
3680+ setRange(dMinValue, dMaxValue);
3681+ setStep(m_dValueRange / 10.f);
3682+ setSmallStep(m_dValueRange / 100.f);
3683
3684 // These controls are deleted when this ControlPotmeter is since we set
3685 // their parent as this.
3686@@ -100,13 +100,11 @@
3687 {
3688 }
3689
3690-double ControlPotmeter::getMin()
3691-{
3692+double ControlPotmeter::getMin() const {
3693 return m_dMinValue;
3694 }
3695
3696-double ControlPotmeter::getMax()
3697-{
3698+double ControlPotmeter::getMax() const {
3699 return m_dMaxValue;
3700 }
3701
3702@@ -124,73 +122,30 @@
3703 {
3704 m_dMinValue = dMinValue;
3705 m_dMaxValue = dMaxValue;
3706- m_dValueRange = m_dMaxValue-m_dMinValue;
3707- m_dValue = m_dMinValue + 0.5*m_dValueRange;
3708- m_dDefaultValue = m_dValue;
3709+ m_dValueRange = m_dMaxValue - m_dMinValue;
3710+ double default_value = m_dMinValue + 0.5 * m_dValueRange;
3711+
3712+ if (m_pControl) {
3713+ ControlNumericBehavior* pOldBehavior = m_pControl->setBehavior(
3714+ new ControlPotmeterBehavior(dMinValue, dMaxValue));
3715+ delete pOldBehavior;
3716+ }
3717+
3718+ setDefaultValue(default_value);
3719+ set(default_value);
3720 //qDebug() << "" << this << ", min " << m_dMinValue << ", max " << m_dMaxValue << ", range " << m_dValueRange << ", val " << m_dValue;
3721 }
3722
3723-double ControlPotmeter::getValueToWidget(double dValue)
3724-{
3725- double out = (dValue-m_dMinValue)/m_dValueRange;
3726- return (out < 0.5) ? out*128. : out*126. + 1.;
3727-}
3728-
3729-double ControlPotmeter::GetMidiValue()
3730-{
3731- double out = (m_dValue-m_dMinValue)/m_dValueRange;
3732- return (out < 0.5) ? out*128. : out*126. + 1.;
3733-}
3734-
3735-double ControlPotmeter::getValueFromWidget(double dValue)
3736-{
3737- double out = (dValue < 64) ? dValue / 128. : (dValue-1) / 126.;
3738- return m_dMinValue + out * m_dValueRange;
3739-}
3740-
3741-void ControlPotmeter::setValueFromThread(double dValue)
3742-{
3743- if (dValue == m_dValue) return;
3744-
3745- if (dValue>m_dMaxValue)
3746- m_dValue = m_dMaxValue;
3747- else if (dValue<m_dMinValue)
3748- m_dValue = m_dMinValue;
3749- else
3750- m_dValue = dValue;
3751- emit(valueChanged(m_dValue));
3752-}
3753-
3754-void ControlPotmeter::setValueFromEngine(double dValue)
3755-{
3756- if (dValue>m_dMaxValue)
3757- m_dValue = m_dMaxValue;
3758- else if (dValue<m_dMinValue)
3759- m_dValue = m_dMinValue;
3760- else
3761- m_dValue = dValue;
3762- emit(valueChangedFromEngine(m_dValue));
3763-}
3764-
3765-void ControlPotmeter::setValueFromMidi(MidiOpCode o, double v)
3766-{
3767- Q_UNUSED(o);
3768- double out = (v < 64) ? v / 128. : (v-1) / 126.;
3769- m_dValue = m_dMinValue + out*m_dValueRange;
3770- emit(valueChanged(m_dValue));
3771-}
3772-
3773 void ControlPotmeter::incValue(double keypos)
3774 {
3775 if (keypos>0)
3776 {
3777- m_dValue += m_dStep;
3778- if (m_dValue > m_dMaxValue)
3779- m_dValue = m_dMaxValue;
3780- emit(valueChanged(m_dValue));
3781-
3782- // incValue will be activated by assosiated _up or _down ControlObject, and thus it is safe to update all proxies.
3783- updateProxies(0);
3784+ double value = get();
3785+ value += m_dStep;
3786+ if (value > m_dMaxValue) {
3787+ value = m_dMaxValue;
3788+ }
3789+ set(value);
3790 }
3791 }
3792
3793@@ -198,13 +153,13 @@
3794 {
3795 if (keypos>0)
3796 {
3797- m_dValue -= m_dStep;
3798- if (m_dValue < m_dMinValue)
3799- m_dValue = m_dMinValue;
3800- emit(valueChanged(m_dValue));
3801+ double value = get();
3802
3803- // decValue will be activated by assosiated _up or _down ControlObject, and thus it is safe to update all proxies.
3804- updateProxies(0);
3805+ value -= m_dStep;
3806+ if (value < m_dMinValue) {
3807+ value = m_dMinValue;
3808+ }
3809+ set(value);
3810 }
3811 }
3812
3813@@ -212,13 +167,12 @@
3814 {
3815 if (keypos>0)
3816 {
3817- m_dValue += m_dSmallStep;
3818- if (m_dValue > m_dMaxValue)
3819- m_dValue = m_dMaxValue;
3820- emit(valueChanged(m_dValue));
3821-
3822- // incSmallValue will be activated by assosiated _up_small or _down_small ControlObject, and thus it is safe to update all proxies.
3823- updateProxies(0);
3824+ double value = get();
3825+ value += m_dSmallStep;
3826+ if (value > m_dMaxValue) {
3827+ value = m_dMaxValue;
3828+ }
3829+ set(value);
3830 }
3831 }
3832
3833@@ -226,13 +180,12 @@
3834 {
3835 if (keypos>0)
3836 {
3837- m_dValue -= m_dSmallStep;
3838- if (m_dValue < m_dMinValue)
3839- m_dValue = m_dMinValue;
3840- emit(valueChanged(m_dValue));
3841-
3842- // decSmallValue will be activated by assosiated _up_small or _down_small ControlObject, and thus it is safe to update all proxies.
3843- updateProxies(0);
3844+ double value = get();
3845+ value -= m_dSmallStep;
3846+ if (value < m_dMinValue) {
3847+ value = m_dMinValue;
3848+ }
3849+ set(value);
3850 }
3851 }
3852
3853@@ -240,9 +193,7 @@
3854 {
3855 if (keypos>0)
3856 {
3857- m_dValue = 0.0;
3858- emit(valueChanged(m_dValue));
3859- updateProxies(0);
3860+ set(0.0);
3861 }
3862 }
3863
3864@@ -250,9 +201,7 @@
3865 {
3866 if (keypos>0)
3867 {
3868- m_dValue = 1.0;
3869- emit(valueChanged(m_dValue));
3870- updateProxies(0);
3871+ set(1.0);
3872 }
3873 }
3874
3875@@ -260,51 +209,35 @@
3876 {
3877 if (keypos>0)
3878 {
3879- m_dValue = -1.0;
3880- emit(valueChanged(m_dValue));
3881- updateProxies(0);
3882+ set(-1.0);
3883 }
3884 }
3885
3886 void ControlPotmeter::setToDefault(double v) {
3887 if (v > 0) {
3888- m_dValue = m_dDefaultValue;
3889- emit(valueChanged(m_dValue));
3890- updateProxies(0);
3891- }
3892-}
3893-
3894-void ControlPotmeter::toggleValue(double keypos)
3895-{
3896- if (keypos>0)
3897- {
3898- if (m_dValue > 0.0)
3899- {
3900- m_dValue = 0.0;
3901- }
3902- else
3903- {
3904- m_dValue = 1.0;
3905- }
3906- emit(valueChanged(m_dValue));
3907- updateProxies(0);
3908- }
3909-}
3910-
3911-void ControlPotmeter::toggleMinusValue(double keypos)
3912-{
3913- if (keypos>0)
3914- {
3915- if (m_dValue > 0.0)
3916- {
3917- m_dValue = -1.0;
3918- }
3919- else
3920- {
3921- m_dValue = 1.0;
3922- }
3923- emit(valueChanged(m_dValue));
3924- updateProxies(0);
3925+ reset();
3926+ }
3927+}
3928+
3929+void ControlPotmeter::toggleValue(double keypos) {
3930+ if (keypos>0) {
3931+ double value = get();
3932+ if (value > 0.0) {
3933+ set(0.0);
3934+ } else {
3935+ set(1.0);
3936+ }
3937+ }
3938+}
3939+
3940+void ControlPotmeter::toggleMinusValue(double keypos) {
3941+ if (keypos>0) {
3942+ double value = get();
3943+ if (value > 0.0) {
3944+ set(-1.0);
3945+ } else {
3946+ set(1.0);
3947+ }
3948 }
3949 }
3950
3951
3952=== modified file 'mixxx/src/controlpotmeter.h'
3953--- mixxx/src/controlpotmeter.h 2012-04-27 07:47:21 +0000
3954+++ mixxx/src/controlpotmeter.h 2013-05-17 16:35:30 +0000
3955@@ -35,23 +35,15 @@
3956 ControlPotmeter(ConfigKey key, double dMinValue=0.0, double dMaxValue=1.0);
3957 ~ControlPotmeter();
3958 /** Returns the minimum allowed value */
3959- double getMin();
3960+ double getMin() const;
3961 /** Returns the maximum allowed value */
3962- double getMax();
3963+ double getMax() const;
3964 /** Sets the step size of the associated PushButtons */
3965 void setStep(double);
3966 /** Sets the small step size of the associated PushButtons */
3967 void setSmallStep(double);
3968- /** Sets the minimum and maximum allowed value. The control value is reset when calling
3969- * this method */
3970- void setRange(double dMinValue, double dMaxValue);
3971- double getValueFromWidget(double dValue);
3972- double getValueToWidget(double dValue);
3973- double GetMidiValue();
3974
3975 public slots:
3976- void setValueFromThread(double dValue);
3977- void setValueFromEngine(double dValue);
3978 /** Increases the value. This method is called from an associated PushButton control */
3979 void incValue(double);
3980 /** Decreases the value. This method is called from an associated PushButton control */
3981@@ -74,9 +66,15 @@
3982 void toggleMinusValue(double);
3983
3984 protected:
3985- void setValueFromMidi(MidiOpCode o, double v);
3986+ /** Sets the minimum and maximum allowed value. The control value is reset when calling
3987+ * this method */
3988+ void setRange(double dMinValue, double dMaxValue);
3989
3990- double m_dMaxValue, m_dMinValue, m_dValueRange, m_dStep, m_dSmallStep;
3991+ double m_dMaxValue;
3992+ double m_dMinValue;
3993+ double m_dValueRange;
3994+ double m_dStep;
3995+ double m_dSmallStep;
3996 };
3997
3998 #endif
3999
4000=== modified file 'mixxx/src/controlpushbutton.cpp'
4001--- mixxx/src/controlpushbutton.cpp 2012-07-09 21:38:03 +0000
4002+++ mixxx/src/controlpushbutton.cpp 2013-05-17 16:35:30 +0000
4003@@ -17,9 +17,6 @@
4004
4005 #include "controlpushbutton.h"
4006
4007-// static
4008-const int ControlPushButton::kPowerWindowTimeMillis = 300;
4009-
4010 /* -------- ------------------------------------------------------
4011 Purpose: Creates a new simulated latching push-button.
4012 Input: key - Key for the configuration file
4013@@ -28,6 +25,13 @@
4014 ControlObject(key, false),
4015 m_buttonMode(PUSH),
4016 m_iNoStates(2) {
4017+ if (m_pControl) {
4018+ ControlNumericBehavior* pOldBehavior = m_pControl->setBehavior(
4019+ new ControlPushButtonBehavior(
4020+ static_cast<ControlPushButtonBehavior::ButtonMode>(m_buttonMode),
4021+ m_iNoStates));
4022+ delete pOldBehavior;
4023+ }
4024 }
4025
4026 ControlPushButton::~ControlPushButton() {
4027@@ -37,57 +41,25 @@
4028 void ControlPushButton::setButtonMode(enum ButtonMode mode) {
4029 //qDebug() << "Setting " << m_Key.group << m_Key.item << "as toggle";
4030 m_buttonMode = mode;
4031+
4032+ if (m_pControl) {
4033+ ControlNumericBehavior* pOldBehavior = m_pControl->setBehavior(
4034+ new ControlPushButtonBehavior(
4035+ static_cast<ControlPushButtonBehavior::ButtonMode>(m_buttonMode),
4036+ m_iNoStates));
4037+ delete pOldBehavior;
4038+ }
4039 }
4040
4041 void ControlPushButton::setStates(int num_states) {
4042 m_iNoStates = num_states;
4043-}
4044-
4045-void ControlPushButton::setValueFromMidi(MidiOpCode o, double v) {
4046- // keyboard events are handled by this function as well
4047- //if (m_bMidiSimulateLatching)
4048-
4049- //qDebug() << "bMidiSimulateLatching is true!";
4050- // Only react on NOTE_ON midi events if simulating latching...
4051-
4052- //qDebug() << o << v;
4053-
4054- // This block makes push-buttons act as power window buttons.
4055- if (m_buttonMode == POWERWINDOW && m_iNoStates == 2) {
4056- if (o == MIDI_NOTE_ON) {
4057- if (v > 0.) {
4058- m_dValue = !m_dValue;
4059- m_pushTimer.setSingleShot(true);
4060- m_pushTimer.start(kPowerWindowTimeMillis);
4061- }
4062- } else if (o == MIDI_NOTE_OFF) {
4063- if (!m_pushTimer.isActive()) {
4064- m_dValue = 0.0;
4065- }
4066- }
4067- } else if (m_buttonMode == TOGGLE) {
4068- // This block makes push-buttons act as toggle buttons.
4069- if (m_iNoStates > 2) { //multistate button
4070- if (v > 0.) { //looking for NOTE_ON doesn't seem to work...
4071- m_dValue++;
4072- if (m_dValue >= m_iNoStates)
4073- m_dValue = 0;
4074- }
4075- } else {
4076- if (o == MIDI_NOTE_ON) {
4077- if (v > 0.) {
4078- m_dValue = !m_dValue;
4079- }
4080- }
4081- }
4082- } else { //Not a toggle button (trigger only when button pushed)
4083- if (o == MIDI_NOTE_ON) {
4084- m_dValue = !m_dValue;
4085- } else if (o == MIDI_NOTE_OFF) {
4086- m_dValue = 0.0;
4087- }
4088+
4089+ if (m_pControl) {
4090+ ControlNumericBehavior* pOldBehavior = m_pControl->setBehavior(
4091+ new ControlPushButtonBehavior(
4092+ static_cast<ControlPushButtonBehavior::ButtonMode>(m_buttonMode),
4093+ m_iNoStates));
4094+ delete pOldBehavior;
4095 }
4096-
4097- emit(valueChanged(m_dValue));
4098 }
4099
4100
4101=== modified file 'mixxx/src/controlpushbutton.h'
4102--- mixxx/src/controlpushbutton.h 2012-05-01 05:59:37 +0000
4103+++ mixxx/src/controlpushbutton.h 2013-05-17 16:35:30 +0000
4104@@ -21,7 +21,6 @@
4105 #include "controlobject.h"
4106 #include "controllers/midi/midimessage.h"
4107 #include "defs.h"
4108-#include <QTimer>
4109
4110 /**
4111 *@author Tue and Ken Haste Andersen
4112@@ -31,11 +30,10 @@
4113 Q_OBJECT
4114 public:
4115 enum ButtonMode {
4116- PUSH,
4117+ PUSH = 0,
4118 TOGGLE,
4119 POWERWINDOW
4120 };
4121- static const int kPowerWindowTimeMillis;
4122
4123 ControlPushButton(ConfigKey key);
4124 virtual ~ControlPushButton();
4125@@ -46,13 +44,9 @@
4126 void setButtonMode(enum ButtonMode mode);
4127 void setStates(int num_states);
4128
4129- protected:
4130- void setValueFromMidi(MidiOpCode o, double v);
4131-
4132 private:
4133 enum ButtonMode m_buttonMode;
4134 int m_iNoStates;
4135- QTimer m_pushTimer;
4136 };
4137
4138 #endif
4139
4140=== modified file 'mixxx/src/controlttrotary.cpp'
4141--- mixxx/src/controlttrotary.cpp 2012-03-17 17:42:44 +0000
4142+++ mixxx/src/controlttrotary.cpp 2013-05-17 16:35:30 +0000
4143@@ -20,31 +20,13 @@
4144 Purpose: Creates a new rotary encoder
4145 Input: key
4146 -------- ------------------------------------------------------ */
4147-ControlTTRotary::ControlTTRotary(ConfigKey key) : ControlObject(key)
4148-{
4149-}
4150-
4151-double ControlTTRotary::getValueFromWidget(double dValue)
4152-{
4153- // Non-linear scaling
4154- double temp = (((dValue-64.)*(dValue-64.))/64.);
4155- if ((dValue-64.)<0)
4156- temp = -temp;
4157-
4158- //qDebug() << "tt rotary in " << dValue << ", out " << temp;
4159-
4160- return temp; //dValue-64.;
4161-}
4162-
4163-double ControlTTRotary::getValueToWidget(double dValue)
4164-{
4165- return dValue*200.+64.;
4166-}
4167-
4168-void ControlTTRotary::setValueFromMidi(MidiOpCode o, double v)
4169-{
4170- Q_UNUSED(o);
4171- m_dValue = v;
4172- emit(valueChanged(m_dValue));
4173-}
4174+ControlTTRotary::ControlTTRotary(ConfigKey key) : ControlObject(key) {
4175+ if (m_pControl) {
4176+ ControlNumericBehavior* pOldBehavior = m_pControl->setBehavior(
4177+ new ControlTTRotaryBehavior());
4178+ delete pOldBehavior;
4179+ }
4180+}
4181+
4182+
4183
4184
4185=== modified file 'mixxx/src/controlttrotary.h'
4186--- mixxx/src/controlttrotary.h 2012-03-17 17:42:44 +0000
4187+++ mixxx/src/controlttrotary.h 2013-05-17 16:35:30 +0000
4188@@ -20,20 +20,10 @@
4189 #include "configobject.h"
4190 #include "controlobject.h"
4191
4192-/** Turn Table rotary controller class. The turntable rotary sends midi events: 0 when turning
4193- * backwards, and 1 when turning forward. This class keeps track of it's speed, using a timer
4194- * interrupt */
4195-class ControlTTRotary : public ControlObject
4196-{
4197+class ControlTTRotary : public ControlObject {
4198 Q_OBJECT
4199-public:
4200+ public:
4201 ControlTTRotary(ConfigKey key);
4202-
4203- double getValueFromWidget(double dValue);
4204- double getValueToWidget(double dValue);
4205-
4206-protected:
4207- void setValueFromMidi(MidiOpCode o, double v);
4208 };
4209
4210 #endif
4211
4212=== modified file 'mixxx/src/dlgprefeq.cpp'
4213--- mixxx/src/dlgprefeq.cpp 2012-04-29 15:54:21 +0000
4214+++ mixxx/src/dlgprefeq.cpp 2013-05-17 16:35:30 +0000
4215@@ -15,8 +15,6 @@
4216 * *
4217 ***************************************************************************/
4218
4219-#include "dlgprefeq.h"
4220-#include "engine/enginefilteriir.h"
4221 #include <qlineedit.h>
4222 #include <qwidget.h>
4223 #include <qslider.h>
4224@@ -28,6 +26,10 @@
4225
4226 #include <assert.h>
4227
4228+#include "dlgprefeq.h"
4229+#include "engine/enginefilteriir.h"
4230+#include "controlobject.h"
4231+
4232 #define CONFIG_KEY "[Mixer Profile]"
4233
4234 const int kFrequencyUpperLimit = 20050;
4235
4236=== modified file 'mixxx/src/dlgprefeq.h'
4237--- mixxx/src/dlgprefeq.h 2011-12-06 19:40:07 +0000
4238+++ mixxx/src/dlgprefeq.h 2013-05-17 16:35:30 +0000
4239@@ -21,7 +21,7 @@
4240 #include "ui_dlgprefeqdlg.h"
4241 #include "configobject.h"
4242 #include "engine/enginefilterblock.h"
4243-#include "controlobject.h"
4244+#include "controlobjectthread.h"
4245
4246 class QWidget;
4247 /**
4248
4249=== modified file 'mixxx/src/dlgtrackinfo.h'
4250--- mixxx/src/dlgtrackinfo.h 2010-09-10 06:04:12 +0000
4251+++ mixxx/src/dlgtrackinfo.h 2013-05-17 16:35:30 +0000
4252@@ -10,7 +10,15 @@
4253 #include "ui_dlgtrackinfo.h"
4254
4255 #include "trackinfoobject.h"
4256-#include "controlbeat.h"
4257+
4258+/** Minimum allowed Beat per minute (BPM) */
4259+const int minBPM = 30;
4260+/** Maximum allowed bpm */
4261+const int maxBPM = 240;
4262+/** Maximum allowed interval between beats in milli seconds (calculated from minBPM) */
4263+const int maxInterval = (int)(1000.*(60./(CSAMPLE)minBPM));
4264+/** Filter length */
4265+const int filterLength = 5;
4266
4267 class Cue;
4268
4269
4270=== modified file 'mixxx/src/engine/bpmcontrol.cpp'
4271--- mixxx/src/engine/bpmcontrol.cpp 2013-03-31 16:56:35 +0000
4272+++ mixxx/src/engine/bpmcontrol.cpp 2013-05-17 16:35:30 +0000
4273@@ -145,7 +145,7 @@
4274 double averageBpm = 60.0 * 1000.0 / averageLength;
4275 double dRate = 1.0 + m_pRateDir->get() * m_pRateRange->get() * m_pRateSlider->get();
4276 m_pFileBpm->set(averageBpm / dRate);
4277- slotAdjustBpm();
4278+ slotAdjustBpm();
4279 }
4280
4281 void BpmControl::slotControlBeatSyncPhase(double v) {
4282
4283=== modified file 'mixxx/src/engine/enginemaster.cpp'
4284--- mixxx/src/engine/enginemaster.cpp 2013-05-11 18:05:26 +0000
4285+++ mixxx/src/engine/enginemaster.cpp 2013-05-17 16:35:30 +0000
4286@@ -32,7 +32,6 @@
4287 #include "enginevumeter.h"
4288 #include "enginexfader.h"
4289 #include "engine/sidechain/enginesidechain.h"
4290-#include "engine/syncworker.h"
4291 #include "sampleutil.h"
4292 #include "util/timer.h"
4293
4294@@ -45,7 +44,6 @@
4295 bool bEnableSidechain) {
4296 m_pWorkerScheduler = new EngineWorkerScheduler(this);
4297 m_pWorkerScheduler->start();
4298- m_pSyncWorker = new SyncWorker(m_pWorkerScheduler);
4299
4300 // Master sample rate
4301 m_pMasterSampleRate = new ControlObject(ConfigKey(group, "samplerate"), true, true);
4302@@ -148,7 +146,6 @@
4303 }
4304
4305 delete m_pWorkerScheduler;
4306- delete m_pSyncWorker;
4307 }
4308
4309 const CSAMPLE* EngineMaster::getMasterBuffer() const
4310@@ -440,9 +437,6 @@
4311 //Master/headphones interleaving is now done in
4312 //SoundManager::requestBuffer() - Albert Nov 18/07
4313
4314- // Schedule a ControlObject sync
4315- m_pSyncWorker->schedule();
4316-
4317 // We're close to the end of the callback. Wake up the engine worker
4318 // scheduler so that it runs the workers.
4319 m_pWorkerScheduler->runWorkers();
4320
4321=== modified file 'mixxx/src/engine/enginemaster.h'
4322--- mixxx/src/engine/enginemaster.h 2013-05-07 18:28:07 +0000
4323+++ mixxx/src/engine/enginemaster.h 2013-05-17 16:35:30 +0000
4324@@ -135,7 +135,6 @@
4325 CSAMPLE *m_pMaster, *m_pHead;
4326
4327 EngineWorkerScheduler *m_pWorkerScheduler;
4328- SyncWorker* m_pSyncWorker;
4329
4330 ControlObject* m_pMasterVolume;
4331 ControlObject* m_pHeadVolume;
4332
4333=== modified file 'mixxx/src/engine/loopingcontrol.cpp'
4334--- mixxx/src/engine/loopingcontrol.cpp 2013-05-14 22:49:05 +0000
4335+++ mixxx/src/engine/loopingcontrol.cpp 2013-05-17 16:35:30 +0000
4336@@ -408,7 +408,7 @@
4337 // If we're looping, stop looping
4338 if (m_bLoopingEnabled) {
4339 setLoopingEnabled(false);
4340- }
4341+ }
4342 }
4343 }
4344
4345@@ -442,6 +442,12 @@
4346 newpos--;
4347 }
4348
4349+
4350+ if (m_iLoopStartSample == newpos) {
4351+ //nothing to do
4352+ return;
4353+ }
4354+
4355 clearActiveBeatLoop();
4356
4357 if (pos == -1.0f) {
4358@@ -469,6 +475,11 @@
4359 newpos--;
4360 }
4361
4362+ if (m_iLoopEndSample == newpos) {
4363+ //nothing to do
4364+ return;
4365+ }
4366+
4367 // Reject if the loop-in is not set, or if the new position is before the
4368 // start point (but not -1).
4369 if (m_iLoopStartSample == -1 ||
4370@@ -583,6 +594,19 @@
4371 void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint) {
4372 int samples = m_pTrackSamples->get();
4373 if (!m_pTrack || samples == 0) {
4374+ clearActiveBeatLoop();
4375+ return;
4376+ }
4377+
4378+
4379+ if (!m_pBeats) {
4380+ clearActiveBeatLoop();
4381+ return;
4382+ }
4383+
4384+ // For now we do not handle negative beatloops.
4385+ if (beats < 0) {
4386+ clearActiveBeatLoop();
4387 return;
4388 }
4389
4390@@ -590,12 +614,14 @@
4391 // fine.
4392 foreach (BeatLoopingControl* pBeatLoopControl, m_beatLoops) {
4393 if (pBeatLoopControl->getSize() == beats) {
4394- if (m_pActiveBeatLoop &&
4395- m_pActiveBeatLoop != pBeatLoopControl) {
4396- m_pActiveBeatLoop->deactivate();
4397+ if (m_pActiveBeatLoop != pBeatLoopControl) {
4398+ if (m_pActiveBeatLoop) {
4399+ m_pActiveBeatLoop->deactivate();
4400+ }
4401+ m_pActiveBeatLoop = pBeatLoopControl;
4402 }
4403- m_pActiveBeatLoop = pBeatLoopControl;
4404 pBeatLoopControl->activate();
4405+ break;
4406 }
4407 }
4408
4409@@ -603,17 +629,6 @@
4410 int loop_in = -1;
4411 int loop_out = -1;
4412
4413- if (!m_pBeats) {
4414- clearActiveBeatLoop();
4415- return;
4416- }
4417-
4418- // For now we do not handle negative beatloops.
4419- if (beats < 0) {
4420- clearActiveBeatLoop();
4421- return;
4422- }
4423-
4424 // For positive numbers we start from the current position/closest beat and
4425 // create the loop around X beats from there.
4426 if (beats > 0) {
4427@@ -729,15 +744,19 @@
4428 }
4429
4430 void BeatLoopingControl::deactivate() {
4431- m_bActive = false;
4432- m_pEnabled->set(0);
4433- m_pLegacy->set(0);
4434+ if (m_bActive) {
4435+ m_bActive = false;
4436+ m_pEnabled->set(0);
4437+ m_pLegacy->set(0);
4438+ }
4439 }
4440
4441 void BeatLoopingControl::activate() {
4442- m_bActive = true;
4443- m_pEnabled->set(1);
4444- m_pLegacy->set(1);
4445+ if (!m_bActive) {
4446+ m_bActive = true;
4447+ m_pEnabled->set(1);
4448+ m_pLegacy->set(1);
4449+ }
4450 }
4451
4452 void BeatLoopingControl::slotLegacy(double v) {
4453
4454=== modified file 'mixxx/src/engine/ratecontrol.cpp'
4455--- mixxx/src/engine/ratecontrol.cpp 2013-05-01 13:38:41 +0000
4456+++ mixxx/src/engine/ratecontrol.cpp 2013-05-17 16:35:30 +0000
4457@@ -258,28 +258,33 @@
4458 {
4459 // Adjusts temp rate down if button pressed
4460 if (buttonRatePermDown->get())
4461- m_pRateSlider->sub(m_pRateDir->get() * m_dPerm / (100. * m_pRateRange->get()));
4462+ m_pRateSlider->set(m_pRateSlider->get() -
4463+ m_pRateDir->get() * m_dPerm / (100. * m_pRateRange->get()));
4464 }
4465
4466 void RateControl::slotControlRatePermDownSmall(double)
4467 {
4468 // Adjusts temp rate down if button pressed
4469 if (buttonRatePermDownSmall->get())
4470- m_pRateSlider->sub(m_pRateDir->get() * m_dPermSmall / (100. * m_pRateRange->get()));
4471+ m_pRateSlider->set(m_pRateSlider->get() -
4472+ m_pRateDir->get() * m_dPermSmall / (100. * m_pRateRange->get()));
4473 }
4474
4475 void RateControl::slotControlRatePermUp(double)
4476 {
4477 // Adjusts temp rate up if button pressed
4478- if (buttonRatePermUp->get())
4479- m_pRateSlider->add(m_pRateDir->get() * m_dPerm / (100. * m_pRateRange->get()));
4480+ if (buttonRatePermUp->get()) {
4481+ m_pRateSlider->set(m_pRateSlider->get() +
4482+ m_pRateDir->get() * m_dPerm / (100. * m_pRateRange->get()));
4483+ }
4484 }
4485
4486 void RateControl::slotControlRatePermUpSmall(double)
4487 {
4488 // Adjusts temp rate up if button pressed
4489 if (buttonRatePermUpSmall->get())
4490- m_pRateSlider->add(m_pRateDir->get() * m_dPermSmall / (100. * m_pRateRange->get()));
4491+ m_pRateSlider->set(m_pRateSlider->get() +
4492+ m_pRateDir->get() * m_dPermSmall / (100. * m_pRateRange->get()));
4493 }
4494
4495 void RateControl::slotControlRateTempDown(double)
4496
4497=== removed file 'mixxx/src/engine/syncworker.cpp'
4498--- mixxx/src/engine/syncworker.cpp 2012-12-10 07:00:21 +0000
4499+++ mixxx/src/engine/syncworker.cpp 1970-01-01 00:00:00 +0000
4500@@ -1,37 +0,0 @@
4501-#include <QApplication>
4502-
4503-#include "engine/syncworker.h"
4504-
4505-#include "controlobject.h"
4506-#include "engine/engineworkerscheduler.h"
4507-
4508-SyncWorker::SyncWorker(EngineWorkerScheduler* pScheduler) {
4509- pScheduler->bindWorker(this);
4510- installEventFilter(this);
4511-}
4512-
4513-SyncWorker::~SyncWorker() {
4514-}
4515-
4516-#define MIXXXEVENT_SYNC ((QEvent::Type)(QEvent::User+4))
4517-bool SyncWorker::eventFilter(QObject* o, QEvent* e) {
4518- if (e && e->type() == MIXXXEVENT_SYNC) {
4519- ControlObject::sync();
4520- return true;
4521- }
4522- return QObject::eventFilter(o,e);
4523-}
4524-
4525-void SyncWorker::run() {
4526- // Notify the EngineWorkerScheduler that the work we scheduled is starting.
4527- emit(workStarting(this));
4528-
4529- QApplication::postEvent(this, new QEvent(MIXXXEVENT_SYNC));
4530-
4531- // Notify the EngineWorkerScheduler that the work we did is done.
4532- emit(workDone(this));
4533-}
4534-
4535-void SyncWorker::schedule() {
4536- emit(workReady(this));
4537-}
4538
4539=== removed file 'mixxx/src/engine/syncworker.h'
4540--- mixxx/src/engine/syncworker.h 2012-12-10 07:00:21 +0000
4541+++ mixxx/src/engine/syncworker.h 1970-01-01 00:00:00 +0000
4542@@ -1,23 +0,0 @@
4543-#ifndef SYNCWORKER_H
4544-#define SYNCWORKER_H
4545-
4546-#include <QObject>
4547-#include <QEvent>
4548-
4549-#include "engine/engineworker.h"
4550-
4551-class EngineWorkerScheduler;
4552-
4553-class SyncWorker : public EngineWorker {
4554- Q_OBJECT
4555- public:
4556- explicit SyncWorker(EngineWorkerScheduler* pScheduler);
4557- virtual ~SyncWorker();
4558-
4559- void run();
4560- void schedule();
4561-
4562- bool eventFilter(QObject* o, QEvent* e);
4563-};
4564-
4565-#endif /* SYNCWORKER_H */
4566
4567=== modified file 'mixxx/src/mixxx.cpp'
4568--- mixxx/src/mixxx.cpp 2013-05-13 00:05:03 +0000
4569+++ mixxx/src/mixxx.cpp 2013-05-17 16:35:30 +0000
4570@@ -401,8 +401,6 @@
4571 // but do not set up controllers until the end of the application startup
4572 qDebug() << "Creating ControllerManager";
4573 m_pControllerManager = new ControllerManager(m_pConfig);
4574- connect(m_pControllerManager, SIGNAL(syncControlSystem()),
4575- this, SLOT(slotSyncControlSystem()));
4576
4577 WaveformWidgetFactory::create();
4578 WaveformWidgetFactory::instance()->setConfig(m_pConfig);
4579@@ -1640,8 +1638,3 @@
4580 }
4581 return true;
4582 }
4583-
4584-void MixxxApp::slotSyncControlSystem() {
4585- ScopedTimer t("MixxxApp::slotSyncControlSystem");
4586- ControlObject::sync();
4587-}
4588
4589=== modified file 'mixxx/src/mixxx.h'
4590--- mixxx/src/mixxx.h 2013-05-13 00:05:03 +0000
4591+++ mixxx/src/mixxx.h 2013-05-17 16:35:30 +0000
4592@@ -118,7 +118,6 @@
4593 void slotToCenterOfPrimaryScreen();
4594
4595 void onNewSkinLoaded();
4596- void slotSyncControlSystem();
4597
4598 signals:
4599 void newSkinLoaded();
4600
4601=== modified file 'mixxx/src/mixxxkeyboard.cpp'
4602--- mixxx/src/mixxxkeyboard.cpp 2012-12-24 10:58:09 +0000
4603+++ mixxx/src/mixxxkeyboard.cpp 2013-05-17 16:35:30 +0000
4604@@ -63,7 +63,7 @@
4605
4606 if (pConfigKey)
4607 {
4608- ControlObject::getControl(*pConfigKey)->queueFromMidi(MIDI_NOTE_ON, 1);
4609+ ControlObject::getControl(*pConfigKey)->setValueFromMidi(MIDI_NOTE_ON, 1);
4610 // Add key to active key list
4611 m_qActiveKeyList.append(QPair<int, ConfigKey *>(keyId,pConfigKey));
4612 return true;
4613@@ -86,7 +86,7 @@
4614 for (int i = m_qActiveKeyList.size() - 1; i >= 0; i--) {
4615 if (m_qActiveKeyList[i].first == keyId) {
4616 if(!autoRepeat) {
4617- ControlObject::getControl(*(m_qActiveKeyList[i].second))->queueFromMidi(MIDI_NOTE_OFF, 0);
4618+ ControlObject::getControl(*(m_qActiveKeyList[i].second))->setValueFromMidi(MIDI_NOTE_OFF, 0);
4619 m_qActiveKeyList.removeAt(i);
4620 }
4621 return true;
4622
4623=== modified file 'mixxx/src/playermanager.cpp'
4624--- mixxx/src/playermanager.cpp 2013-05-07 05:10:11 +0000
4625+++ mixxx/src/playermanager.cpp 2013-05-17 16:35:30 +0000
4626@@ -1,5 +1,6 @@
4627 // playermanager.cpp
4628 // Created 6/1/2010 by RJ Ryan (rryan@mit.edu)
4629+#include <QMutexLocker>
4630
4631 #include "playermanager.h"
4632
4633@@ -23,6 +24,7 @@
4634 SoundManager* pSoundManager,
4635 EngineMaster* pEngine,
4636 VinylControlManager* pVCManager) :
4637+ m_mutex(QMutex::Recursive),
4638 m_pConfig(pConfig),
4639 m_pSoundManager(pSoundManager),
4640 m_pEngine(pEngine),
4641@@ -35,11 +37,14 @@
4642 m_pCONumPreviewDecks(new ControlObject(ConfigKey("[Master]", "num_preview_decks"), true, true)) {
4643
4644 connect(m_pCONumDecks, SIGNAL(valueChanged(double)),
4645- this, SLOT(slotNumDecksControlChanged(double)));
4646+ this, SLOT(slotNumDecksControlChanged(double)),
4647+ Qt::DirectConnection);
4648 connect(m_pCONumSamplers, SIGNAL(valueChanged(double)),
4649- this, SLOT(slotNumSamplersControlChanged(double)));
4650+ this, SLOT(slotNumSamplersControlChanged(double)),
4651+ Qt::DirectConnection);
4652 connect(m_pCONumPreviewDecks, SIGNAL(valueChanged(double)),
4653- this, SLOT(slotNumPreviewDecksControlChanged(double)));
4654+ this, SLOT(slotNumPreviewDecksControlChanged(double)),
4655+ Qt::DirectConnection);
4656
4657 // This is parented to the PlayerManager so does not need to be deleted
4658 SamplerBank* pSamplerBank = new SamplerBank(this);
4659@@ -58,6 +63,7 @@
4660 }
4661
4662 PlayerManager::~PlayerManager() {
4663+ QMutexLocker locker(&m_mutex);
4664 // No need to delete anything because they are all parented to us and will
4665 // be destroyed when we are destroyed.
4666 m_players.clear();
4667@@ -73,6 +79,7 @@
4668 }
4669
4670 void PlayerManager::bindToLibrary(Library* pLibrary) {
4671+ QMutexLocker locker(&m_mutex);
4672 connect(pLibrary, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool)),
4673 this, SLOT(slotLoadTrackToPlayer(TrackPointer, QString, bool)));
4674 connect(pLibrary, SIGNAL(loadTrack(TrackPointer)),
4675@@ -159,57 +166,65 @@
4676 }
4677
4678 void PlayerManager::slotNumDecksControlChanged(double v) {
4679- // First off, undo any changes to the control.
4680- m_pCONumDecks->set(m_decks.size());
4681-
4682- int num = v;
4683+ QMutexLocker locker(&m_mutex);
4684+ int num = (int)v;
4685 if (num < m_decks.size()) {
4686+ // The request was invalid -- reset the value.
4687+ m_pCONumDecks->set(m_decks.size());
4688 qDebug() << "Ignoring request to reduce the number of decks to" << num;
4689 return;
4690 }
4691
4692 while (m_decks.size() < num) {
4693- addDeck();
4694+ addDeckInner();
4695 }
4696 }
4697
4698 void PlayerManager::slotNumSamplersControlChanged(double v) {
4699- // First off, undo any changes to the control.
4700- m_pCONumSamplers->set(m_samplers.size());
4701-
4702- int num = v;
4703+ QMutexLocker locker(&m_mutex);
4704+ int num = (int)v;
4705 if (num < m_samplers.size()) {
4706+ // The request was invalid -- reset the value.
4707+ m_pCONumSamplers->set(m_samplers.size());
4708 qDebug() << "Ignoring request to reduce the number of samplers to" << num;
4709 return;
4710 }
4711
4712 while (m_samplers.size() < num) {
4713- addSampler();
4714+ addSamplerInner();
4715 }
4716 }
4717
4718 void PlayerManager::slotNumPreviewDecksControlChanged(double v) {
4719- // First off, undo any changes to the control.
4720- m_pCONumPreviewDecks->set(m_preview_decks.size());
4721-
4722- int num = v;
4723+ QMutexLocker locker(&m_mutex);
4724+ int num = (int)v;
4725 if (num < m_preview_decks.size()) {
4726+ // The request was invalid -- reset the value.
4727+ m_pCONumPreviewDecks->set(m_preview_decks.size());
4728 qDebug() << "Ignoring request to reduce the number of preview decks to" << num;
4729 return;
4730 }
4731
4732 while (m_preview_decks.size() < num) {
4733- addPreviewDeck();
4734+ addPreviewDeckInner();
4735 }
4736 }
4737
4738-Deck* PlayerManager::addDeck() {
4739- QString group = groupForDeck(numDecks());
4740- int number = numDecks() + 1;
4741+void PlayerManager::addDeck() {
4742+ QMutexLocker locker(&m_mutex);
4743+ addDeckInner();
4744+ m_pCONumDecks->set((double)m_decks.count());
4745+}
4746+
4747+void PlayerManager::addDeckInner() {
4748+ // Do not lock m_mutex here.
4749+ QString group = groupForDeck(m_decks.count());
4750+ int number = m_decks.count() + 1;
4751
4752 EngineChannel::ChannelOrientation orientation = EngineChannel::LEFT;
4753- if (number % 2 == 0)
4754+ if (number % 2 == 0) {
4755 orientation = EngineChannel::RIGHT;
4756+ }
4757
4758 Deck* pDeck = new Deck(this, m_pConfig, m_pEngine, orientation, group);
4759 if (m_pAnalyserQueue) {
4760@@ -220,7 +235,6 @@
4761 Q_ASSERT(!m_players.contains(group));
4762 m_players[group] = pDeck;
4763 m_decks.append(pDeck);
4764- m_pCONumDecks->add(1);
4765
4766 // Register the deck output with SoundManager (deck is 0-indexed to SoundManager)
4767 m_pSoundManager->registerOutput(
4768@@ -237,12 +251,17 @@
4769 EngineDeck* pEngineDeck = pDeck->getEngineDeck();
4770 m_pSoundManager->registerInput(
4771 AudioInput(AudioInput::VINYLCONTROL, 0, number-1), pEngineDeck);
4772-
4773- return pDeck;
4774-}
4775-
4776-Sampler* PlayerManager::addSampler() {
4777- QString group = groupForSampler(numSamplers());
4778+}
4779+
4780+void PlayerManager::addSampler() {
4781+ QMutexLocker locker(&m_mutex);
4782+ addSamplerInner();
4783+ m_pCONumSamplers->set(m_samplers.count());
4784+}
4785+
4786+void PlayerManager::addSamplerInner() {
4787+ // Do not lock m_mutex here.
4788+ QString group = groupForSampler(m_samplers.count());
4789
4790 // All samplers are in the center
4791 EngineChannel::ChannelOrientation orientation = EngineChannel::CENTER;
4792@@ -256,13 +275,17 @@
4793 Q_ASSERT(!m_players.contains(group));
4794 m_players[group] = pSampler;
4795 m_samplers.append(pSampler);
4796- m_pCONumSamplers->add(1);
4797-
4798- return pSampler;
4799-}
4800-
4801-PreviewDeck* PlayerManager::addPreviewDeck() {
4802- QString group = groupForPreviewDeck(numPreviewDecks());
4803+}
4804+
4805+void PlayerManager::addPreviewDeck() {
4806+ QMutexLocker locker(&m_mutex);
4807+ addPreviewDeckInner();
4808+ m_pCONumPreviewDecks->set(m_preview_decks.count());
4809+}
4810+
4811+void PlayerManager::addPreviewDeckInner() {
4812+ // Do not lock m_mutex here.
4813+ QString group = groupForPreviewDeck(m_preview_decks.count());
4814
4815 // All preview decks are in the center
4816 EngineChannel::ChannelOrientation orientation = EngineChannel::CENTER;
4817@@ -276,11 +299,10 @@
4818 Q_ASSERT(!m_players.contains(group));
4819 m_players[group] = pPreviewDeck;
4820 m_preview_decks.append(pPreviewDeck);
4821- m_pCONumPreviewDecks->add(1);
4822- return pPreviewDeck;
4823 }
4824
4825 BaseTrackPlayer* PlayerManager::getPlayer(QString group) const {
4826+ QMutexLocker locker(&m_mutex);
4827 if (m_players.contains(group)) {
4828 return m_players[group];
4829 }
4830@@ -289,6 +311,7 @@
4831
4832
4833 Deck* PlayerManager::getDeck(unsigned int deck) const {
4834+ QMutexLocker locker(&m_mutex);
4835 if (deck < 1 || deck > numDecks()) {
4836 qWarning() << "Warning PlayerManager::getDeck() called with invalid index: "
4837 << deck;
4838@@ -298,6 +321,7 @@
4839 }
4840
4841 PreviewDeck* PlayerManager::getPreviewDeck(unsigned int libPreviewPlayer) const {
4842+ QMutexLocker locker(&m_mutex);
4843 if (libPreviewPlayer < 1 || libPreviewPlayer > numPreviewDecks()) {
4844 qWarning() << "Warning PlayerManager::getPreviewDeck() called with invalid index: "
4845 << libPreviewPlayer;
4846@@ -307,6 +331,7 @@
4847 }
4848
4849 Sampler* PlayerManager::getSampler(unsigned int sampler) const {
4850+ QMutexLocker locker(&m_mutex);
4851 if (sampler < 1 || sampler > numSamplers()) {
4852 qWarning() << "Warning PlayerManager::getSampler() called with invalid index: "
4853 << sampler;
4854@@ -316,6 +341,8 @@
4855 }
4856
4857 void PlayerManager::slotLoadTrackToPlayer(TrackPointer pTrack, QString group, bool play) {
4858+ // Do not lock mutex in this method unless it is changed to access
4859+ // PlayerManager state.
4860 BaseTrackPlayer* pPlayer = getPlayer(group);
4861
4862 if (pPlayer == NULL) {
4863@@ -344,31 +371,33 @@
4864 slotLoadToPlayer(location, groupForSampler(sampler-1));
4865 }
4866
4867-void PlayerManager::slotLoadTrackIntoNextAvailableDeck(TrackPointer pTrack)
4868-{
4869+void PlayerManager::slotLoadTrackIntoNextAvailableDeck(TrackPointer pTrack) {
4870+ QMutexLocker locker(&m_mutex);
4871 QList<Deck*>::iterator it = m_decks.begin();
4872 while (it != m_decks.end()) {
4873 Deck* pDeck = *it;
4874 ControlObject* playControl =
4875 ControlObject::getControl(ConfigKey(pDeck->getGroup(), "play"));
4876 if (playControl && playControl->get() != 1.) {
4877+ locker.unlock();
4878 pDeck->slotLoadTrack(pTrack, false);
4879- break;
4880+ return;
4881 }
4882 it++;
4883 }
4884 }
4885
4886-void PlayerManager::slotLoadTrackIntoNextAvailableSampler(TrackPointer pTrack)
4887-{
4888+void PlayerManager::slotLoadTrackIntoNextAvailableSampler(TrackPointer pTrack) {
4889+ QMutexLocker locker(&m_mutex);
4890 QList<Sampler*>::iterator it = m_samplers.begin();
4891 while (it != m_samplers.end()) {
4892 Sampler* pSampler = *it;
4893 ControlObject* playControl =
4894 ControlObject::getControl(ConfigKey(pSampler->getGroup(), "play"));
4895 if (playControl && playControl->get() != 1.) {
4896+ locker.unlock();
4897 pSampler->slotLoadTrack(pTrack, false);
4898- break;
4899+ return;
4900 }
4901 it++;
4902 }
4903
4904=== modified file 'mixxx/src/playermanager.h'
4905--- mixxx/src/playermanager.h 2013-04-29 21:05:20 +0000
4906+++ mixxx/src/playermanager.h 2013-05-17 16:35:30 +0000
4907@@ -5,6 +5,7 @@
4908 #define PLAYERMANAGER_H
4909
4910 #include <QList>
4911+#include <QMutex>
4912
4913 #include "configobject.h"
4914 #include "trackinfoobject.h"
4915@@ -32,13 +33,13 @@
4916 virtual ~PlayerManager();
4917
4918 // Add a deck to the PlayerManager
4919- Deck* addDeck();
4920+ void addDeck();
4921
4922 // Add a sampler to the PlayerManager
4923- Sampler* addSampler();
4924+ void addSampler();
4925
4926 // Add a PreviewDeck to the PlayerManager
4927- PreviewDeck* addPreviewDeck();
4928+ void addPreviewDeck();
4929
4930 // Return the number of players. Thread-safe.
4931 static unsigned int numDecks();
4932@@ -109,6 +110,19 @@
4933
4934 private:
4935 TrackPointer lookupTrack(QString location);
4936+ // Must hold m_mutex before calling this method. Internal method that
4937+ // creates a new deck.
4938+ void addDeckInner();
4939+ // Must hold m_mutex before calling this method. Internal method that
4940+ // creates a new sampler.
4941+ void addSamplerInner();
4942+ // Must hold m_mutex before calling this method. Internal method that
4943+ // creates a new preview deck.
4944+ void addPreviewDeckInner();
4945+
4946+ // Used to protect access to PlayerManager state across threads.
4947+ mutable QMutex m_mutex;
4948+
4949 ConfigObject<ConfigValue>* m_pConfig;
4950 SoundManager* m_pSoundManager;
4951 EngineMaster* m_pEngine;
4952
4953=== modified file 'mixxx/src/skin/legacyskinparser.cpp'
4954--- mixxx/src/skin/legacyskinparser.cpp 2013-05-01 12:42:34 +0000
4955+++ mixxx/src/skin/legacyskinparser.cpp 2013-05-17 16:35:30 +0000
4956@@ -272,16 +272,6 @@
4957 mainControl.slotSet(value);
4958 }
4959 }
4960- // Force a sync to deliver the control change messages so they take effect
4961- // before we start processing the skin. This is mostly just so that
4962- // additional decks/samplers are created before we start creating widgets
4963- // for them.
4964-
4965- // HACK(XXX) This relies on the fact that the PlayerManager listens to
4966- // changes to this control via a CO instead of a COTM. Otherwise the message
4967- // would not get delivered until the Qt event loop delivered the
4968- // message. rryan 10/2012
4969- ControlObject::sync();
4970
4971 ColorSchemeParser::setupLegacyColorSchemes(skinDocument, m_pConfig);
4972
4973
4974=== modified file 'mixxx/src/skin/propertybinder.cpp'
4975--- mixxx/src/skin/propertybinder.cpp 2013-01-04 16:07:55 +0000
4976+++ mixxx/src/skin/propertybinder.cpp 2013-05-17 16:35:30 +0000
4977@@ -34,6 +34,5 @@
4978 if (!m_pWidget->setProperty(propertyAscii.constData(), value)) {
4979 qDebug() << "Setting property" << m_propertyName << "to widget failed. Value:" << value;
4980 }
4981- m_pConfig->set(m_pControlThreadMain->getControlObject()->getKey(),
4982- QString::number(dValue));
4983+ m_pConfig->set(m_pControlThreadMain->getKey(), QString::number(dValue));
4984 }
4985
4986=== modified file 'mixxx/src/sounddeviceportaudio.cpp'
4987--- mixxx/src/sounddeviceportaudio.cpp 2013-03-21 22:29:13 +0000
4988+++ mixxx/src/sounddeviceportaudio.cpp 2013-05-17 16:35:30 +0000
4989@@ -167,7 +167,7 @@
4990 m_dSampleRate,
4991 m_framesPerBuffer,
4992 paClipOff, // Stream flags
4993- callback,
4994+ callback,
4995 (void*) this); // pointer passed to the callback function
4996
4997 if (err != paNoError)
4998@@ -329,7 +329,8 @@
4999 if (!m_undeflowUpdateCount) {
5000 if (statusFlags & (paOutputUnderflow | paInputOverflow)) {
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches