annotate widgets/AudioDial.cpp @ 77:fd348f36c0d3

* Implement harmonic cursor in spectrogram * Implement layer export. This doesn't quite do the right thing for the SV XML layer export yet -- it doesn't include layer display information, so when imported, it only creates an invisible model. Could also do with fixing CSV file import so as to work correctly for note and text layers.
author Chris Cannam
date Mon, 10 Apr 2006 17:22:59 +0000
parents 705f05ab42e3
children 82482231b6b1
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@0 7
Chris@59 8 This program is free software; you can redistribute it and/or
Chris@59 9 modify it under the terms of the GNU General Public License as
Chris@59 10 published by the Free Software Foundation; either version 2 of the
Chris@59 11 License, or (at your option) any later version. See the file
Chris@59 12 COPYING included with this distribution for more information.
Chris@0 13 */
Chris@0 14
Chris@0 15 /**
Chris@0 16 * A rotary dial widget.
Chris@0 17 *
Chris@0 18 * Based on an original design by Thorsten Wilms.
Chris@0 19 *
Chris@0 20 * Implemented as a widget for the Rosegarden MIDI and audio sequencer
Chris@0 21 * and notation editor by Chris Cannam.
Chris@0 22 *
Chris@0 23 * Extracted into a standalone Qt3 widget by Pedro Lopez-Cabanillas
Chris@0 24 * and adapted for use in QSynth.
Chris@0 25 *
Chris@0 26 * Ported to Qt4 by Chris Cannam.
Chris@0 27 *
Chris@0 28 * This file copyright 2003-2005 Chris Cannam, copyright 2005 Pedro
Chris@0 29 * Lopez-Cabanillas.
Chris@0 30 *
Chris@0 31 * This program is free software; you can redistribute it and/or
Chris@0 32 * modify it under the terms of the GNU General Public License as
Chris@0 33 * published by the Free Software Foundation; either version 2 of the
Chris@0 34 * License, or (at your option) any later version. See the file
Chris@0 35 * COPYING included with this distribution for more information.
Chris@0 36 */
Chris@0 37
Chris@0 38 #include "AudioDial.h"
Chris@0 39
Chris@0 40 #include <cmath>
Chris@0 41 #include <iostream>
Chris@0 42
Chris@0 43 #include <QTimer>
Chris@0 44 #include <QPainter>
Chris@0 45 #include <QPixmap>
Chris@0 46 #include <QColormap>
Chris@0 47 #include <QMouseEvent>
Chris@0 48 #include <QPaintEvent>
Chris@34 49 #include <QInputDialog>
Chris@0 50
Chris@0 51 using std::endl;
Chris@0 52 using std::cerr;
Chris@0 53
Chris@0 54
Chris@0 55 //!!! Pedro updated his version to use my up/down response code from RG -- need to grab that code in preference to this version from Rui
Chris@0 56
Chris@0 57
Chris@0 58 //-------------------------------------------------------------------------
Chris@0 59 // AudioDial - Instance knob widget class.
Chris@0 60 //
Chris@0 61
Chris@0 62 #define AUDIO_DIAL_MIN (0.25 * M_PI)
Chris@0 63 #define AUDIO_DIAL_MAX (1.75 * M_PI)
Chris@0 64 #define AUDIO_DIAL_RANGE (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN)
Chris@0 65
Chris@0 66
Chris@0 67 // Constructor.
Chris@0 68 AudioDial::AudioDial(QWidget *parent) :
Chris@0 69 QDial(parent),
Chris@34 70 m_knobColor(Qt::black), m_meterColor(Qt::white),
Chris@34 71 m_defaultValue(0)
Chris@0 72 {
Chris@0 73 m_mouseDial = false;
Chris@0 74 m_mousePressed = false;
Chris@0 75 }
Chris@0 76
Chris@0 77
Chris@0 78 // Destructor.
Chris@0 79 AudioDial::~AudioDial (void)
Chris@0 80 {
Chris@0 81 }
Chris@0 82
Chris@0 83
Chris@0 84 void AudioDial::paintEvent(QPaintEvent *)
Chris@0 85 {
Chris@0 86 QPainter paint;
Chris@0 87
Chris@0 88 float angle = AUDIO_DIAL_MIN // offset
Chris@0 89 + (AUDIO_DIAL_RANGE *
Chris@0 90 (float(QDial::value() - QDial::minimum()) /
Chris@0 91 (float(QDial::maximum() - QDial::minimum()))));
Chris@0 92 int degrees = int(angle * 180.0 / M_PI);
Chris@0 93
Chris@0 94 int ns = notchSize();
Chris@0 95 int numTicks = 1 + (maximum() + ns - minimum()) / ns;
Chris@0 96
Chris@0 97 QColor knobColor(m_knobColor);
Chris@0 98 if (knobColor == Qt::black)
Chris@0 99 knobColor = palette().mid().color();
Chris@0 100
Chris@0 101 QColor meterColor(m_meterColor);
Chris@0 102 if (!isEnabled())
Chris@0 103 meterColor = palette().mid().color();
Chris@0 104 else if (m_meterColor == Qt::white)
Chris@0 105 meterColor = palette().highlight().color();
Chris@0 106
Chris@0 107 int m_size = width() < height() ? width() : height();
Chris@0 108 int scale = 1;
Chris@0 109 int width = m_size - 2*scale, height = m_size - 2*scale;
Chris@0 110
Chris@0 111 paint.begin(this);
Chris@0 112 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@0 113 paint.translate(1, 1);
Chris@0 114
Chris@0 115 QPen pen;
Chris@0 116 QColor c;
Chris@0 117
Chris@0 118 // Knob body and face...
Chris@0 119
Chris@0 120 c = knobColor;
Chris@0 121 pen.setColor(knobColor);
Chris@0 122 pen.setWidth(scale * 2);
Chris@0 123 pen.setCapStyle(Qt::FlatCap);
Chris@0 124
Chris@0 125 paint.setPen(pen);
Chris@0 126 paint.setBrush(c);
Chris@0 127
Chris@0 128 int indent = (int)(width * 0.15 + 1);
Chris@0 129
Chris@0 130 paint.drawEllipse(indent-1, indent-1, width-2*indent, width-2*indent);
Chris@0 131
Chris@0 132 pen.setWidth(3 * scale);
Chris@0 133 int pos = indent-1 + (width-2*indent) / 20;
Chris@0 134 int darkWidth = (width-2*indent) * 3 / 4;
Chris@0 135 while (darkWidth) {
Chris@0 136 c = c.light(102);
Chris@0 137 pen.setColor(c);
Chris@0 138 paint.setPen(pen);
Chris@0 139 paint.drawEllipse(pos, pos, darkWidth, darkWidth);
Chris@0 140 if (!--darkWidth) break;
Chris@0 141 paint.drawEllipse(pos, pos, darkWidth, darkWidth);
Chris@0 142 if (!--darkWidth) break;
Chris@0 143 paint.drawEllipse(pos, pos, darkWidth, darkWidth);
Chris@0 144 ++pos; --darkWidth;
Chris@0 145 }
Chris@0 146
Chris@0 147 // Tick notches...
Chris@0 148
Chris@34 149 if ( notchesVisible() ) {
Chris@0 150 // std::cerr << "Notches visible" << std::endl;
Chris@0 151 pen.setColor(palette().dark().color());
Chris@0 152 pen.setWidth(scale);
Chris@0 153 paint.setPen(pen);
Chris@0 154 for (int i = 0; i < numTicks; ++i) {
Chris@0 155 int div = numTicks;
Chris@0 156 if (div > 1) --div;
Chris@0 157 drawTick(paint, AUDIO_DIAL_MIN + (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN) * i / div,
Chris@0 158 width, true);
Chris@0 159 }
Chris@0 160 }
Chris@0 161
Chris@0 162 // The bright metering bit...
Chris@0 163
Chris@0 164 c = meterColor;
Chris@0 165 pen.setColor(c);
Chris@0 166 pen.setWidth(indent);
Chris@0 167 paint.setPen(pen);
Chris@0 168
Chris@0 169 // std::cerr << "degrees " << degrees << ", gives us " << -(degrees - 45) * 16 << std::endl;
Chris@0 170
Chris@0 171 int arcLen = -(degrees - 45) * 16;
Chris@0 172 if (arcLen == 0) arcLen = -16;
Chris@0 173
Chris@0 174 paint.drawArc(indent/2, indent/2,
Chris@0 175 width-indent, width-indent, (180 + 45) * 16, arcLen);
Chris@0 176
Chris@0 177 paint.setBrush(Qt::NoBrush);
Chris@0 178
Chris@0 179 // Shadowing...
Chris@0 180
Chris@0 181 pen.setWidth(scale);
Chris@0 182 paint.setPen(pen);
Chris@0 183
Chris@0 184 // Knob shadow...
Chris@0 185
Chris@0 186 int shadowAngle = -720;
Chris@0 187 c = knobColor.dark();
Chris@0 188 for (int arc = 120; arc < 2880; arc += 240) {
Chris@0 189 pen.setColor(c);
Chris@0 190 paint.setPen(pen);
Chris@0 191 paint.drawArc(indent, indent,
Chris@0 192 width-2*indent, width-2*indent, shadowAngle + arc, 240);
Chris@0 193 paint.drawArc(indent, indent,
Chris@0 194 width-2*indent, width-2*indent, shadowAngle - arc, 240);
Chris@0 195 c = c.light(110);
Chris@0 196 }
Chris@0 197
Chris@0 198 // Scale shadow...
Chris@0 199
Chris@0 200 shadowAngle = 2160;
Chris@0 201 c = palette().dark().color();
Chris@0 202 for (int arc = 120; arc < 2880; arc += 240) {
Chris@0 203 pen.setColor(c);
Chris@0 204 paint.setPen(pen);
Chris@0 205 paint.drawArc(scale/2, scale/2,
Chris@0 206 width-scale, width-scale, shadowAngle + arc, 240);
Chris@0 207 paint.drawArc(scale/2, scale/2,
Chris@0 208 width-scale, width-scale, shadowAngle - arc, 240);
Chris@0 209 c = c.light(108);
Chris@0 210 }
Chris@0 211
Chris@0 212 // Undraw the bottom part...
Chris@0 213
Chris@0 214 pen.setColor(palette().background().color());
Chris@0 215 pen.setWidth(scale * 4);
Chris@0 216 paint.setPen(pen);
Chris@0 217 paint.drawArc(scale/2, scale/2,
Chris@0 218 width-scale, width-scale, -45 * 16, -92 * 16);
Chris@0 219
Chris@0 220 // Scale ends...
Chris@0 221
Chris@0 222 pen.setColor(palette().dark().color());
Chris@0 223 pen.setWidth(scale);
Chris@0 224 paint.setPen(pen);
Chris@0 225 for (int i = 0; i < numTicks; ++i) {
Chris@0 226 if (i != 0 && i != numTicks - 1) continue;
Chris@0 227 int div = numTicks;
Chris@0 228 if (div > 1) --div;
Chris@0 229 drawTick(paint, AUDIO_DIAL_MIN + (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN) * i / div,
Chris@0 230 width, false);
Chris@0 231 }
Chris@0 232
Chris@0 233 // Pointer notch...
Chris@0 234
Chris@0 235 float hyp = float(width) / 2.0;
Chris@0 236 float len = hyp - indent;
Chris@0 237 --len;
Chris@0 238
Chris@0 239 float x0 = hyp;
Chris@0 240 float y0 = hyp;
Chris@0 241
Chris@0 242 float x = hyp - len * sin(angle);
Chris@0 243 float y = hyp + len * cos(angle);
Chris@0 244
Chris@0 245 c = palette().dark().color();
Chris@0 246 pen.setColor(isEnabled() ? c.dark(130) : c);
Chris@0 247 pen.setWidth(scale * 2);
Chris@0 248 paint.setPen(pen);
Chris@0 249 paint.drawLine(int(x0), int(y0), int(x), int(y));
Chris@0 250
Chris@0 251 paint.end();
Chris@0 252 }
Chris@0 253
Chris@0 254
Chris@0 255 void AudioDial::drawTick(QPainter &paint,
Chris@0 256 float angle, int size, bool internal)
Chris@0 257 {
Chris@0 258 float hyp = float(size) / 2.0;
Chris@0 259 float x0 = hyp - (hyp - 1) * sin(angle);
Chris@0 260 float y0 = hyp + (hyp - 1) * cos(angle);
Chris@0 261
Chris@0 262 // cerr << "drawTick: angle " << angle << ", size " << size << ", internal " << internal << endl;
Chris@0 263
Chris@0 264 if (internal) {
Chris@0 265
Chris@0 266 float len = hyp / 4;
Chris@0 267 float x1 = hyp - (hyp - len) * sin(angle);
Chris@0 268 float y1 = hyp + (hyp - len) * cos(angle);
Chris@0 269
Chris@0 270 paint.drawLine(int(x0), int(y0), int(x1), int(y1));
Chris@0 271
Chris@0 272 } else {
Chris@0 273
Chris@0 274 float len = hyp / 4;
Chris@0 275 float x1 = hyp - (hyp + len) * sin(angle);
Chris@0 276 float y1 = hyp + (hyp + len) * cos(angle);
Chris@0 277
Chris@0 278 paint.drawLine(int(x0), int(y0), int(x1), int(y1));
Chris@0 279 }
Chris@0 280 }
Chris@0 281
Chris@0 282
Chris@0 283 void AudioDial::setKnobColor(const QColor& color)
Chris@0 284 {
Chris@0 285 m_knobColor = color;
Chris@0 286 update();
Chris@0 287 }
Chris@0 288
Chris@0 289
Chris@0 290 void AudioDial::setMeterColor(const QColor& color)
Chris@0 291 {
Chris@0 292 m_meterColor = color;
Chris@0 293 update();
Chris@0 294 }
Chris@0 295
Chris@0 296
Chris@0 297 void AudioDial::setMouseDial(bool mouseDial)
Chris@0 298 {
Chris@0 299 m_mouseDial = mouseDial;
Chris@0 300 }
Chris@0 301
Chris@0 302
Chris@34 303 void AudioDial::setDefaultValue(int defaultValue)
Chris@34 304 {
Chris@34 305 m_defaultValue = defaultValue;
Chris@34 306 }
Chris@34 307
Chris@34 308
Chris@0 309 // Alternate mouse behavior event handlers.
Chris@0 310 void AudioDial::mousePressEvent(QMouseEvent *mouseEvent)
Chris@0 311 {
Chris@0 312 if (m_mouseDial) {
Chris@0 313 QDial::mousePressEvent(mouseEvent);
Chris@0 314 } else if (mouseEvent->button() == Qt::LeftButton) {
Chris@0 315 m_mousePressed = true;
Chris@0 316 m_posMouse = mouseEvent->pos();
Chris@34 317 } else if (mouseEvent->button() == Qt::MidButton) {
Chris@34 318 int dv = m_defaultValue;
Chris@34 319 if (dv < minimum()) dv = minimum();
Chris@34 320 if (dv > maximum()) dv = maximum();
Chris@34 321 setValue(m_defaultValue);
Chris@34 322 }
Chris@34 323 }
Chris@34 324
Chris@34 325
Chris@34 326 void AudioDial::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
Chris@34 327 {
Chris@34 328 if (m_mouseDial) {
Chris@34 329 QDial::mouseDoubleClickEvent(mouseEvent);
Chris@34 330 } else if (mouseEvent->button() == Qt::LeftButton) {
Chris@34 331 bool ok = false;
Chris@34 332 int newValue = QInputDialog::getInteger
Chris@34 333 (this,
Chris@34 334 tr("Enter new value"),
Chris@34 335 tr("Select a new value in the range %1 to %2:")
Chris@34 336 .arg(minimum()).arg(maximum()),
Chris@34 337 value(), minimum(), maximum(), pageStep(), &ok);
Chris@34 338 if (ok) {
Chris@34 339 setValue(newValue);
Chris@34 340 }
Chris@0 341 }
Chris@0 342 }
Chris@0 343
Chris@0 344
Chris@0 345 void AudioDial::mouseMoveEvent(QMouseEvent *mouseEvent)
Chris@0 346 {
Chris@0 347 if (m_mouseDial) {
Chris@0 348 QDial::mouseMoveEvent(mouseEvent);
Chris@0 349 } else if (m_mousePressed) {
Chris@0 350 const QPoint& posMouse = mouseEvent->pos();
Chris@0 351 int v = QDial::value()
Chris@0 352 + (posMouse.x() - m_posMouse.x())
Chris@0 353 + (m_posMouse.y() - posMouse.y());
Chris@0 354 if (v > QDial::maximum())
Chris@0 355 v = QDial::maximum();
Chris@0 356 else
Chris@0 357 if (v < QDial::minimum())
Chris@0 358 v = QDial::minimum();
Chris@0 359 m_posMouse = posMouse;
Chris@0 360 QDial::setValue(v);
Chris@0 361 }
Chris@0 362 }
Chris@0 363
Chris@0 364
Chris@0 365 void AudioDial::mouseReleaseEvent(QMouseEvent *mouseEvent)
Chris@0 366 {
Chris@0 367 if (m_mouseDial) {
Chris@0 368 QDial::mouseReleaseEvent(mouseEvent);
Chris@0 369 } else if (m_mousePressed) {
Chris@0 370 m_mousePressed = false;
Chris@0 371 }
Chris@0 372 }
Chris@0 373
Chris@0 374 #ifdef INCLUDE_MOCFILES
Chris@0 375 #include "AudioDial.moc.cpp"
Chris@0 376 #endif
Chris@0 377