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

Proposed by Albert Santoni
Status: Merged
Merged at revision: 2794
Proposed branch: lp:~mixxxdevelopers/mixxx/features_spinny
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 904 lines (+617/-26)
18 files modified
mixxx/build/depends.py (+2/-0)
mixxx/src/dlgprefvinyl.cpp (+17/-2)
mixxx/src/dlgprefvinyl.h (+4/-9)
mixxx/src/engine/positionscratchcontroller.cpp (+1/-0)
mixxx/src/engine/vinylcontrolcontrol.cpp (+13/-0)
mixxx/src/engine/vinylcontrolcontrol.h (+1/-0)
mixxx/src/mathstuff.h (+1/-1)
mixxx/src/sharedglcontext.cpp (+23/-0)
mixxx/src/sharedglcontext.h (+15/-0)
mixxx/src/skin/legacyskinparser.cpp (+19/-0)
mixxx/src/skin/legacyskinparser.h (+1/-0)
mixxx/src/vinylcontrol/vinylcontrol.h (+3/-0)
mixxx/src/waveformviewerfactory.cpp (+2/-11)
mixxx/src/waveformviewerfactory.h (+0/-1)
mixxx/src/widget/wglwaveformviewer.cpp (+13/-2)
mixxx/src/widget/wglwaveformviewer.h (+1/-0)
mixxx/src/widget/wspinny.cpp (+434/-0)
mixxx/src/widget/wspinny.h (+67/-0)
To merge this branch: bzr merge lp:~mixxxdevelopers/mixxx/features_spinny
Reviewer Review Type Date Requested Status
RJ Skerry-Ryan Approve
Owen Williams Needs Fixing
Review via email: mp+60327@code.launchpad.net

Description of the change

Spinning vinyl widget, and factors out GL context creation into its own class. Also makes the waveform grab used the _closed_ hand icon. :)

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

* In SharedGLContext, are a constructor / destructor necessary? I don't think anything creates an instance, just uses the static methods.

* Check for PathBackground / PathForeground / PathGhost actually existing?

* WPixmapStore::getPixmap can return NULL if the pixmap doesn't exist. All uses of the member-variable pixmaps should be checked for non-NULL first

* QElapsedTimer, while much better than QTime, means we are ditching any platform <Qt 4.7 (e.g. Ubuntu 10.04 and earlier). I think it's a bit early to do that -- maybe stick to QTime for now?

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

* Qt::MiddleButton isn't compatible with <Qt 4.7 either, use Qt::MidButton instead

2775. By Albert Santoni

* Addressing RJ's code review:
    - Remove unnecessary SharedGLContext destructor (kept private constructor though).
    - Handle WSpinny math when track_samples or other params are NaN.
    - Protect all pixmap drawing in WSpinny against NULL pointers and pixmaps.
    - Fixes for Qt < 4.7 compatibility

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

k, fixed everything you pointed out in r2775 plus the ASSERTs Jus found when no tracks were loaded.

Thanks,
Albert

2776. By Albert Santoni

Squash WSpinny compiler warnings and my leftover qDebugs (oops)

2777. By Albert Santoni

* Merged from trunk

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

This widget will need to understand 33 vs 45 rpm record speeds and move the spinner at a higher speed if the record is set to 45RPM. Use [ChannelX],"vinylcontrol_speed_type", which is either "33.3 RPM" or "45 RPM".

Also, is the spinny thing guaranteed to show the same angle for the same sample index number? That's the important part :).

also, "roughly 0.5" is not acceptable. This value needs to be exact (5/9).

review: Needs Fixing
2778. By Albert Santoni

Use exact 33.3 RPM rotational speed in WSpinny

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

Hey Owen,

I just jammed that fraction in there to make the rotation more precise.

The good news is that the spinny thing is guaranteed to show the same
angle for the same sample index.

The bad news is that I can't monitor for changes of that ConfigKey
because it's a ConfigKey and not a ControlObject. (ie. WSpinny can't
tell when the vinyl control speed preferences get changed.) In the
past, we've solved this problem by duplicating the ConfigKey's value
across to a ControlObject. For example, the [Master],"samplerate"
ControlObject just mirrors a ConfigKey that's hooked into the prefs. I
guess I'll have to fix this that way?

Thanks,
Albert

On Mon, May 9, 2011 at 7:18 AM, Owen Williams <email address hidden> wrote:
> Review: Needs Fixing
> This widget will need to understand 33 vs 45 rpm record speeds and move the spinner at a higher speed if the record is set to 45RPM.  Use [ChannelX],"vinylcontrol_speed_type", which is either "33.3 RPM" or "45 RPM".
>
> Also, is the spinny thing guaranteed to show the same angle for the same sample index number?  That's the important part :).
>
> also, "roughly 0.5" is not acceptable.  This value needs to be exact (5/9).
> --
> https://code.launchpad.net/~mixxxdevelopers/mixxx/features_spinny/+merge/60327
> Your team Mixxx Development Team is subscribed to branch lp:mixxx.
>

--
Albert Santoni
Developer, Mixxx
http://www.mixxx.org

2779. By Albert Santoni

* WSpinny respects the vinyl control RPM setting now so it always matches your turntables' speed.
* Minor cleanup in DlgPrefVinyl.

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

Ok that all up in r2779. Should be good to go now and matches your vinyl control speed.

Albert

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

"Ok, *fixed* that all up in r2779.

> Ok that all up in r2779. Should be good to go now and matches your vinyl
> control speed.
>
> Albert

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

LGTM! thanks albert

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

> The bad news is that I can't monitor for changes of that ConfigKey
> because it's a ConfigKey and not a ControlObject. (ie. WSpinny can't
> tell when the vinyl control speed preferences get changed.) In the
> past, we've solved this problem by duplicating the ConfigKey's value
> across to a ControlObject. For example, the [Master],"samplerate"
> ControlObject just mirrors a ConfigKey that's hooked into the prefs. I
> guess I'll have to fix this that way?

