Merge lp:~mixxxdevelopers/mixxx/atomic-co into lp:~mixxxdevelopers/mixxx/trunk
- atomic-co
- Merge into trunk
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 |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Schürmann | Needs Fixing | ||
RJ Skerry-Ryan | Needs Fixing | ||
Review via email:
|
Commit message
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://
The core change was made in http://
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.
- 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
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RJ Skerry-Ryan (rryan) wrote : | # |
- 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 ControlObjectRi
ngValue: :tryGet( ) - 3358. By Daniel Schürmann
-
added more comments
- 3359. By Daniel Schürmann
-
cRingSize added + an assertion to check for valid values
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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
> * ControlObjectRi
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
William Good (bkgood) wrote : | # |
There appear to be some issues with class ControlObjectVa
First, assuming sizeof(double) <= sizeof(void*) (which is the case on my machine), this class is used for ControlObjectVa
QAtomicPoin
double x = 1.5;
qap = reinterpret_
void *qap_val = qap;
double y = *reinterpret_
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/
Along these lines:
return reinterpret_
As far as I can tell, this is actually trying to cast the QAtomicPointer<
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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_
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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 valueChangedFro
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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*
> 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+
When cReaderCnt+1 concurrent writes are occurring, then a reader will be spin-locked.
When cReaderCnt*
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?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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(
T __declspec(
This is a nice thread:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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 valueChangedFro
> 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
> valueChangedFro
>
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RJ Skerry-Ryan (rryan) wrote : | # |
Also, I just noticed the change to ControlObjectTh
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.
- 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
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RJ Skerry-Ryan (rryan) wrote : | # |
I added COTM main-thread proxying back in lp:~rryan/mixxx/atomic-co.
- 3363. By Daniel Schürmann
-
merged from lp:~rryan/mixxx/atomic-co
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Daniel Schürmann (daschuer) wrote : | # |
Here some results:
* I think the CO is registered twice: In ControlObject:
* it is not clear where the home of m_sqCOHash is, for me it should move to ControlDoublePr
* all static functions should have the leading comment // static
* control.cpp/h should be renamed to controldoublepr
* DRY violation in overloaded 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 valueChangedByT
For this propose we should implement a new proxy which sends out pSender, or allow to use ControlDoublePr
please not the merge request with additional changes
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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:
and in ControlDoublePr
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 controldoublepr
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:
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
valueChangedByT
receive valueChangedByT
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 ControlDoublePr
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:/
> You are reviewing the proposed merge of
lp:~mixxxdevelopers/mixxx/atomic-co into lp:mixxx.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Daniel Schürmann (daschuer) wrote : | # |
> > * i do not like to distingish between valueChanged(v) and
> valueChangedByT
> receive valueChangedByT
>
> 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 ControlDoublePr
>
> 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.
- 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
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)) { |
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 controlobjectba se.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 controlobjectba se.h. This is such a crucial file it needs plenty of documentation for future developers :).
* ControlObjectRi ngValue: :tryGet( ) fetchAndAddAcqu ire(-1) ; fetchAndAddAcqu ire(-1) <= 0;
- why return an int when it's functionally a bool (i.e. was it successful)
- bool originalSlots = m_readerSlots.
Should instead be:
bool originalSlots = m_readerSlots.
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?
* ControlObjectVa lue::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().