# HG changeset patch # User Chris Cannam # Date 1427105091 0 # Node ID 4968bbaf1ed87860df7d8a4bc68e0c89f122dbab # Parent b62c2785ed8333c4c876e54bcdde86991bdec6d7# Parent 78e041e45ff0ff3c103e0d794c6f66f391020aff Merge from default branch diff -r b62c2785ed83 -r 4968bbaf1ed8 layer/TimeValueLayer.cpp --- a/layer/TimeValueLayer.cpp Tue Nov 11 17:05:44 2014 +0000 +++ b/layer/TimeValueLayer.cpp Mon Mar 23 10:04:51 2015 +0000 @@ -1151,6 +1151,7 @@ // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2); } else { + path.lineTo(x0, y0); path.lineTo((x0 + x1) / 2, (y0 + y1) / 2); } } diff -r b62c2785ed83 -r 4968bbaf1ed8 svgui.pro --- a/svgui.pro Tue Nov 11 17:05:44 2014 +0000 +++ b/svgui.pro Mon Mar 23 10:04:51 2015 +0000 @@ -136,6 +136,7 @@ widgets/Thumbwheel.h \ widgets/TipDialog.h \ widgets/TransformFinder.h \ + widgets/UnitConverter.h \ widgets/WindowShapePreview.h \ widgets/WindowTypeSelector.h SOURCES += widgets/ActivityLog.cpp \ @@ -173,5 +174,6 @@ widgets/Thumbwheel.cpp \ widgets/TipDialog.cpp \ widgets/TransformFinder.cpp \ + widgets/UnitConverter.cpp \ widgets/WindowShapePreview.cpp \ widgets/WindowTypeSelector.cpp diff -r b62c2785ed83 -r 4968bbaf1ed8 view/Pane.cpp --- a/view/Pane.cpp Tue Nov 11 17:05:44 2014 +0000 +++ b/view/Pane.cpp Mon Mar 23 10:04:51 2015 +0000 @@ -2024,6 +2024,11 @@ int smallThreshold = 10, bigThreshold = 80; + if (m_manager) { + smallThreshold = m_manager->scalePixelSize(smallThreshold); + bigThreshold = m_manager->scalePixelSize(bigThreshold); + } + // SVDEBUG << "Pane::updateDragMode: xdiff = " << xdiff << ", ydiff = " // << ydiff << ", canMoveVertical = " << canMoveVertical << ", drag mode = " << m_dragMode << endl; @@ -2288,7 +2293,7 @@ // Sometimes on Linux we're seeing absurdly extreme angles on // the first wheel event -- discard those entirely - if (abs(m_pendingWheelAngle) >= 600) { + if (abs(m_pendingWheelAngle) > 1000) { m_pendingWheelAngle = 0; return; } diff -r b62c2785ed83 -r 4968bbaf1ed8 view/ViewManager.cpp --- a/view/ViewManager.cpp Tue Nov 11 17:05:44 2014 +0000 +++ b/view/ViewManager.cpp Mon Mar 23 10:04:51 2015 +0000 @@ -720,3 +720,23 @@ return dark; } +int +ViewManager::scalePixelSize(int pixels) +{ + static float ratio = 0.f; + if (ratio == 0.f) { + float baseEm; +#ifdef Q_OS_MAC + baseEm = 17.f; +#else + baseEm = 15.f; +#endif + float em = QFontMetrics(QFont()).height(); + ratio = em / baseEm; + } + + int scaled = int(pixels * ratio + 0.5); + cerr << "scaledSize: " << pixels << " -> " << scaled << " at ratio " << ratio << endl; + if (pixels != 0 && scaled == 0) scaled = 1; + return scaled; +} diff -r b62c2785ed83 -r 4968bbaf1ed8 view/ViewManager.h --- a/view/ViewManager.h Tue Nov 11 17:05:44 2014 +0000 +++ b/view/ViewManager.h Mon Mar 23 10:04:51 2015 +0000 @@ -184,6 +184,13 @@ void setMainModelSampleRate(int sr) { m_mainModelSampleRate = sr; } + /** + * Take a "design pixel" size and scale it for the actual + * display. This is relevant to hi-dpi systems that do not do + * pixel doubling (i.e. Windows and Linux rather than OS/X). + */ + int scalePixelSize(int pixels); + enum OverlayMode { NoOverlays, GlobalOverlays, diff -r b62c2785ed83 -r 4968bbaf1ed8 widgets/UnitConverter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/UnitConverter.cpp Mon Mar 23 10:04:51 2015 +0000 @@ -0,0 +1,401 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "UnitConverter.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "base/Debug.h" +#include "base/Pitch.h" +#include "base/Preferences.h" + +using namespace std; + +static QString pianoNotes[] = { + "C", "C# / Db", "D", "D# / Eb", "E", + "F", "F# / Gb", "G", "G# / Ab", "A", "A# / Bb", "B" +}; + +UnitConverter::UnitConverter(QWidget *parent) : + QDialog(parent) +{ + QGridLayout *maingrid = new QGridLayout; + setLayout(maingrid); + + QTabWidget *tabs = new QTabWidget; + maingrid->addWidget(tabs, 0, 0); + + QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close); + maingrid->addWidget(bb, 1, 0); + connect(bb, SIGNAL(rejected()), this, SLOT(close())); + + QFrame *frame = new QFrame; + tabs->addTab(frame, tr("Pitch")); + + QGridLayout *grid = new QGridLayout; + frame->setLayout(grid); + + m_freq = new QDoubleSpinBox; + m_freq->setSuffix(QString(" Hz")); + m_freq->setDecimals(6); + m_freq->setMinimum(1e-3); + m_freq->setMaximum(1e6); + m_freq->setValue(440); + connect(m_freq, SIGNAL(valueChanged(double)), + this, SLOT(freqChanged())); + + // The min and max range values for all the remaining controls are + // determined by the min and max Hz above + + m_midi = new QSpinBox; + m_midi->setMinimum(-156); + m_midi->setMaximum(203); + connect(m_midi, SIGNAL(valueChanged(int)), + this, SLOT(midiChanged())); + + m_note = new QComboBox; + for (int i = 0; i < 12; ++i) { + m_note->addItem(pianoNotes[i]); + } + connect(m_note, SIGNAL(currentIndexChanged(int)), + this, SLOT(noteChanged())); + + m_octave = new QSpinBox; + m_octave->setMinimum(-14); + m_octave->setMaximum(15); + connect(m_octave, SIGNAL(valueChanged(int)), + this, SLOT(octaveChanged())); + + m_cents = new QDoubleSpinBox; + m_cents->setSuffix(tr(" cents")); + m_cents->setDecimals(4); + m_cents->setMinimum(-50); + m_cents->setMaximum(50); + connect(m_cents, SIGNAL(valueChanged(double)), + this, SLOT(centsChanged())); + + int row = 0; + + grid->addWidget(new QLabel(tr("In 12-tone Equal Temperament:")), row, 0, 1, 9); + + ++row; + + grid->setRowMinimumHeight(row, 8); + + ++row; + + grid->addWidget(m_freq, row, 0, 2, 1, Qt::AlignRight | Qt::AlignVCenter); + grid->addWidget(new QLabel(tr("=")), row, 1, 2, 1, Qt::AlignHCenter | Qt::AlignVCenter); + + grid->addWidget(new QLabel(tr("+")), row, 7, 2, 1, Qt::AlignHCenter | Qt::AlignVCenter); + grid->addWidget(m_cents, row, 8, 2, 1, Qt::AlignLeft | Qt::AlignVCenter); + + grid->addWidget(new QLabel(tr("Piano note")), row, 2, 1, 2); + grid->addWidget(m_note, row, 4); + grid->addWidget(new QLabel(tr("in octave")), row, 5); + grid->addWidget(m_octave, row, 6); + + ++row; + + grid->addWidget(new QLabel(tr("MIDI pitch")), row, 2, 1, 2); + grid->addWidget(m_midi, row, 4); + + ++row; + + grid->setRowStretch(row, 20); + grid->setRowMinimumHeight(row, 8); + + ++row; + + m_pitchPrefsLabel = new QLabel; + grid->addWidget(m_pitchPrefsLabel, row, 0, 1, 9); + + ++row; + + grid->addWidget + (new QLabel(tr("Note that only pitches in the range 0 to 127 are valid " + "in the MIDI protocol.")), + row, 0, 1, 9); + + ++row; + + frame = new QFrame; + tabs->addTab(frame, tr("Tempo")); + + grid = new QGridLayout; + frame->setLayout(grid); + + m_samples = new QDoubleSpinBox; + m_samples->setSuffix(QString(" samples")); + m_samples->setDecimals(2); + m_samples->setMinimum(1); + m_samples->setMaximum(1e8); + m_samples->setValue(22050); + connect(m_samples, SIGNAL(valueChanged(double)), + this, SLOT(samplesChanged())); + + m_period = new QDoubleSpinBox; + m_period->setSuffix(QString(" ms")); + m_period->setDecimals(4); + m_period->setMinimum(1e-3); + m_period->setMaximum(100000); + m_period->setValue(500); + connect(m_period, SIGNAL(valueChanged(double)), + this, SLOT(periodChanged())); + + m_bpm = new QDoubleSpinBox; + m_bpm->setSuffix(QString(" bpm")); + m_bpm->setDecimals(4); + m_bpm->setMinimum(0.1); + m_bpm->setMaximum(1e6); + m_bpm->setValue(120); + connect(m_bpm, SIGNAL(valueChanged(double)), + this, SLOT(bpmChanged())); + + m_tempofreq = new QDoubleSpinBox; + m_tempofreq->setSuffix(QString(" beats/sec")); + m_tempofreq->setDecimals(4); + m_tempofreq->setMinimum(1e-3); + m_tempofreq->setMaximum(1e5); + m_tempofreq->setValue(0.5); + + connect(m_tempofreq, SIGNAL(valueChanged(double)), + this, SLOT(tempofreqChanged())); + + m_samplerate = new QComboBox; + QList rates; + rates << 8000; + for (int i = 1; i <= 16; i *= 2) { + rates << 11025 * i << 12000 * i; + } + foreach (int r, rates) { + m_samplerate->addItem(QString("%1 Hz").arg(r)); + } + connect(m_samplerate, SIGNAL(currentIndexChanged(int)), + this, SLOT(samplerateChanged())); + m_samplerate->setCurrentText("44100 Hz"); + + connect(Preferences::getInstance(), + SIGNAL(propertyChanged(PropertyContainer::PropertyName)), + this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); + + row = 0; + + grid->setRowStretch(row, 20); + grid->setRowMinimumHeight(row, 8); + + ++row; + + grid->addWidget(new QLabel(tr("Beat period")), row, 0, 2, 1, Qt::AlignVCenter); + grid->addWidget(m_period, row, 1); + grid->addWidget(new QLabel(tr("=")), row, 2, 2, 1, Qt::AlignVCenter); + + grid->addWidget(m_tempofreq, row, 3); + + grid->addWidget(new QLabel(tr("at")), row, 4, 2, 1, Qt::AlignVCenter); + grid->addWidget(m_samplerate, row, 5, 2, 1, Qt::AlignVCenter); + + ++row; + + grid->addWidget(m_samples, row, 1); + grid->addWidget(m_bpm, row, 3); + + ++row; + + grid->setRowStretch(row, 20); + grid->setRowMinimumHeight(row, 8); + + updatePitchesFromFreq(); + updatePitchPrefsLabel(); + updateTempiFromSamples(); +} + +UnitConverter::~UnitConverter() +{ +} + +void +UnitConverter::setTo(QSpinBox *box, int value) +{ + box->blockSignals(true); + if (value < box->minimum() || value > box->maximum()) { + QPalette p; + p.setColor(QPalette::Text, Qt::red); + box->setPalette(p); + } else { + box->setPalette(QPalette()); + } + box->setValue(value); + box->blockSignals(false); +} + +void +UnitConverter::setTo(QDoubleSpinBox *box, double value) +{ + box->blockSignals(true); + if (value < box->minimum() || value > box->maximum()) { + QPalette p; + p.setColor(QPalette::Text, Qt::red); + box->setPalette(p); + } else { + box->setPalette(QPalette()); + } + box->setValue(value); + box->blockSignals(false); +} + +void +UnitConverter::preferenceChanged(PropertyContainer::PropertyName) +{ + updatePitchesFromFreq(); + updatePitchPrefsLabel(); +} + +void +UnitConverter::updatePitchPrefsLabel() +{ + m_pitchPrefsLabel->setText + (tr("With concert-A tuning frequency at %1 Hz, and " + "middle C residing in octave %2.\n" + "(These can be changed in the application preferences.)") + .arg(Preferences::getInstance()->getTuningFrequency()) + .arg(Preferences::getInstance()->getOctaveOfMiddleC())); +} + +void +UnitConverter::freqChanged() +{ + updatePitchesFromFreq(); +} + +void +UnitConverter::midiChanged() +{ + double freq = Pitch::getFrequencyForPitch(m_midi->value(), m_cents->value()); + m_freq->setValue(freq); +} + +void +UnitConverter::noteChanged() +{ + int pitch = Pitch::getPitchForNoteAndOctave(m_note->currentIndex(), + m_octave->value()); + double freq = Pitch::getFrequencyForPitch(pitch, m_cents->value()); + m_freq->setValue(freq); +} + +void +UnitConverter::octaveChanged() +{ + int pitch = Pitch::getPitchForNoteAndOctave(m_note->currentIndex(), + m_octave->value()); + double freq = Pitch::getFrequencyForPitch(pitch, m_cents->value()); + m_freq->setValue(freq); +} + +void +UnitConverter::centsChanged() +{ + double freq = Pitch::getFrequencyForPitch(m_midi->value(), m_cents->value()); + m_freq->setValue(freq); +} + +void +UnitConverter::updatePitchesFromFreq() +{ + double cents = 0; + int pitch = Pitch::getPitchForFrequency(m_freq->value(), ¢s); + int note, octave; + Pitch::getNoteAndOctaveForPitch(pitch, note, octave); + + cerr << "pitch " << pitch << " note " << note << " octave " << octave << " cents " << cents << endl; + + setTo(m_midi, pitch); + setTo(m_cents, cents); + setTo(m_octave, octave); + + m_note->blockSignals(true); + m_note->setCurrentIndex(note); + m_note->blockSignals(false); +} + +void +UnitConverter::samplesChanged() +{ + updateTempiFromSamples(); +} + +void +UnitConverter::periodChanged() +{ + double rate = getSampleRate(); + double sec = m_period->value() / 1000.0; + double samples = rate * sec; + m_samples->setValue(samples); +} + +void +UnitConverter::bpmChanged() +{ + double rate = getSampleRate(); + double sec = 60.0 / m_bpm->value(); + double samples = rate * sec; + m_samples->setValue(samples); +} + +void +UnitConverter::tempofreqChanged() +{ + double rate = getSampleRate(); + double samples = rate / m_tempofreq->value(); + m_samples->setValue(samples); +} + +void +UnitConverter::samplerateChanged() +{ + // Preserve the beat period in seconds, here, not in samples + periodChanged(); +} + +double +UnitConverter::getSampleRate() +{ + return double(atoi(m_samplerate->currentText().toLocal8Bit().data())); +} + +void +UnitConverter::updateTempiFromSamples() +{ + double samples = m_samples->value(); + double rate = getSampleRate(); + + cerr << samples << " samples at rate " << rate << endl; + + double sec = samples / rate; + double hz = rate / samples; + double bpm = 60.0 / sec; + + setTo(m_bpm, bpm); + setTo(m_period, sec * 1000.0); + setTo(m_tempofreq, hz); +} + + diff -r b62c2785ed83 -r 4968bbaf1ed8 widgets/UnitConverter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/UnitConverter.h Mon Mar 23 10:04:51 2015 +0000 @@ -0,0 +1,73 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef UNIT_CONVERTER_H +#define UNIT_CONVERTER_H + +#include + +#include "base/PropertyContainer.h" + +class QSpinBox; +class QDoubleSpinBox; +class QComboBox; +class QLabel; + +class UnitConverter : public QDialog +{ + Q_OBJECT + +public: + UnitConverter(QWidget *parent = 0); + virtual ~UnitConverter(); + +private slots: + void freqChanged(); + void midiChanged(); + void noteChanged(); + void octaveChanged(); + void centsChanged(); + + void samplesChanged(); + void periodChanged(); + void bpmChanged(); + void tempofreqChanged(); + void samplerateChanged(); + + void preferenceChanged(PropertyContainer::PropertyName); + +private: + QDoubleSpinBox *m_freq; + QSpinBox *m_midi; + QComboBox *m_note; + QSpinBox *m_octave; + QDoubleSpinBox *m_cents; + QLabel *m_pitchPrefsLabel; + void updatePitchesFromFreq(); + void updatePitchPrefsLabel(); + + QDoubleSpinBox *m_samples; + QDoubleSpinBox *m_period; + QDoubleSpinBox *m_bpm; + QDoubleSpinBox *m_tempofreq; + QComboBox *m_samplerate; + void updateTempiFromSamples(); + + double getSampleRate(); + + void setTo(QSpinBox *, int); + void setTo(QDoubleSpinBox *, double); +}; + +#endif