We can either do that, or I can add a function in vinylcontrolproxy like getSpeed() that returns 1.0 for 33rpm, and 1.35 for 45rpm

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

Is there a skin / example xml that shows this widget in action? It looks like it wants a couple pixmaps

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

Jus is cooking up some examples -- my guess is they will show up in his 1.10
branch soon.

On Wed, May 11, 2011 at 2:09 PM, Owen Williams <email address hidden> wrote:

> Is there a skin / example xml that shows this widget in action? It looks
> like it wants a couple pixmaps
> --
>
> https://code.launchpad.net/~mixxxdevelopers/mixxx/features_spinny/+merge/60327
> You are reviewing the proposed merge of
> lp:~mixxxdevelopers/mixxx/features_spinny into lp:mixxx.
>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mixxx/build/depends.py'
2--- mixxx/build/depends.py 2011-04-28 21:54:22 +0000
3+++ mixxx/build/depends.py 2011-05-11 04:41:18 +0000
4@@ -414,6 +414,7 @@
5
6 "soundsource.cpp",
7
8+ "sharedglcontext.cpp",
9 "widget/wwidget.cpp",
10 "widget/wlabel.cpp",
11 "widget/wtracktext.cpp",
12@@ -428,6 +429,7 @@
13 "widget/wslider.cpp",
14 "widget/wstatuslight.cpp",
15 "widget/woverview.cpp",
16+ "widget/wspinny.cpp",
17 "widget/wskincolor.cpp",
18 "widget/wabstractcontrol.cpp",
19 "widget/wsearchlineedit.cpp",
20
21=== modified file 'mixxx/src/dlgprefvinyl.cpp'
22--- mixxx/src/dlgprefvinyl.cpp 2011-04-17 04:16:00 +0000
23+++ mixxx/src/dlgprefvinyl.cpp 2011-05-11 04:41:18 +0000
24@@ -30,7 +30,9 @@
25
26 DlgPrefVinyl::DlgPrefVinyl(QWidget * parent, VinylControlManager *pVCMan,
27 ConfigObject<ConfigValue> * _config) : QWidget(parent), Ui::DlgPrefVinylDlg(),
28- m_COTMode(ControlObject::getControl(ConfigKey("[VinylControl]", "mode")))
29+ m_COMode(ControlObject::getControl(ConfigKey("[VinylControl]", "mode"))),
30+ m_COSpeed1(ControlObject::getControl(ConfigKey("[Channel1]", "vinylcontrol_speed_type"))),
31+ m_COSpeed2(ControlObject::getControl(ConfigKey("[Channel2]", "vinylcontrol_speed_type")))
32 {
33 m_pVCManager = pVCMan;
34 config = _config;
35@@ -177,7 +179,7 @@
36
37 ControlObject::getControl(ConfigKey("[Channel1]", "vinylcontrol_mode"))->set(iMode);
38 ControlObject::getControl(ConfigKey("[Channel2]", "vinylcontrol_mode"))->set(iMode);
39- m_COTMode.slotSet(iMode);
40+ m_COMode.slotSet(iMode);
41 config->set(ConfigKey("[VinylControl]","mode"), ConfigValue(iMode));
42 config->set(ConfigKey("[VinylControl]","needle_skip_prevention" ), ConfigValue( (int)(NeedleSkipEnable->isChecked( )) ) );
43
44@@ -196,6 +198,19 @@
45 config->set(ConfigKey("[Channel2]","vinylcontrol_vinyl_type"), ConfigValue(ComboBoxVinylType2->currentText()));
46 config->set(ConfigKey("[Channel1]","vinylcontrol_speed_type"), ConfigValue(ComboBoxVinylSpeed1->currentText()));
47 config->set(ConfigKey("[Channel2]","vinylcontrol_speed_type"), ConfigValue(ComboBoxVinylSpeed2->currentText()));
48+
49+ //Save the vinylcontrol_speed_type in ControlObjects as well so it can be retrieved quickly
50+ //on the fly. (eg. WSpinny needs to know how fast to spin)
51+ if (ComboBoxVinylSpeed1->currentText() == MIXXX_VINYL_SPEED_33) {
52+ m_COSpeed1.slotSet(MIXXX_VINYL_SPEED_33_NUM);
53+ } else if (ComboBoxVinylSpeed1->currentText() == MIXXX_VINYL_SPEED_45) {
54+ m_COSpeed1.slotSet(MIXXX_VINYL_SPEED_45_NUM);
55+ }
56+ if (ComboBoxVinylSpeed2->currentText() == MIXXX_VINYL_SPEED_33) {
57+ m_COSpeed2.slotSet(MIXXX_VINYL_SPEED_33_NUM);
58+ } else if (ComboBoxVinylSpeed2->currentText() == MIXXX_VINYL_SPEED_45) {
59+ m_COSpeed2.slotSet(MIXXX_VINYL_SPEED_45_NUM);
60+ }
61 }
62
63 void DlgPrefVinyl::VinylGainSlotApply()
64
65=== modified file 'mixxx/src/dlgprefvinyl.h'
66--- mixxx/src/dlgprefvinyl.h 2011-04-17 04:16:00 +0000
67+++ mixxx/src/dlgprefvinyl.h 2011-05-11 04:41:18 +0000
68@@ -21,7 +21,7 @@
69 #include "ui_dlgprefvinyldlg.h"
70 #include "configobject.h"
71 #include "vinylcontrol/vinylcontrolsignalwidget.h"
72-#include "controlobjectthread.h"
73+#include "controlobjectthreadmain.h"
74
75 class QWidget;
76 class PlayerProxy;
77@@ -61,14 +61,9 @@
78 VinylControlManager* m_pVCManager;
79 /** Pointer to config object */
80 ConfigObject<ConfigValue> *config;
81- /** Indicates the strength of the timecode signal on each input */
82- ControlObjectThreadMain* m_timecodeQuality1;
83- ControlObjectThreadMain* m_timecodeQuality2;
84- ControlObjectThreadMain* m_vinylControlInput1L;
85- ControlObjectThreadMain* m_vinylControlInput1R;
86- ControlObjectThreadMain* m_vinylControlInput2L;
87- ControlObjectThreadMain* m_vinylControlInput2R;
88- ControlObjectThread m_COTMode;
89+ ControlObjectThreadMain m_COMode;
90+ ControlObjectThreadMain m_COSpeed1;
91+ ControlObjectThreadMain m_COSpeed2;
92 };
93
94 #endif
95
96=== modified file 'mixxx/src/engine/positionscratchcontroller.cpp'
97--- mixxx/src/engine/positionscratchcontroller.cpp 2011-04-26 23:57:28 +0000
98+++ mixxx/src/engine/positionscratchcontroller.cpp 2011-05-11 04:41:18 +0000
99@@ -113,6 +113,7 @@
100 m_bScratching = false;
101 m_iScratchTime = 0;
102 m_bEnableInertia = false;
103+ m_dRate = 0.;
104
105 //m_pVelocityController->setPID(0.2, 1.0, 5.0);
106 //m_pVelocityController->setPID(0.1, 0.0, 5.0);
107
108=== modified file 'mixxx/src/engine/vinylcontrolcontrol.cpp'
109--- mixxx/src/engine/vinylcontrolcontrol.cpp 2011-04-19 14:03:57 +0000
110+++ mixxx/src/engine/vinylcontrolcontrol.cpp 2011-05-11 04:41:18 +0000
111@@ -6,6 +6,19 @@
112 VinylControlControl::VinylControlControl(const char* pGroup, ConfigObject<ConfigValue>* pConfig)
113 : EngineControl(pGroup, pConfig) {
114 m_pControlVinylStatus = new ControlObject(ConfigKey(pGroup, "vinylcontrol_status"));
115+ m_pControlVinylSpeedType = new ControlObject(ConfigKey(pGroup, "vinylcontrol_speed_type"));
116+
117+ //Convert the ConfigKey's value into a double for the CO (for fast reads).
118+ QString strVinylSpeedType = pConfig->getValueString(ConfigKey(pGroup,
119+ "vinylcontrol_speed_type"));
120+ if (strVinylSpeedType == MIXXX_VINYL_SPEED_33) {
121+ m_pControlVinylSpeedType->set(MIXXX_VINYL_SPEED_33_NUM);
122+ } else if (strVinylSpeedType == MIXXX_VINYL_SPEED_45) {
123+ m_pControlVinylSpeedType->set(MIXXX_VINYL_SPEED_45_NUM);
124+ } else {
125+ m_pControlVinylSpeedType->set(MIXXX_VINYL_SPEED_33_NUM);
126+ }
127+
128 m_pControlVinylSeek = new ControlObject(ConfigKey(pGroup, "vinylcontrol_seek"));
129 connect(m_pControlVinylSeek, SIGNAL(valueChanged(double)),
130 this, SLOT(slotControlVinylSeek(double)),
131
132=== modified file 'mixxx/src/engine/vinylcontrolcontrol.h'
133--- mixxx/src/engine/vinylcontrolcontrol.h 2011-04-17 02:42:45 +0000
134+++ mixxx/src/engine/vinylcontrolcontrol.h 2011-05-11 04:41:18 +0000
135@@ -21,6 +21,7 @@
136
137 private:
138 ControlObject* m_pControlVinylSeek;
139+ ControlObject* m_pControlVinylSpeedType;
140 ControlObject* m_pControlVinylStatus;
141 ControlPushButton* m_pControlVinylMode;
142 ControlPushButton* m_pControlVinylEnabled;
143
144=== modified file 'mixxx/src/mathstuff.h'
145--- mixxx/src/mathstuff.h 2010-11-27 03:10:36 +0000
146+++ mixxx/src/mathstuff.h 2011-05-11 04:41:18 +0000
147@@ -23,7 +23,6 @@
148 #include <math.h>
149 #include <algorithm>
150
151-static CSAMPLE two_pi = (2.f*acos(-1.f));
152
153 CSAMPLE besseli(CSAMPLE);
154 int sign(CSAMPLE);
155@@ -44,6 +43,7 @@
156 float sigmoid_zero(double t, double max_t);
157
158 static CSAMPLE pi = acos(-1.0f);
159+static CSAMPLE two_pi = (2.f*acos(-1.f));
160
161 #ifdef _MSC_VER
162 #include <float.h> // for _isnan() on VC++
163
164=== added file 'mixxx/src/sharedglcontext.cpp'
165--- mixxx/src/sharedglcontext.cpp 1970-01-01 00:00:00 +0000
166+++ mixxx/src/sharedglcontext.cpp 2011-05-11 04:41:18 +0000
167@@ -0,0 +1,23 @@
168+#include <QGLContext>
169+#include <QGLFormat>
170+#include "sharedglcontext.h"
171+
172+/** Singleton wrapper around QGLContext */
173+
174+QGLContext* SharedGLContext::s_pSharedGLContext = (QGLContext*)NULL;
175+
176+QGLContext* SharedGLContext::getContext()
177+{
178+ QGLContext *ctxt;
179+
180+ if (s_pSharedGLContext == (QGLContext*)NULL) {
181+ s_pSharedGLContext = new QGLContext(QGLFormat(QGL::SampleBuffers));
182+ s_pSharedGLContext->create();
183+ s_pSharedGLContext->makeCurrent();
184+ }
185+
186+ ctxt = new QGLContext(QGLFormat(QGL::SampleBuffers));
187+ ctxt->create(s_pSharedGLContext);
188+
189+ return ctxt;
190+}
191
192=== added file 'mixxx/src/sharedglcontext.h'
193--- mixxx/src/sharedglcontext.h 1970-01-01 00:00:00 +0000
194+++ mixxx/src/sharedglcontext.h 2011-05-11 04:41:18 +0000
195@@ -0,0 +1,15 @@
196+#ifndef SHAREDGLCONTEXT_H_
197+#define SHAREDGLCONTEXT_H_
198+
199+class QGLContext;
200+
201+class SharedGLContext
202+{
203+ public:
204+ static QGLContext* getContext();
205+ private:
206+ SharedGLContext() { };
207+ static QGLContext* s_pSharedGLContext;
208+};
209+
210+#endif //SHAREDGLCONTEXT_H_
211
212=== modified file 'mixxx/src/skin/legacyskinparser.cpp'
213--- mixxx/src/skin/legacyskinparser.cpp 2011-04-24 06:00:11 +0000
214+++ mixxx/src/skin/legacyskinparser.cpp 2011-05-11 04:41:18 +0000
215@@ -39,6 +39,7 @@
216 #include "widget/wnumberpos.h"
217 #include "widget/wnumberrate.h"
218 #include "widget/woverview.h"
219+#include "widget/wspinny.h"
220
221 #include "widget/wvisualsimple.h"
222 #include "widget/wglwaveformviewer.h"
223@@ -264,6 +265,8 @@
224 return parseWidgetGroup(node);
225 } else if (nodeName == "Style") {
226 return parseStyle(node);
227+ } else if (nodeName == "Spinny") {
228+ return parseSpinny(node);
229 } else {
230 qDebug() << "Invalid node name in skin:" << nodeName;
231 }
232@@ -605,6 +608,22 @@
233 return p;
234 }
235
236+QWidget* LegacySkinParser::parseSpinny(QDomElement node) {
237+ QString channelStr = lookupNodeGroup(node);
238+ const char* pSafeChannelStr = safeChannelString(channelStr);
239+ WSpinny* p = new WSpinny(m_pParent);
240+ setupWidget(node, p);
241+
242+ connect(p, SIGNAL(trackDropped(QString, QString)),
243+ m_pPlayerManager, SLOT(slotLoadToPlayer(QString, QString)));
244+
245+ p->setup(node, pSafeChannelStr);
246+ setupConnections(node, p);
247+ p->installEventFilter(m_pKeyboard);
248+ return p;
249+}
250+
251+
252 QWidget* LegacySkinParser::parseTableView(QDomElement node) {
253 QStackedWidget* pTabWidget = new QStackedWidget(m_pParent);
254
255
256=== modified file 'mixxx/src/skin/legacyskinparser.h'
257--- mixxx/src/skin/legacyskinparser.h 2011-03-26 02:04:46 +0000
258+++ mixxx/src/skin/legacyskinparser.h 2011-05-11 04:41:18 +0000
259@@ -59,6 +59,7 @@
260 QWidget* parseKnob(QDomElement node);
261 QWidget* parseTableView(QDomElement node);
262 QWidget* parseStyle(QDomElement node);
263+ QWidget* parseSpinny(QDomElement node);
264
265 void setupPosition(QDomNode node, QWidget* pWidget);
266 void setupSize(QDomNode node, QWidget* pWidget);
267
268=== modified file 'mixxx/src/vinylcontrol/vinylcontrol.h'
269--- mixxx/src/vinylcontrol/vinylcontrol.h 2011-04-17 04:16:00 +0000
270+++ mixxx/src/vinylcontrol/vinylcontrol.h 2011-05-11 04:41:18 +0000
271@@ -21,6 +21,9 @@
272 #define MIXXX_VINYL_SPEED_33 "33.3 RPM"
273 #define MIXXX_VINYL_SPEED_45 "45 RPM"
274
275+#define MIXXX_VINYL_SPEED_33_NUM 33.3f
276+#define MIXXX_VINYL_SPEED_45_NUM 45.0f
277+
278 #define MIXXX_VCMODE_ABSOLUTE 0
279 #define MIXXX_VCMODE_RELATIVE 1
280 #define MIXXX_VCMODE_CONSTANT 2
281
282=== modified file 'mixxx/src/waveformviewerfactory.cpp'
283--- mixxx/src/waveformviewerfactory.cpp 2011-01-14 09:50:57 +0000
284+++ mixxx/src/waveformviewerfactory.cpp 2011-05-11 04:41:18 +0000
285@@ -5,6 +5,7 @@
286
287 #include "configobject.h"
288 #include "waveformviewerfactory.h"
289+#include "sharedglcontext.h"
290
291 #include "waveform/waveformrenderer.h"
292 #include "widget/wvisualsimple.h"
293@@ -17,21 +18,11 @@
294 QList<WWaveformViewer*> WaveformViewerFactory::m_visualViewers = QList<WWaveformViewer*>();
295 QList<WGLWaveformViewer*> WaveformViewerFactory::m_visualGLViewers = QList<WGLWaveformViewer*>();
296 QTimer WaveformViewerFactory::s_waveformUpdateTimer;;
297-QGLContext* WaveformViewerFactory::s_pSharedOGLCtxt = (QGLContext *)NULL;
298
299
300 WaveformViewerType WaveformViewerFactory::createWaveformViewer(const char *group, QWidget *parent, ConfigObject<ConfigValue> *pConfig, QWidget **target, WaveformRenderer* pWaveformRenderer) {
301
302- QGLContext *ctxt;
303-
304- if ( s_pSharedOGLCtxt == (QGLContext*)NULL ) {
305- s_pSharedOGLCtxt = new QGLContext(QGLFormat(QGL::SampleBuffers));
306- s_pSharedOGLCtxt->create();
307- s_pSharedOGLCtxt->makeCurrent();
308- }
309-
310- ctxt = new QGLContext(QGLFormat(QGL::SampleBuffers));
311- ctxt->create(s_pSharedOGLCtxt);
312+ QGLContext* ctxt = SharedGLContext::getContext();
313
314 qDebug() << "createWaveformViewer()";
315
316
317=== modified file 'mixxx/src/waveformviewerfactory.h'
318--- mixxx/src/waveformviewerfactory.h 2011-01-14 09:32:49 +0000
319+++ mixxx/src/waveformviewerfactory.h 2011-05-11 04:41:18 +0000
320@@ -34,7 +34,6 @@
321 static QList<WWaveformViewer*> m_visualViewers;
322 static QList<WGLWaveformViewer*> m_visualGLViewers;
323 static QTimer s_waveformUpdateTimer;
324- static QGLContext *s_pSharedOGLCtxt;
325
326 public:
327 static WaveformViewerType createWaveformViewer(const char* group, QWidget *pParent, ConfigObject<ConfigValue> *pConfig, QWidget **target, WaveformRenderer *pWaveformRenderer);
328
329=== modified file 'mixxx/src/widget/wglwaveformviewer.cpp'
330--- mixxx/src/widget/wglwaveformviewer.cpp 2011-04-26 02:43:58 +0000
331+++ mixxx/src/widget/wglwaveformviewer.cpp 2011-05-11 04:41:18 +0000
332@@ -13,6 +13,7 @@
333 #include "wglwaveformviewer.h"
334 #include "waveform/waveformrenderer.h"
335 #include "controlobjectthreadmain.h"
336+#include "sharedglcontext.h"
337
338 WGLWaveformViewer::WGLWaveformViewer(
339 const char *group,
340@@ -24,7 +25,6 @@
341 ) :
342 QGLWidget(ctxt, pParent, pShareWidget)
343 {
344-
345 m_pWaveformRenderer = pWaveformRenderer;
346 Q_ASSERT(m_pWaveformRenderer);
347
348@@ -46,6 +46,9 @@
349 installEventFilter(this);
350
351 m_painting = false;
352+
353+ setSizePolicy(QSizePolicy::MinimumExpanding,
354+ QSizePolicy::MinimumExpanding);
355 }
356
357 bool WGLWaveformViewer::directRendering()
358@@ -63,6 +66,14 @@
359 m_pWaveformRenderer->resize(w, h);
360 }
361
362+void WGLWaveformViewer::resizeEvent(QResizeEvent* e)
363+{
364+ const QSize newSize = e->size();
365+ m_pWaveformRenderer->resize(newSize.width(),
366+ newSize.height());
367+}
368+
369+
370 void WGLWaveformViewer::paintEvent(QPaintEvent *event) {
371 QPainter painter;
372 painter.begin(this);
373@@ -110,7 +121,7 @@
374 //qDebug() << "m_dInitialPlaypos" << m_dInitialPlaypos;
375
376 // Set the cursor to a hand while the mouse is down.
377- setCursor(Qt::OpenHandCursor);
378+ setCursor(Qt::ClosedHandCursor);
379 }
380 } else if(e->type() == QEvent::MouseMove) {
381 // Only send signals for mouse moving if the left button is pressed
382
383=== modified file 'mixxx/src/widget/wglwaveformviewer.h'
384--- mixxx/src/widget/wglwaveformviewer.h 2011-04-26 02:30:22 +0000
385+++ mixxx/src/widget/wglwaveformviewer.h 2011-05-11 04:41:18 +0000
386@@ -47,6 +47,7 @@
387
388 protected:
389 void paintEvent(QPaintEvent* event);
390+ void resizeEvent(QResizeEvent* e);
391
392 private:
393 /** Used in mouse event handler */
394
395=== added file 'mixxx/src/widget/wspinny.cpp'
396--- mixxx/src/widget/wspinny.cpp 1970-01-01 00:00:00 +0000
397+++ mixxx/src/widget/wspinny.cpp 2011-05-11 04:41:18 +0000
398@@ -0,0 +1,434 @@
399+#include <math.h>
400+#include "mathstuff.h"
401+#include "wpixmapstore.h"
402+#include "controlobject.h"
403+#include "controlobjectthreadmain.h"
404+#include "sharedglcontext.h"
405+#include "wspinny.h"
406+
407+WSpinny::WSpinny(QWidget* parent) : QGLWidget(SharedGLContext::getContext(), parent),
408+ m_pBG(NULL),
409+ m_pFG(NULL),
410+ m_pGhost(NULL),
411+ m_pPlay(NULL),
412+ m_pPlayPos(NULL),
413+ m_pVisualPlayPos(NULL),
414+ m_pDuration(NULL),
415+ m_pTrackSamples(NULL),
416+ m_pBPM(NULL),
417+ m_pScratch(NULL),
418+ m_pScratchToggle(NULL),
419+ m_pScratchPos(NULL),
420+ m_pVinylControlSpeedType(NULL),
421+ m_fAngle(0.0f),
422+ m_fGhostAngle(0.0f),
423+ m_dPausedPosition(0.0f),
424+ m_bGhostPlayback(false),
425+ m_iStartMouseX(-1),
426+ m_iStartMouseY(-1),
427+ m_iFullRotations(0),
428+ m_dPrevTheta(0.)
429+{
430+ //Drag and drop
431+ setAcceptDrops(true);
432+}
433+
434+WSpinny::~WSpinny()
435+{
436+ //Don't delete these because the pixmap store takes care of them.
437+ //delete m_pBG;
438+ //delete m_pFG;
439+ //delete m_pGhost;
440+ WPixmapStore::deletePixmap(m_pBG);
441+ WPixmapStore::deletePixmap(m_pFG);
442+ WPixmapStore::deletePixmap(m_pGhost);
443+ delete m_pPlay;
444+ delete m_pPlayPos;
445+ delete m_pVisualPlayPos;
446+ delete m_pDuration;
447+ delete m_pTrackSamples;
448+ delete m_pTrackSampleRate;
449+ delete m_pBPM;
450+ delete m_pScratch;
451+ delete m_pScratchToggle;
452+ delete m_pScratchPos;
453+ delete m_pVinylControlSpeedType;
454+}
455+
456+void WSpinny::setup(QDomNode node, QString group)
457+{
458+ m_group = group;
459+
460+ // Set pixmaps
461+ m_pBG = WPixmapStore::getPixmap(WWidget::getPath(WWidget::selectNodeQString(node,
462+ "PathBackground")));
463+ m_pFG = WPixmapStore::getPixmap(WWidget::getPath(WWidget::selectNodeQString(node,
464+ "PathForeground")));
465+ m_pGhost = WPixmapStore::getPixmap(WWidget::getPath(WWidget::selectNodeQString(node,
466+ "PathGhost")));
467+ if (m_pBG && !m_pBG->isNull()) {
468+ setFixedSize(m_pBG->size());
469+ }
470+
471+ m_pPlay = new ControlObjectThreadMain(ControlObject::getControl(
472+ ConfigKey(group, "play")));
473+ m_pPlayPos = new ControlObjectThreadMain(ControlObject::getControl(
474+ ConfigKey(group, "playposition")));
475+ m_pVisualPlayPos = new ControlObjectThreadMain(ControlObject::getControl(
476+ ConfigKey(group, "visual_playposition")));
477+ m_pDuration = new ControlObjectThreadMain(ControlObject::getControl(
478+ ConfigKey(group, "duration")));
479+ m_pTrackSamples = new ControlObjectThreadMain(ControlObject::getControl(
480+ ConfigKey(group, "track_samples")));
481+ m_pTrackSampleRate = new ControlObjectThreadMain(
482+ ControlObject::getControl(
483+ ConfigKey(group, "track_samplerate")));
484+ m_pBPM = new ControlObjectThreadMain(ControlObject::getControl(
485+ ConfigKey(group, "bpm")));
486+
487+ m_pScratch = new ControlObjectThreadMain(ControlObject::getControl(
488+ ConfigKey(group, "scratch2")));
489+ m_pScratchToggle = new ControlObjectThreadMain(ControlObject::getControl(
490+ ConfigKey(group, "scratch_position_enable")));
491+ m_pScratchPos = new ControlObjectThreadMain(ControlObject::getControl(
492+ ConfigKey(group, "scratch_position")));
493+ m_pVinylControlSpeedType = new ControlObjectThreadMain(ControlObject::getControl(
494+ ConfigKey(group, "vinylcontrol_speed_type")));
495+ if (m_pVinylControlSpeedType)
496+ {
497+ //Initialize the rotational speed.
498+ this->updateVinylControlSpeed(m_pVinylControlSpeedType->get());
499+ }
500+ Q_ASSERT(m_pPlayPos);
501+ Q_ASSERT(m_pDuration);
502+
503+ //Repaint when visual_playposition changes.
504+ connect(m_pVisualPlayPos, SIGNAL(valueChanged(double)),
505+ this, SLOT(updateAngle(double)));
506+
507+ //Match the vinyl control's set RPM so that the spinny widget rotates at the same
508+ //speed as your physical decks, if you're using vinyl control.
509+ connect(m_pVinylControlSpeedType, SIGNAL(valueChanged(double)),
510+ this, SLOT(updateVinylControlSpeed(double)));
511+}
512+
513+void WSpinny::paintEvent(QPaintEvent *e)
514+{
515+ Q_UNUSED(e); //ditch unused param warning
516+
517+ QPainter p(this);
518+
519+ if (m_pBG) {
520+ p.drawPixmap(0, 0, *m_pBG);
521+ }
522+
523+ //To rotate the foreground pixmap around the center of the image,
524+ //we use the classic trick of translating the coordinate system such that
525+ //the origin is at the center of the image. We then rotate the coordinate system,
526+ //and draw the pixmap at the corner.
527+ p.translate(width() / 2, height() / 2);
528+
529+ if (m_bGhostPlayback)
530+ p.save();
531+
532+ if (m_pFG && !m_pFG->isNull()) {
533+ //Now rotate the pixmap and draw it on the screen.
534+ p.rotate(m_fAngle);
535+ p.drawPixmap(-(width() / 2), -(height() / 2), *m_pFG);
536+ }
537+
538+ if (m_bGhostPlayback && m_pGhost && !m_pGhost->isNull())
539+ {
540+ p.restore();
541+ p.save();
542+ p.rotate(m_fGhostAngle);
543+ p.drawPixmap(-(width() / 2), -(height() / 2), *m_pGhost);
544+
545+ //Rotate back to the playback position (not the ghost positon),
546+ //and draw the beat marks from there.
547+ p.restore();
548+
549+ /*
550+ //Draw a line where the next 4 beats are
551+ double bpm = m_pBPM->get();
552+ double duration = m_pDuration->get();
553+ if (bpm <= 0. || duration <= 0.) {
554+ return; //Prevent div by zero
555+ }
556+ double beatLengthInSec = 60. / bpm;
557+ double beatLengthNormalized = beatLengthInSec / duration; //Noramlized to duration
558+ double beatAngle = calculateAngle(beatLengthNormalized);
559+ //qDebug() << "beatAngle:" << beatAngle;
560+ //qDebug() << "beatLenInSec:" << beatLengthInSec << "norm:" << beatLengthNormalized;
561+ p.rotate(m_fAngle);
562+ for (int i = 0; i < 4; i++) {
563+ QLineF beatLine(-(width()*0.6 / 2), -(height()*0.6 / 2),
564+ -(width()*0.8 / 2), -(height()*0.8 / 2));
565+ //p.drawPoint(-(width()*0.5 / 2), -(height()*0.5 / 2));
566+ p.drawLine(beatLine);
567+ p.rotate(beatAngle);
568+ } */
569+ }
570+}
571+
572+/* Convert between a normalized playback position (0.0 - 1.0) and an angle
573+ in our polar coordinate system.
574+ Returns an angle clamped between -180 and 180 degrees. */
575+double WSpinny::calculateAngle(double playpos)
576+{
577+ if (isnan(playpos))
578+ return 0.0f;
579+
580+ //Convert playpos to seconds.
581+ //double t = playpos * m_pDuration->get();
582+ double t = playpos * (m_pTrackSamples->get()/2 / // Stereo audio!
583+ m_pTrackSampleRate->get());
584+
585+ if (isnan(t)) //Bad samplerate or number of track samples.
586+ return 0.0f;
587+
588+ //33 RPM is approx. 0.5 rotations per second.
589+ double angle = 360*m_dRotationsPerSecond*t;
590+ //Clamp within -180 and 180 degrees
591+ //qDebug() << "pc:" << angle;
592+ //angle = ((angle + 180) % 360.) - 180;
593+ //modulo for doubles :)
594+ if (angle > 0)
595+ {
596+ int x = (angle+180)/360;
597+ angle = angle - (360*x);
598+ } else
599+ {
600+ int x = (angle-180)/360;
601+ angle = angle - (360*x);
602+ }
603+
604+ Q_ASSERT(angle <= 180 && angle >= -180);
605+ return angle;
606+}
607+
608+/** Given a normalized playpos, calculate the integer number of rotations
609+ that it would take to wind the vinyl to that position. */
610+int WSpinny::calculateFullRotations(double playpos)
611+{
612+ if (isnan(playpos))
613+ return 0.0f;
614+ //Convert playpos to seconds.
615+ //double t = playpos * m_pDuration->get();
616+ double t = playpos * (m_pTrackSamples->get()/2 / // Stereo audio!
617+ m_pTrackSampleRate->get());
618+
619+ //33 RPM is approx. 0.5 rotations per second.
620+ //qDebug() << t;
621+ double angle = 360*m_dRotationsPerSecond*t;
622+
623+ return (((int)angle+180) / 360);
624+}
625+
626+//Inverse of calculateAngle()
627+double WSpinny::calculatePositionFromAngle(double angle)
628+{
629+ if (isnan(angle))
630+ return 0.0f;
631+
632+ //33 RPM is approx. 0.5 rotations per second.
633+ double t = angle/(360*m_dRotationsPerSecond); //time in seconds
634+
635+ //Convert t from seconds into a normalized playposition value.
636+ //double playpos = t / m_pDuration->get();
637+ double playpos = t / (m_pTrackSamples->get()/2 / // Stereo audio!
638+ m_pTrackSampleRate->get());
639+ return playpos;
640+}
641+
642+/** Update the playback angle saved in the widget and repaint.
643+ @param playpos A normalized (0.0-1.0) playback position. (Not an angle!)
644+*/
645+void WSpinny::updateAngle(double playpos)
646+{
647+ m_fAngle = calculateAngle(playpos);
648+ update();
649+}
650+
651+//Update the angle using the ghost playback position.
652+void WSpinny::updateAngleForGhost()
653+{
654+ qint64 elapsed = m_time.elapsed();
655+ double duration = m_pDuration->get();
656+ double newPlayPos = m_dPausedPosition +
657+ (((double)elapsed)/1000.)/duration;
658+ m_fGhostAngle = calculateAngle(newPlayPos);
659+ update();
660+}
661+
662+void WSpinny::updateVinylControlSpeed(double rpm)
663+{
664+ m_dRotationsPerSecond = rpm/60.;
665+}
666+
667+void WSpinny::mouseMoveEvent(QMouseEvent * e)
668+{
669+ int y = e->y();
670+ int x = e->x();
671+
672+ //Keeping these around in case we want to switch to control relative
673+ //to the original mouse position.
674+ //int dX = x-m_iStartMouseX;
675+ //int dY = y-m_iStartMouseY;
676+
677+ //Coordinates from center of widget
678+ int c_x = x - width()/2;
679+ int c_y = y - height()/2;
680+ double theta = (180.0f/M_PI)*atan2(c_x, -c_y);
681+
682+ //qDebug() << "c_x:" << c_x << "c_y:" << c_y <<
683+ // "dX:" << dX << "dY:" << dY;
684+
685+ //When we finish one full rotation (clockwise or anticlockwise),
686+ //we'll need to manually add/sub 360 degrees because atan2()'s range is
687+ //only within -180 to 180 degrees. We need a wider range so your position
688+ //in the song can be tracked.
689+ if (m_dPrevTheta > 100 && theta < 0) {
690+ m_iFullRotations++;
691+ }
692+ else if (m_dPrevTheta < -100 && theta > 0) {
693+ m_iFullRotations--;
694+ }
695+
696+ m_dPrevTheta = theta;
697+ theta += m_iFullRotations*360;
698+
699+ //qDebug() << "c t:" << theta << "pt:" << m_dPrevTheta <<
700+ // "icr" << m_iFullRotations;
701+
702+ if (e->buttons() & Qt::LeftButton)
703+ {
704+ //Convert deltaTheta into a percentage of song length.
705+ double absPos = calculatePositionFromAngle(theta);
706+
707+ double absPosInSamples = absPos * m_pTrackSamples->get();
708+ m_pScratchPos->slotSet(absPosInSamples);
709+ }
710+ else if (e->buttons() & Qt::MidButton)
711+ {
712+ }
713+ else if (e->buttons() & Qt::NoButton)
714+ {
715+ setCursor(QCursor(Qt::OpenHandCursor));
716+ }
717+}
718+
719+void WSpinny::mousePressEvent(QMouseEvent * e)
720+{
721+ int y = e->y();
722+ int x = e->x();
723+
724+ m_iStartMouseX = x;
725+ m_iStartMouseY = y;
726+
727+ if (e->button() == Qt::LeftButton)
728+ {
729+ QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
730+
731+ double initialPosInSamples = m_pPlayPos->get() * m_pTrackSamples->get();
732+ m_pScratchPos->slotSet(initialPosInSamples);
733+ m_pScratchToggle->slotSet(1.0f);
734+
735+ m_iFullRotations = calculateFullRotations(m_pPlayPos->get());
736+
737+ m_dPrevTheta = calculateAngle(m_pPlayPos->get());
738+
739+ //Trigger a mouse move to immediately line up the vinyl with the cursor
740+ mouseMoveEvent(e);
741+ }
742+ else if (e->button() == Qt::MidButton)
743+ {
744+ }
745+ else if (e->button() == Qt::RightButton)
746+ {
747+ //Stop playback and start the timer for ghost playback
748+ m_time.start();
749+ m_dPausedPosition = m_pPlayPos->get();
750+ updateAngleForGhost(); //Need to recalc the ghost angle right away
751+ m_bGhostPlayback = true;
752+ m_ghostPaintTimer.start(30);
753+ connect(&m_ghostPaintTimer, SIGNAL(timeout()),
754+ this, SLOT(updateAngleForGhost()));
755+
756+ //TODO: Ramp down (brake) over a period of 1 beat
757+ // instead? Would be sweet.
758+ m_pPlay->slotSet(0.0f);
759+ }
760+}
761+
762+void WSpinny::mouseReleaseEvent(QMouseEvent * e)
763+{
764+ if (e->button() == Qt::LeftButton)
765+ {
766+ QApplication::restoreOverrideCursor();
767+ m_pScratchToggle->slotSet(0.0f);
768+ m_iFullRotations = 0;
769+ }
770+ else if (e->button() == Qt::RightButton)
771+ {
772+ //Start playback by jumping forwards in the song as if playback
773+ //was never paused. (useful for bleeping or adding silence breaks)
774+ qint64 elapsed = m_time.elapsed();
775+ //qDebug() << "elapsed:" << elapsed;
776+ m_ghostPaintTimer.stop();
777+ m_bGhostPlayback = false;
778+
779+ //Convert elapsed to seconds, then normalize it to the duration so we can
780+ //move the playback position ahead by the elapsed amount.
781+ double duration = m_pDuration->get();
782+ double newPlayPos = m_dPausedPosition + (((double)elapsed)/1000.)/duration;
783+ //qDebug() << m_dPausedPosition << newPlayPos;
784+ m_pPlay->slotSet(1.0f);
785+ m_pPlayPos->slotSet(newPlayPos);
786+ //m_bRightButtonPressed = true;
787+ }
788+}
789+
790+void WSpinny::wheelEvent(QWheelEvent *e)
791+{
792+ Q_UNUSED(e); //ditch unused param warning
793+
794+ /*
795+ double wheelDirection = ((QWheelEvent *)e)->delta() / 120.;
796+ double newValue = getValue() + (wheelDirection);
797+ this->updateValue(newValue);
798+
799+ e->accept();
800+ */
801+}
802+
803+/** DRAG AND DROP **/
804+void WSpinny::dragEnterEvent(QDragEnterEvent * event)
805+{
806+ // Accept the enter event if the thing is a filepath and nothing's playing
807+ // in this deck.
808+ if (event->mimeData()->hasUrls()) {
809+ if (m_pPlay && m_pPlay->get()) {
810+ event->ignore();
811+ } else {
812+ event->acceptProposedAction();
813+ }
814+ }
815+}
816+
817+void WSpinny::dropEvent(QDropEvent * event)
818+{
819+ if (event->mimeData()->hasUrls()) {
820+ QList<QUrl> urls(event->mimeData()->urls());
821+ QUrl url = urls.first();
822+ QString name = url.toLocalFile();
823+ //If the file is on a network share, try just converting the URL to a string...
824+ if (name == "")
825+ name = url.toString();
826+
827+ event->accept();
828+ emit(trackDropped(name, m_group));
829+ } else {
830+ event->ignore();
831+ }
832+}
833
834=== added file 'mixxx/src/widget/wspinny.h'
835--- mixxx/src/widget/wspinny.h 1970-01-01 00:00:00 +0000
836+++ mixxx/src/widget/wspinny.h 2011-05-11 04:41:18 +0000
837@@ -0,0 +1,67 @@
838+
839+#ifndef _WSPINNY_H
840+#define _WSPINNY_H
841+
842+#include <QGLWidget>
843+#include "wwidget.h"
844+
845+class ControlObjectThreadMain;
846+
847+class WSpinny : public QGLWidget
848+{
849+ Q_OBJECT
850+ public:
851+ WSpinny(QWidget* parent);
852+ ~WSpinny();
853+ void setup(QDomNode node, QString group);
854+ void dragEnterEvent(QDragEnterEvent *event);
855+ void dropEvent(QDropEvent *event);
856+ public slots:
857+ void updateAngle(double);
858+ void updateAngleForGhost();
859+ void updateVinylControlSpeed(double rpm);
860+ signals:
861+ void trackDropped(QString filename, QString group);
862+ protected:
863+ //QWidget:
864+ void paintEvent(QPaintEvent*);
865+ void mouseMoveEvent(QMouseEvent * e);
866+ void mousePressEvent(QMouseEvent * e);
867+ void mouseReleaseEvent(QMouseEvent * e);
868+ void wheelEvent(QWheelEvent *e);
869+
870+ double calculateAngle(double playpos);
871+ int calculateFullRotations(double playpos);
872+ double calculatePositionFromAngle(double angle);
873+ private:
874+ QPixmap* m_pBG;
875+ QPixmap* m_pFG;
876+ QPixmap* m_pGhost;
877+ ControlObjectThreadMain* m_pPlay;
878+ ControlObjectThreadMain* m_pPlayPos;
879+ ControlObjectThreadMain* m_pVisualPlayPos;
880+ ControlObjectThreadMain* m_pDuration;
881+ ControlObjectThreadMain* m_pTrackSamples;
882+ ControlObjectThreadMain* m_pTrackSampleRate;
883+ ControlObjectThreadMain* m_pBPM;
884+ ControlObjectThreadMain* m_pScratch;
885+ ControlObjectThreadMain* m_pScratchToggle;
886+ ControlObjectThreadMain* m_pScratchPos;
887+ ControlObjectThreadMain* m_pVinylControlSpeedType;
888+ QString m_group;
889+ float m_fAngle; //Degrees
890+ float m_fGhostAngle;
891+ QTime m_time;
892+ double m_dPausedPosition;
893+ bool m_bGhostPlayback;
894+ QTimer m_ghostPaintTimer;
895+ int m_iStartMouseX;
896+ int m_iStartMouseY;
897+ int m_iFullRotations;
898+ double m_dPrevTheta;
899+ double m_dTheta;
900+ /** Speed of the vinyl rotation. */
901+ double m_dRotationsPerSecond;
902+};
903+
904+#endif //_WSPINNY_H

Subscribers

People subscribed via source and target branches