annotate widgets/AudioDial.cpp @ 37:21d061e66177

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