diff layer/NoteLayer.cpp @ 101:0f36cdf407a6 sv1-v0.9rc1

* Make vertical scale alignment modes work in note layer as well as time-value layer, and several significant fixes to it * Make it possible to draw notes properly on the note layer * Show units (and frequencies etc in note layer's case) in the time-value and note layer description boxes * Minor fix to item edit dialog layout * Some minor menu rearrangement * Comment out a lot of debug output * Add SV website and reference URLs to Help menu, and add code to (attempt to) open them in the user's preferred browser
author Chris Cannam
date Fri, 12 May 2006 14:40:43 +0000
parents 0db5e7492ce8
children 571805759a66
line wrap: on
line diff
--- a/layer/NoteLayer.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/layer/NoteLayer.cpp	Fri May 12 14:40:43 2006 +0000
@@ -42,7 +42,7 @@
     m_editingPoint(0, 0.0, 0, tr("New Point")),
     m_editingCommand(0),
     m_colour(Qt::black),
-    m_verticalScale(MinMaxRangeScale)
+    m_verticalScale(AutoAlignScale)
 {
     
 }
@@ -60,7 +60,7 @@
     connect(m_model, SIGNAL(completionChanged()),
 	    this, SIGNAL(modelCompletionChanged()));
 
-    std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl;
+//    std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl;
 
     emit modelReplaced();
 }
@@ -114,7 +114,7 @@
     } else if (name == "Vertical Scale") {
 	
 	if (min) *min = 0;
-	if (max) *max = 2;
+	if (max) *max = 3;
 	
 	deft = int(m_verticalScale);
 
@@ -150,9 +150,10 @@
     } else if (name == "Vertical Scale") {
 	switch (value) {
 	default:
-	case 0: return tr("Note Range In Use");
-	case 1: return tr("MIDI Note Range");
-	case 2: return tr("Frequency");
+	case 0: return tr("Auto-Align");
+	case 1: return tr("Linear Scale");
+	case 2: return tr("Log Scale");
+	case 3: return tr("MIDI Note Range");
 	}
     }
     return tr("<unknown>");
@@ -206,12 +207,52 @@
 }
 
 bool
-NoteLayer::getValueExtents(float &min, float &max, QString &unit) const
+NoteLayer::shouldConvertMIDIToHz() const
+{
+    QString unit = m_model->getScaleUnits();
+    return (unit != "Hz");
+//    if (unit == "" ||
+//        unit.startsWith("MIDI") ||
+//        unit.startsWith("midi")) return true;
+//    return false;
+}
+
+bool
+NoteLayer::getValueExtents(float &min, float &max,
+                           bool &logarithmic, QString &unit) const
 {
     if (!m_model) return false;
     min = m_model->getValueMinimum();
     max = m_model->getValueMaximum();
-    unit = m_model->getScaleUnits();
+
+    if (shouldConvertMIDIToHz()) unit = "Hz";
+    else unit = m_model->getScaleUnits();
+
+    if (m_verticalScale == MIDIRangeScale ||
+        m_verticalScale == LogScale) logarithmic = true;
+
+    return true;
+}
+
+bool
+NoteLayer::getDisplayExtents(float &min, float &max) const
+{
+    if (!m_model || m_verticalScale == AutoAlignScale) return false;
+
+    if (m_verticalScale == MIDIRangeScale) {
+        min = Pitch::getFrequencyForPitch(0);
+        max = Pitch::getFrequencyForPitch(127);
+        return true;
+    }
+
+    min = m_model->getValueMinimum();
+    max = m_model->getValueMaximum();
+
+    if (shouldConvertMIDIToHz()) {
+        min = Pitch::getFrequencyForPitch(lrintf(min));
+        max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+    }
+
     return true;
 }
 
@@ -301,17 +342,38 @@
     RealTime rd = RealTime::frame2RealTime(note.duration,
 					   m_model->getSampleRate());
     
+    QString pitchText;
+
+    if (shouldConvertMIDIToHz()) {
+
+        int mnote = lrintf(note.value);
+        int cents = lrintf((note.value - mnote) * 100);
+        float freq = Pitch::getFrequencyForPitch(mnote, cents);
+        pitchText = QString("%1 (%2 Hz)")
+            .arg(Pitch::getPitchLabel(mnote, cents)).arg(freq);
+
+    } else if (m_model->getScaleUnits() == "Hz") {
+
+        pitchText = QString("%1 Hz (%2)")
+            .arg(note.value)
+            .arg(Pitch::getPitchLabelForFrequency(note.value));
+
+    } else {
+        pitchText = QString("%1 %2")
+            .arg(note.value).arg(m_model->getScaleUnits());
+    }
+
     QString text;
 
     if (note.label == "") {
 	text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
 	    .arg(rt.toText(true).c_str())
-	    .arg(note.value)
+	    .arg(pitchText)
 	    .arg(rd.toText(true).c_str());
     } else {
 	text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
 	    .arg(rt.toText(true).c_str())
-	    .arg(note.value)
+	    .arg(pitchText)
 	    .arg(rd.toText(true).c_str())
 	    .arg(note.label);
     }
@@ -393,66 +455,107 @@
     return found;
 }
 
