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"
10
#include "widget/wspinny.h"
11
#include "vinylcontrol/vinylcontrolmanager.h"
12
#include "vinylcontrol/vinylcontrol.h"
9
14
WSpinny::WSpinny(QWidget* parent, VinylControlManager* pVCMan)
10
: QGLWidget(SharedGLContext::getContext(), parent),
15
: QGLWidget(parent, SharedGLContext::getWidget()),
16
21
m_pVisualPlayPos(NULL),
18
23
m_pTrackSamples(NULL),
21
25
m_pScratchToggle(NULL),
22
26
m_pScratchPos(NULL),
23
27
m_pVinylControlSpeedType(NULL),
24
28
m_pVinylControlEnabled(NULL),
25
30
m_bVinylActive(false),
26
31
m_bSignalActive(true),
28
m_iSignalUpdateTick(0),
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),
41
m_bClampFailedWarning(false) {
35
42
#ifdef __VINYLCONTROL__
36
43
m_pVCManager = pVCMan;
37
m_pVinylControl = NULL;
40
46
setAcceptDrops(true);
45
//Don't delete these because the pixmap store takes care of them.
49
WPixmapStore::deletePixmap(m_pBG);
50
WPixmapStore::deletePixmap(m_pFG);
51
WPixmapStore::deletePixmap(m_pGhost);
54
delete m_pVisualPlayPos;
56
delete m_pTrackSamples;
57
delete m_pTrackSampleRate;
60
delete m_pScratchToggle;
47
qDebug() << "WSpinny(): Created QGLWidget, Context"
48
<< "Valid:" << context()->isValid()
49
<< "Sharing:" << context()->isSharing();
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);
60
delete m_pVisualPlayPos;
62
delete m_pTrackSamples;
63
delete m_pTrackSampleRate;
65
delete m_pScratchToggle;
67
#ifdef __VINYLCONTROL__
68
delete m_pVinylControlSpeedType;
69
delete m_pVinylControlEnabled;
70
delete m_pSignalEnabled;
75
void WSpinny::onVinylSignalQualityUpdate(const VinylSignalQualityReport& report) {
62
76
#ifdef __VINYLCONTROL__
63
delete m_pVinylControlSpeedType;
64
delete m_pVinylControlEnabled;
65
delete m_pSignalEnabled;
77
// Skip reports for vinyl inputs we don't care about.
78
if (report.processor != m_iVinylInput) {
82
QColor qual_color = QColor();
83
float signalQuality = report.timecode_quality;
85
// color is related to signal quality
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);
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));
71
void WSpinny::setup(QDomNode node, QString group)
106
void WSpinny::setup(QDomNode node, QString group) {
76
m_pBG = WPixmapStore::getPixmap(WWidget::getPath(WWidget::selectNodeQString(node,
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,
82
if (m_pBG && !m_pBG->isNull()) {
83
setFixedSize(m_pBG->size());
116
if (m_pBgImage && !m_pBgImage->isNull()) {
117
setFixedSize(m_pBgImage->size());
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.
123
m_iVinylInput = m_pVCManager->vinylInputFromGroup(m_group);
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));
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)));
123
//Repaint when visual_playposition changes.
124
connect(m_pVisualPlayPos, SIGNAL(valueChanged(double)),
125
this, SLOT(updateAngleFromPlaypos(double)));
127
connect(m_pSlipPosition, SIGNAL(valueChanged(double)),
128
this, SLOT(updateGhostAngleFromPlaypos(double)));
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());
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)));
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)));
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)));
152
//Make sure vinyl control proxies are up to date
153
connect(m_pVinylControlEnabled, SIGNAL(valueChanged(double)),
154
this, SLOT(updateVinylControlEnabled(double)));
156
//Check the rate to see if we are stopped
157
connect(m_pRate, SIGNAL(valueChanged(double)),
158
this, SLOT(updateRate(double)));
160
184
//if no vinyl control, just call it 33
161
185
this->updateVinylControlSpeed(33.0);
165
void WSpinny::paintEvent(QPaintEvent *e)
189
void WSpinny::paintEvent(QPaintEvent *e) {
167
190
Q_UNUSED(e); //ditch unused param warning
169
192
QPainter p(this);
170
193
p.setRenderHint(QPainter::SmoothPixmapTransform);
173
p.drawPixmap(0, 0, *m_pBG);
196
p.drawImage(0, 0, *m_pBgImage);
176
199
#ifdef __VINYLCONTROL__
177
200
// Overlay the signal quality drawing if vinyl is active
178
if (m_bVinylActive && m_bSignalActive)
180
//reduce cpu load by only updating every 3 times
181
m_iSignalUpdateTick = (m_iSignalUpdateTick + 1) % 3;
182
if (m_iSignalUpdateTick == 0)
184
unsigned char * buf = m_pVinylControl->getScopeBytemap();
186
QColor qual_color = QColor();
187
float signalQuality = m_pVinylControl->getTimecodeQuality();
189
//color is related to signal quality
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);
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));
208
p.drawImage(this->rect(), m_qImage);
213
//draw the last good image
214
p.drawImage(this->rect(), m_qImage);
201
if (m_bVinylActive && m_bSignalActive) {
202
// draw the last good image
203
p.drawImage(this->rect(), m_qImage);
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);
225
213
bool bGhostPlayback = m_pSlipEnabled->get();
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();
222
if (playPosition != m_dAngleLastPlaypos) {
223
m_fAngle = calculateAngle(playPosition);
224
m_dAngleLastPlaypos = playPosition;
227
if (slipPosition != m_dGhostAngleLastPlaypos) {
228
m_fGhostAngle = calculateAngle(slipPosition);
229
m_dGhostAngleLastPlaypos = slipPosition;
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);
237
if (bGhostPlayback && m_pGhost && !m_pGhost->isNull()) {
239
if (bGhostPlayback && m_pGhostImage && !m_pGhostImage->isNull()) {
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);
243
246
//Rotate back to the playback position (not the ghost positon),
244
247
//and draw the beat marks from there.
337
void WSpinny::updateAngleFromPlaypos(double playpos) {
338
m_fAngle = calculateAngle(playpos);
341
void WSpinny::updateGhostAngleFromPlaypos(double playpos) {
342
m_fGhostAngle = calculateAngle(playpos);
345
void WSpinny::updateRate(double rate) {
348
345
void WSpinny::updateVinylControlSpeed(double rpm) {
349
346
m_dRotationsPerSecond = rpm/60.;
349
void WSpinny::updateVinylControlSignalEnabled(double enabled) {
350
m_bSignalActive = enabled;
352
if (enabled && m_iVinylInput != -1) {
353
m_pVCManager->addSignalQualityListener(this);
355
m_pVCManager->removeSignalQualityListener(this);
356
// fill with transparent black
357
m_qImage.fill(qRgba(0,0,0,0));
352
361
void WSpinny::updateVinylControlEnabled(double enabled) {
353
#ifdef __VINYLCONTROL__
356
if (m_pVinylControl == NULL)
358
m_pVinylControl = m_pVCManager->getVinylControlProxyForChannel(m_group);
359
if (m_pVinylControl != NULL)
361
m_bVinylActive = true;
362
m_bSignalActive = m_pSignalEnabled->get();
363
connect(m_pVinylControl, SIGNAL(destroyed()),
364
this, SLOT(invalidateVinylControl()));
369
m_bVinylActive = true;
374
m_bVinylActive = false;
379
void WSpinny::invalidateVinylControl() {
380
#ifdef __VINYLCONTROL__
381
m_bVinylActive = false;
382
m_pVinylControl = NULL;
387
void WSpinny::mouseMoveEvent(QMouseEvent * e)
362
m_bVinylActive = enabled;
365
void WSpinny::mouseMoveEvent(QMouseEvent * e) {