~mixxxdevelopers/mixxx/engine-control-refactor

« back to all changes in this revision

Viewing changes to mixxx/src/widget/wspinny.cpp

  • Committer: RJ Ryan
  • Date: 2013-06-04 00:41:29 UTC
  • mfrom: (2890.22.101 mixxx)
  • Revision ID: rryan@mixxx.org-20130604004129-8jjxkicsb3givu4a
MergingĀ fromĀ lp:mixxx.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#include <math.h>
 
2
 
 
3
#include <QtDebug>
 
4
 
2
5
#include "mathstuff.h"
3
 
#include "wpixmapstore.h"
 
6
#include "wimagestore.h"
4
7
#include "controlobject.h"
5
8
#include "controlobjectthreadmain.h"
6
9
#include "sharedglcontext.h"
7
 
#include "wspinny.h"
 
10
#include "widget/wspinny.h"
 
11
#include "vinylcontrol/vinylcontrolmanager.h"
 
12
#include "vinylcontrol/vinylcontrol.h"
8
13
 
9
14
WSpinny::WSpinny(QWidget* parent, VinylControlManager* pVCMan)
10
 
        : QGLWidget(SharedGLContext::getContext(), parent),
11
 
          m_pBG(NULL),
12
 
          m_pFG(NULL),
13
 
          m_pGhost(NULL),
 
15
        : QGLWidget(parent, SharedGLContext::getWidget()),
 
16
          m_pBgImage(NULL),
 
17
          m_pFgImage(NULL),
 
18
          m_pGhostImage(NULL),
14
19
          m_pPlay(NULL),
15
20
          m_pPlayPos(NULL),
16
21
          m_pVisualPlayPos(NULL),
17
22
          m_pDuration(NULL),
18
23
          m_pTrackSamples(NULL),
19
 
          m_pBPM(NULL),
20
24
          m_pScratch(NULL),
21
25
          m_pScratchToggle(NULL),
22
26
          m_pScratchPos(NULL),
23
27
          m_pVinylControlSpeedType(NULL),
24
28
          m_pVinylControlEnabled(NULL),
 
29
          m_iVinylInput(-1),
25
30
          m_bVinylActive(false),
26
31
          m_bSignalActive(true),
27
 
          m_iSize(0),
28
 
          m_iSignalUpdateTick(0),
 
32
          m_iVinylScopeSize(0),
29
33
          m_fAngle(0.0f),
 
34
          m_dAngleLastPlaypos(-1),
30
35
          m_fGhostAngle(0.0f),
 
36
          m_dGhostAngleLastPlaypos(-1),
31
37
          m_iStartMouseX(-1),
32
38
          m_iStartMouseY(-1),
33
39
          m_iFullRotations(0),