-int
-NoteLayer::getYForValue(View *v, float value) const
+void
+NoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
 {
-    float min, max, h = v->height();
+    min = 0.0;
+    max = 0.0;
+    log = false;
 
-    switch (m_verticalScale) {
+    QString queryUnits;
+    if (shouldConvertMIDIToHz()) queryUnits = "Hz";
+    else queryUnits = m_model->getScaleUnits();
 
-    case MIDIRangeScale:
-	min = 0.0;
-	max = 127.0;
-	break;
+    if (m_verticalScale == AutoAlignScale) {
 
-    case MinMaxRangeScale:
-	min = m_model->getValueMinimum();
-	max = m_model->getValueMaximum();
-	break;
+        if (!v->getValueExtents(queryUnits, min, max, log)) {
 
-    case FrequencyScale:
-    
-	value = Pitch::getFrequencyForPitch(lrintf(value),
-					    value - lrintf(value));
+            min = m_model->getValueMinimum();
+            max = m_model->getValueMaximum();
 
-	// If we have a spectrogram layer on the same view as us, align
-	// ourselves with it...
-	for (int i = 0; i < v->getLayerCount(); ++i) {
-	    SpectrogramLayer *spectrogram = dynamic_cast<SpectrogramLayer *>
-		(v->getLayer(i));
-	    if (spectrogram) {
-		return spectrogram->getYForFrequency(v, value);
-	    }
-	}
+            if (shouldConvertMIDIToHz()) {
+                min = Pitch::getFrequencyForPitch(lrintf(min));
+                max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+            }
 
-	// ...otherwise just interpolate
-	std::cerr << "FrequencyScale: value in = " << value << std::endl;
-	min = m_model->getValueMinimum();
-	min = Pitch::getFrequencyForPitch(lrintf(min), min - lrintf(min));
-	max = m_model->getValueMaximum();
-	max = Pitch::getFrequencyForPitch(lrintf(max), max - lrintf(max));
-	std::cerr << "FrequencyScale: min = " << min << ", max = " << max << ", value = " << value << std::endl;
-	break;
+        } else if (log) {
+
+//            std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
+
+            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
+            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+        }
+
+    } else {
+
+        min = m_model->getValueMinimum();
+        max = m_model->getValueMaximum();
+
+        if (m_verticalScale == MIDIRangeScale) {
+            min = Pitch::getFrequencyForPitch(0);
+            max = Pitch::getFrequencyForPitch(127);
+        } else if (shouldConvertMIDIToHz()) {
+            min = Pitch::getFrequencyForPitch(lrintf(min));
+            max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+        }
+
+        if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
+            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
+            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+            log = true;
+        }
     }
 
-    if (max < min) max = min;
-    max = max + 1.0;
+    if (max == min) max = min + 1.0;
+}
 
-    return int(h - ((value - min) * h) / (max - min)) - 1;
+int
+NoteLayer::getYForValue(View *v, float val) const
+{
+    float min = 0.0, max = 0.0;
+    bool logarithmic = false;
+    int h = v->height();
+
+    getScaleExtents(v, min, max, logarithmic);
+
+//    std::cerr << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
+
+    if (shouldConvertMIDIToHz()) {
+        val = Pitch::getFrequencyForPitch(lrintf(val),
+                                          lrintf((val - lrintf(val)) * 100));
+//        std::cerr << "shouldConvertMIDIToHz true, val now = " << val << std::endl;
+    }
+
+    if (logarithmic) {
+        val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
+//        std::cerr << "logarithmic true, val now = " << val << std::endl;
+    }
+
+    int y = int(h - ((val - min) * h) / (max - min)) - 1;
+//    std::cerr << "y = " << y << std::endl;
+    return y;
 }
 
 float
 NoteLayer::getValueForY(View *v, int y) const
 {
-    //!!!
-
-    float min = m_model->getValueMinimum();
-    float max = m_model->getValueMaximum();
-    if (max == min) max = min + 1.0;
-
+    float min = 0.0, max = 0.0;
+    bool logarithmic = false;
     int h = v->height();
 
-    return min + (float(h - y) * float(max - min)) / h;
+    getScaleExtents(v, min, max, logarithmic);
+
+    float val = min + (float(h - y) * float(max - min)) / h;
+
+    if (logarithmic) {
+        val = pow(10, val);
+    }
+
+    if (shouldConvertMIDIToHz()) {
+        val = Pitch::getPitchForFrequency(val);
+    }
+
+    return val;
 }
 
 void
@@ -538,7 +641,7 @@
 void
 NoteLayer::drawStart(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model) return;
 
@@ -562,7 +665,7 @@
 void
 NoteLayer::drawDrag(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model || !m_editing) return;
 
@@ -570,18 +673,28 @@
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
-    float value = getValueForY(v, e->y());
+    float newValue = getValueForY(v, e->y());
+
+    long newFrame = m_editingPoint.frame;
+    long newDuration = frame - newFrame;
+    if (newDuration < 0) {
+        newFrame = frame;
+        newDuration = -newDuration;
+    } else if (newDuration == 0) {
+        newDuration = 1;
+    }
 
     m_editingCommand->deletePoint(m_editingPoint);
-    m_editingPoint.frame = frame;
-    m_editingPoint.value = value;
+    m_editingPoint.frame = newFrame;
+    m_editingPoint.value = newValue;
+    m_editingPoint.duration = newDuration;
     m_editingCommand->addPoint(m_editingPoint);
 }
 
 void
 NoteLayer::drawEnd(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
     m_editingCommand->finish();
     m_editingCommand = 0;
@@ -591,7 +704,7 @@
 void
 NoteLayer::editStart(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model) return;
 
@@ -612,7 +725,7 @@
 void
 NoteLayer::editDrag(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model || !m_editing) return;
 
@@ -636,7 +749,7 @@
 void
 NoteLayer::editEnd(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
 
     if (m_editingCommand) {