34
 
          m_dPrevTheta(0.) {
 
40
          m_dPrevTheta(0.),
 
41
          m_bClampFailedWarning(false) {
35
42
#ifdef __VINYLCONTROL__
36
43
    m_pVCManager = pVCMan;
37
 
    m_pVinylControl = NULL;
38
44
#endif
39
45
    //Drag and drop
40
46
    setAcceptDrops(true);
41
 
}
42
 
 
43
 
WSpinny::~WSpinny()
44
 
{
45
 
    //Don't delete these because the pixmap store takes care of them.
46
 
    //delete m_pBG;
47
 
    //delete m_pFG;
48
 
    //delete m_pGhost;
49
 
    WPixmapStore::deletePixmap(m_pBG);
50
 
    WPixmapStore::deletePixmap(m_pFG);
51
 
    WPixmapStore::deletePixmap(m_pGhost);
52
 
    delete m_pPlay;
53
 
    delete m_pPlayPos;
54
 
    delete m_pVisualPlayPos;
55
 
    delete m_pDuration;
56
 
    delete m_pTrackSamples;
57
 
    delete m_pTrackSampleRate;
58
 
    delete m_pBPM;
59
 
    delete m_pScratch;
60
 
    delete m_pScratchToggle;
61
 
    delete m_pScratchPos;
 
47
    qDebug() << "WSpinny(): Created QGLWidget, Context"
 
48
             << "Valid:" << context()->isValid()
 
49
             << "Sharing:" << context()->isSharing();
 
50
}
 
51
 
 
52
WSpinny::~WSpinny() {
 
53
    // No need to delete anything if m_group is empty because setup() was not called.
 
54
    if (!m_group.isEmpty()) {
 
55
        WImageStore::deleteImage(m_pBgImage);
 
56
        WImageStore::deleteImage(m_pFgImage);
 
57
        WImageStore::deleteImage(m_pGhostImage);
 
58
        delete m_pPlay;
 
59
        delete m_pPlayPos;
 
60
        delete m_pVisualPlayPos;
 
61
        delete m_pDuration;
 
62
        delete m_pTrackSamples;
 
63
        delete m_pTrackSampleRate;
 
64
        delete m_pScratch;
 
65
        delete m_pScratchToggle;
 
66
        delete m_pScratchPos;
 
67
    #ifdef __VINYLCONTROL__
 
68
        delete m_pVinylControlSpeedType;
 
69
        delete m_pVinylControlEnabled;
 
70
        delete m_pSignalEnabled;
 
71
    #endif
 
72
    }
 
73
}
 
74
 
 
75
void WSpinny::onVinylSignalQualityUpdate(const VinylSignalQualityReport& report) {
62
76
#ifdef __VINYLCONTROL__
63
 
    delete m_pVinylControlSpeedType;
64
 
    delete m_pVinylControlEnabled;
65
 
    delete m_pSignalEnabled;
66
 
    delete m_pRate;
 
77
    // Skip reports for vinyl inputs we don't care about.
 
78
    if (report.processor != m_iVinylInput) {
 
79
        return;
 
80
    }
 
81
    int r,g,b;
 
82
    QColor qual_color = QColor();
 
83
    float signalQuality = report.timecode_quality;
 
84
 
 
85
    // color is related to signal quality
 
86
    // hsv:  s=1, v=1
 
87
    // h is the only variable.
 
88
    // h=0 is red, h=120 is green
 
89
    qual_color.setHsv((int)(120.0 * signalQuality), 255, 255);
 
90
    qual_color.getRgb(&r, &g, &b);
 
91
 
 
92
    for (int y = 0; y < m_iVinylScopeSize; ++y) {
 
93
        QRgb *line = (QRgb *)m_qImage.scanLine(y);
 
94
        for (int x = 0; x < m_iVinylScopeSize; ++x) {
 
95
            // use xwax's bitmap to set alpha data only
 
96
            // adjust alpha by 3/4 so it's not quite so distracting
 
97
            // setpixel is slow, use scanlines instead
 
98
            //m_qImage.setPixel(x, y, qRgba(r,g,b,(int)buf[x+m_iVinylScopeSize*y] * .75));
 
99
            *line = qRgba(r,g,b,(int)(report.scope[x+m_iVinylScopeSize*y] * .75));
 
100
            line++;
 
101
        }
 
102
    }
67
103
#endif
68
 
 
69
104
}
70
105
 
71
 
void WSpinny::setup(QDomNode node, QString group)
72
 
{
 
106
void WSpinny::setup(QDomNode node, QString group) {
73
107
    m_group = group;
74
108
 
75
 
    // Set pixmaps
76
 
    m_pBG = WPixmapStore::getPixmap(WWidget::getPath(WWidget::selectNodeQString(node,
 
109
    // Set images
 
110
    m_pBgImage = WImageStore::getImage(WWidget::getPath(WWidget::selectNodeQString(node,
77
111
                                                    "PathBackground")));
78
 
    m_pFG = WPixmapStore::getPixmap(WWidget::getPath(WWidget::selectNodeQString(node,
 
112
    m_pFgImage = WImageStore::getImage(WWidget::getPath(WWidget::selectNodeQString(node,
79
113
                                                    "PathForeground")));
80
 
    m_pGhost = WPixmapStore::getPixmap(WWidget::getPath(WWidget::selectNodeQString(node,
 
114
    m_pGhostImage = WImageStore::getImage(WWidget::getPath(WWidget::selectNodeQString(node,
81
115
                                                    "PathGhost")));
82
 
    if (m_pBG && !m_pBG->isNull()) {
83
 
        setFixedSize(m_pBG->size());
 
116
    if (m_pBgImage && !m_pBgImage->isNull()) {
 
117
        setFixedSize(m_pBgImage->size());
84
118
    }
85
119
 
86
120
#ifdef __VINYLCONTROL__
87
 
    m_iSize = MIXXX_VINYL_SCOPE_SIZE;
88
 
    m_qImage = QImage(m_iSize, m_iSize, QImage::Format_ARGB32);
89
 
    //fill with transparent black
 
121
    // Find the vinyl input we should listen to reports about.
 
122
    if (m_pVCManager) {
 
123
        m_iVinylInput = m_pVCManager->vinylInputFromGroup(m_group);
 
124
    }
 
125
    m_iVinylScopeSize = MIXXX_VINYL_SCOPE_SIZE;
 
126
    m_qImage = QImage(m_iVinylScopeSize, m_iVinylScopeSize, QImage::Format_ARGB32);
 
127
    // fill with transparent black
90
128
    m_qImage.fill(qRgba(0,0,0,0));
91
129
#endif
92
130
 
103
141
    m_pTrackSampleRate = new ControlObjectThreadMain(
104
142
                                    ControlObject::getControl(
105
143
                                    ConfigKey(group, "track_samplerate")));
106
 
    m_pBPM = new ControlObjectThreadMain(ControlObject::getControl(
107
 
                        ConfigKey(group, "bpm")));
108
144
 
109
145
    m_pScratch = new ControlObjectThreadMain(ControlObject::getControl(
110
146
                        ConfigKey(group, "scratch2")));
117
153
        ConfigKey(group, "slip_enabled")));
118
154
    m_pSlipPosition = new ControlObjectThreadMain(ControlObject::getControl(
119
155
        ConfigKey(group, "slip_playposition")));
120
 
    connect(m_pSlipPosition, SIGNAL(valueChanged(double)),
121
 
            this, SLOT(updateGhostAngleFromPlaypos(double)));
122
 
 
123
 
    //Repaint when visual_playposition changes.
124
 
    connect(m_pVisualPlayPos, SIGNAL(valueChanged(double)),
125
 
            this, SLOT(updateAngleFromPlaypos(double)));
126
 
 
127
 
    connect(m_pSlipPosition, SIGNAL(valueChanged(double)),
128
 
            this, SLOT(updateGhostAngleFromPlaypos(double)));
129
 
 
130
 
 
131
156
 
132
157
#ifdef __VINYLCONTROL__
133
158
    m_pVinylControlSpeedType = new ControlObjectThreadMain(ControlObject::getControl(
137
162
        //Initialize the rotational speed.
138
163
        this->updateVinylControlSpeed(m_pVinylControlSpeedType->get());
139
164
    }
 
165
 
140
166
    m_pVinylControlEnabled = new ControlObjectThreadMain(ControlObject::getControl(
141
167
                        ConfigKey(group, "vinylcontrol_enabled")));
 
168
    connect(m_pVinylControlEnabled, SIGNAL(valueChanged(double)),
 
169
            this, SLOT(updateVinylControlEnabled(double)));
 
170
 
142
171
    m_pSignalEnabled = new ControlObjectThreadMain(ControlObject::getControl(
143
172
                        ConfigKey(group, "vinylcontrol_signal_enabled")));
144
 
    m_pRate = new ControlObjectThreadMain(ControlObject::getControl(
145
 
                        ConfigKey(group, "rate")));
 
173
    connect(m_pSignalEnabled, SIGNAL(valueChanged(double)),
 
174
            this, SLOT(updateVinylControlSignalEnabled(double)));
146
175
 
147
176
    //Match the vinyl control's set RPM so that the spinny widget rotates at the same
148
177
    //speed as your physical decks, if you're using vinyl control.
149
178
    connect(m_pVinylControlSpeedType, SIGNAL(valueChanged(double)),
150
179
            this, SLOT(updateVinylControlSpeed(double)));
151
180
 
152
 
    //Make sure vinyl control proxies are up to date
153
 
    connect(m_pVinylControlEnabled, SIGNAL(valueChanged(double)),
154
 
            this, SLOT(updateVinylControlEnabled(double)));
155
 
 
156
 
    //Check the rate to see if we are stopped
157
 
    connect(m_pRate, SIGNAL(valueChanged(double)),
158
 
            this, SLOT(updateRate(double)));
 
181
 
 
182
 
159
183
#else
160
184
    //if no vinyl control, just call it 33
161
185
    this->updateVinylControlSpeed(33.0);
162
186
#endif
163
187
}
164
188
 
165
 
void WSpinny::paintEvent(QPaintEvent *e)
166
 
{
 
189
void WSpinny::paintEvent(QPaintEvent *e) {
167
190
    Q_UNUSED(e); //ditch unused param warning
168
191
 
169
192
    QPainter p(this);
170
193
    p.setRenderHint(QPainter::SmoothPixmapTransform);
171
194
 
172
 
    if (m_pBG) {
173
 
        p.drawPixmap(0, 0, *m_pBG);
 
195
    if (m_pBgImage) {
 
196
        p.drawImage(0, 0, *m_pBgImage);
174
197
    }
175
198
 
176
199
#ifdef __VINYLCONTROL__
177
200
    // Overlay the signal quality drawing if vinyl is active
178
 
    if (m_bVinylActive && m_bSignalActive)
179
 
    {
180
 
        //reduce cpu load by only updating every 3 times
181
 
        m_iSignalUpdateTick = (m_iSignalUpdateTick + 1) % 3;
182
 
        if (m_iSignalUpdateTick == 0)
183
 
        {
184
 
            unsigned char * buf = m_pVinylControl->getScopeBytemap();
185
 
            int r,g,b;
186
 
            QColor qual_color = QColor();
187
 
            float signalQuality = m_pVinylControl->getTimecodeQuality();
188
 
 
189
 
            //color is related to signal quality
190
 
            //hsv:  s=1, v=1
191
 
            //h is the only variable.
192
 
            //h=0 is red, h=120 is green
193
 
            qual_color.setHsv((int)(120.0 * signalQuality), 255, 255);
194
 
            qual_color.getRgb(&r, &g, &b);
195
 
 
196
 
            if (buf) {
197
 
                for (int y=0; y<m_iSize; y++) {
198
 
                    QRgb *line = (QRgb *)m_qImage.scanLine(y);
199
 
                    for(int x=0; x<m_iSize; x++) {
200
 
                        //use xwax's bitmap to set alpha data only
201
 
                        //adjust alpha by 3/4 so it's not quite so distracting
202
 
                        //setpixel is slow, use scanlines instead
203
 
                        //m_qImage.setPixel(x, y, qRgba(r,g,b,(int)buf[x+m_iSize*y] * .75));
204
 
                        *line = qRgba(r,g,b,(int)(buf[x+m_iSize*y] * .75));
205
 
                        line++;
206
 
                    }
207
 
                }
208
 
                p.drawImage(this->rect(), m_qImage);
209
 
            }
210
 
        }
211
 
        else
212
 
        {
213
 
            //draw the last good image
214
 
            p.drawImage(this->rect(), m_qImage);
215
 
        }
 
201
    if (m_bVinylActive && m_bSignalActive) {
 
202
        // draw the last good image
 
203
        p.drawImage(this->rect(), m_qImage);
216
204
    }
217
205
#endif
218
206
 
219
 
    //To rotate the foreground pixmap around the center of the image,
220
 
    //we use the classic trick of translating the coordinate system such that
221
 
    //the origin is at the center of the image. We then rotate the coordinate system,
222
 
    //and draw the pixmap at the corner.
 
207
    // To rotate the foreground image around the center of the image,
 
208
    // we use the classic trick of translating the coordinate system such that
 
209
    // the origin is at the center of the image. We then rotate the coordinate system,
 
210
    // and draw the image at the corner.
223
211
    p.translate(width() / 2, height() / 2);
224
212
 
225
213
    bool bGhostPlayback = m_pSlipEnabled->get();
228
216
        p.save();
229
217
    }
230
218
 
231
 
    if (m_pFG && !m_pFG->isNull()) {
232
 
        //Now rotate the pixmap and draw it on the screen.
 
219
    double playPosition = m_pVisualPlayPos->get();
 
220
    double slipPosition = m_pSlipPosition->get();
 
221
 
 
222
    if (playPosition != m_dAngleLastPlaypos) {
 
223
        m_fAngle = calculateAngle(playPosition);
 
224
        m_dAngleLastPlaypos = playPosition;
 
225
    }
 
226
 
 
227
    if (slipPosition != m_dGhostAngleLastPlaypos) {
 
228
        m_fGhostAngle = calculateAngle(slipPosition);
 
229
        m_dGhostAngleLastPlaypos = slipPosition;
 
230
    }
 
231
 
 
232
    if (m_pFgImage && !m_pFgImage->isNull()) {
 
233
        // Now rotate the image and draw it on the screen.
233
234
        p.rotate(m_fAngle);
234
 
        p.drawPixmap(-(width() / 2), -(height() / 2), *m_pFG);
 
235
        p.drawImage(-(m_pFgImage->width() / 2),
 
236
                -(m_pFgImage->height() / 2), *m_pFgImage);
235
237
    }
236
238
 
237
 
    if (bGhostPlayback && m_pGhost && !m_pGhost->isNull()) {
 
239
    if (bGhostPlayback && m_pGhostImage && !m_pGhostImage->isNull()) {
238
240
        p.restore();
239
241
        p.save();
240
242
        p.rotate(m_fGhostAngle);
241
 
        p.drawPixmap(-(width() / 2), -(height() / 2), *m_pGhost);
 
243
        p.drawImage(-(m_pGhostImage->width() / 2),
 
244
                -(m_pGhostImage->height() / 2), *m_pGhostImage);
242
245
 
243
246
        //Rotate back to the playback position (not the ghost positon),
244
247
        //and draw the beat marks from there.
283
286
    }
284
287
 
285
288
    if (angle <= -180 || angle > 180) {
286
 
        qDebug() << "Angle clamping failed!" << t << originalAngle << "->" << angle
287
 
                 << "Please file a bug or email mixxx-devel@lists.sourceforge.net";
 
289
        // Only warn once per session. This can tank performance since it prints
 
290
        // like crazy.
 
291
        if (!m_bClampFailedWarning) {
 
292
            qDebug() << "Angle clamping failed!" << t << originalAngle << "->" << angle
 
293
                     << "Please file a bug or email mixxx-devel@lists.sourceforge.net";
 
294
            m_bClampFailedWarning = true;
 
295
        }
288
296
        return 0.0;
289
297
    }
290
298
    return angle;
334
342
    return playpos;
335
343
}
336
344
 
337
 
void WSpinny::updateAngleFromPlaypos(double playpos) {
338
 
    m_fAngle = calculateAngle(playpos);
339
 
}
340
 
 
341
 
void WSpinny::updateGhostAngleFromPlaypos(double playpos) {
342
 
    m_fGhostAngle = calculateAngle(playpos);
343
 
}
344
 
 
345
 
void WSpinny::updateRate(double rate) {
346
 
}
347
 
 
348
345
void WSpinny::updateVinylControlSpeed(double rpm) {
349
346
    m_dRotationsPerSecond = rpm/60.;
350
347
}
351
348
 
 
349
void WSpinny::updateVinylControlSignalEnabled(double enabled) {
 
350
    m_bSignalActive = enabled;
 
351
 
 
352
    if (enabled && m_iVinylInput != -1) {
 
353
        m_pVCManager->addSignalQualityListener(this);
 
354
    } else {
 
355
        m_pVCManager->removeSignalQualityListener(this);
 
356
        // fill with transparent black
 
357
        m_qImage.fill(qRgba(0,0,0,0));
 
358
    }
 
359
}
 
360
 
352
361
void WSpinny::updateVinylControlEnabled(double enabled) {
353
 
#ifdef __VINYLCONTROL__
354
 
    if (enabled)
355
 
    {
356
 
        if (m_pVinylControl == NULL)
357
 
        {
358
 
            m_pVinylControl = m_pVCManager->getVinylControlProxyForChannel(m_group);
359
 
            if (m_pVinylControl != NULL)
360
 
            {
361
 
                m_bVinylActive = true;
362
 
                m_bSignalActive = m_pSignalEnabled->get();
363
 
                connect(m_pVinylControl, SIGNAL(destroyed()),
364
 
                    this, SLOT(invalidateVinylControl()));
365
 
            }
366
 
        }
367
 
        else
368
 
        {
369
 
            m_bVinylActive = true;
370
 
        }
371
 
    }
372
 
    else
373
 
    {
374
 
        m_bVinylActive = false;
375
 
    }
376
 
#endif
377
 
}
378
 
 
379
 
void WSpinny::invalidateVinylControl() {
380
 
#ifdef __VINYLCONTROL__
381
 
    m_bVinylActive = false;
382
 
    m_pVinylControl = NULL;
383
 
#endif
384
 
}
385
 
 
386
 
 
387
 
void WSpinny::mouseMoveEvent(QMouseEvent * e)
388
 
{
 
362
    m_bVinylActive = enabled;
 
363
}
 
364
 
 
365
void WSpinny::mouseMoveEvent(QMouseEvent * e) {
389
366
    int y = e->y();
390
367
    int x = e->x();
391
368
 
490
467
    */
491
468
}
492
469
 
 
470
void WSpinny::showEvent(QShowEvent* event) {
 
471
    // If we want to draw the VC signal on this widget then register for
 
472
    // updates.
 
473
    if (m_bSignalActive && m_iVinylInput != -1 && m_pVCManager) {
 
474
        m_pVCManager->addSignalQualityListener(this);
 
475
    }
 
476
}
 
477
 
 
478
void WSpinny::hideEvent(QHideEvent* event) {
 
479
    // When we are hidden we do not want signal quality updates.
 
480
    if (m_pVCManager) {
 
481
        m_pVCManager->removeSignalQualityListener(this);
 
482
    }
 
483
    // fill with transparent black
 
484
    m_qImage.fill(qRgba(0,0,0,0));
 
485
}
 
486
 
493
487
/** DRAG AND DROP **/
494
488
void WSpinny::dragEnterEvent(QDragEnterEvent * event)
495
489
{