comparison layer/SpectrogramLayer.cpp @ 25:dcdb21b62dbb

* Refactor sparse models. Previously the 1D and time-value models duplicated a lot of code; now there is a base class (SparseModel) templated on the stored point type, and the subclasses define point types with the necessary characteristics. * Add NoteModel, a new SparseModel subclass. * Reorganise local feature description display. Instead of asking the layer to draw its own, just query it for a textual description and draw that in Pane. Greatly simplifies this part of the layer code. * Add local feature descriptions to colour 3D plot and waveform layers. * Add pitch in MIDI-pitch-and-cents to spectrogram layer. * Give AudioGenerator its own mutex to shorten lock times in CallbackPlaySource. * Minor adjustments to layers menu &c
author Chris Cannam
date Thu, 02 Feb 2006 16:10:19 +0000
parents 6b794a2af3d9
children 202d1dca67d2
comparison
equal deleted inserted replaced
24:6b794a2af3d9 25:dcdb21b62dbb
1470 m_pixmapCacheZoomLevel = zoomLevel; 1470 m_pixmapCacheZoomLevel = zoomLevel;
1471 1471
1472 #ifdef DEBUG_SPECTROGRAM_REPAINT 1472 #ifdef DEBUG_SPECTROGRAM_REPAINT
1473 std::cerr << "SpectrogramLayer::paint() returning" << std::endl; 1473 std::cerr << "SpectrogramLayer::paint() returning" << std::endl;
1474 #endif 1474 #endif
1475
1476 //!!! drawLocalFeatureDescription(paint);
1477 } 1475 }
1478 1476
1479 int 1477 int
1480 SpectrogramLayer::getCompletion() const 1478 SpectrogramLayer::getCompletion() const
1481 { 1479 {
1483 size_t completion = m_fillThread->getFillCompletion(); 1481 size_t completion = m_fillThread->getFillCompletion();
1484 // std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl; 1482 // std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl;
1485 return completion; 1483 return completion;
1486 } 1484 }
1487 1485
1488 QRect
1489 SpectrogramLayer::getFeatureDescriptionRect(QPainter &paint, QPoint pos) const
1490 {
1491 if (!m_model || !m_model->isOK()) return QRect();
1492
1493 QString timeLabel = tr("Time: ");
1494 QString freqLabel = tr("Hz: ");
1495 QString dBLabel = tr("dB: ");
1496
1497 // assume time is widest
1498 RealTime rtMin, rtMax;
1499 if (!getXBinSourceRange(pos.x(), rtMin, rtMax)) return QRect();
1500 QString timeMinText = QString("%1").arg(rtMin.toText(true).c_str());
1501 QString timeMaxText = QString(" - %1").arg(rtMax.toText(true).c_str());
1502
1503 QFontMetrics metrics = paint.fontMetrics();
1504
1505 int labelwidth =
1506 std::max(std::max(metrics.width(timeLabel),
1507 metrics.width(freqLabel)),
1508 metrics.width(dBLabel));
1509
1510 int boxwidth = labelwidth +
1511 metrics.width(timeMinText) + metrics.width(timeMaxText);
1512
1513 int fontHeight = metrics.height();
1514 int boxheight = fontHeight * 3 + 4;
1515
1516 return QRect(0, 0, boxwidth + 20, boxheight + 15);
1517 }
1518
1519 void
1520 SpectrogramLayer::paintLocalFeatureDescription(QPainter &paint,
1521 QRect rect, QPoint pos) const
1522 {
1523 int x = pos.x();
1524 int y = pos.y();
1525
1526 if (!m_model || !m_model->isOK()) return;
1527
1528 float dbMin = 0, dbMax = 0;
1529 float freqMin = 0, freqMax = 0;
1530 RealTime rtMin, rtMax;
1531
1532 bool haveDb = false;
1533
1534 if (!getXBinSourceRange(x, rtMin, rtMax)) return;
1535 if (!getYBinSourceRange(y, freqMin, freqMax)) return;
1536 if (getXYBinSourceRange(x, y, dbMin, dbMax)) haveDb = true;
1537
1538 QString timeLabel = tr("Time: ");
1539 QString freqLabel = tr("Hz: ");
1540 QString dBLabel = tr("dB: ");
1541
1542 QString timeMinText = QString("%1").arg(rtMin.toText(true).c_str());
1543 QString timeMaxText = QString(" - %1").arg(rtMax.toText(true).c_str());
1544
1545 QString freqMinText = QString("%1").arg(freqMin);
1546 QString freqMaxText = "";
1547 if (freqMax != freqMin) {
1548 freqMaxText = QString(" - %1").arg(freqMax);
1549 }
1550
1551 QString dBMinText = "";
1552 QString dBMaxText = "";
1553
1554 if (haveDb) {
1555 int dbmxi = int(dbMax - 0.001);
1556 int dbmni = int(dbMin - 0.001);
1557 dBMinText = QString("%1").arg(dbmni);
1558 if (dbmxi != dbmni) dBMaxText = QString(" - %1").arg(dbmxi);
1559 }
1560
1561 QFontMetrics metrics = paint.fontMetrics();
1562
1563 int labelwidth =
1564 std::max(std::max(metrics.width(timeLabel),
1565 metrics.width(freqLabel)),
1566 metrics.width(dBLabel));
1567
1568 int minwidth =
1569 std::max(std::max(metrics.width(timeMinText),
1570 metrics.width(freqMinText)),
1571 metrics.width(dBMinText));
1572
1573 int maxwidth =
1574 std::max(std::max(metrics.width(timeMaxText),
1575 metrics.width(freqMaxText)),
1576 metrics.width(dBMaxText));
1577
1578 int boxwidth = labelwidth + minwidth + maxwidth;
1579
1580 int fontAscent = metrics.ascent();
1581 int fontHeight = metrics.height();
1582
1583 int boxheight = fontHeight * 3 + 4;
1584
1585 // paint.setPen(Qt::white);
1586 // paint.setBrush(Qt::NoBrush);
1587
1588 //!!! int xbase = m_view->width() - boxwidth - 20;
1589 int xbase = rect.x() + 5;
1590 int ybase = rect.y() + 5;
1591
1592 paint.drawRect(xbase, ybase, boxwidth + 10,
1593 boxheight + 10 - metrics.descent() + 1);
1594
1595 paint.drawText(xbase + 5 + labelwidth - metrics.width(timeLabel),
1596 ybase + 5 + fontAscent, timeLabel);
1597
1598 paint.drawText(xbase + 5 + labelwidth - metrics.width(freqLabel),
1599 ybase + 7 + fontAscent + fontHeight, freqLabel);
1600
1601 paint.drawText(xbase + 5 + labelwidth - metrics.width(dBLabel),
1602 ybase + 9 + fontAscent + fontHeight * 2, dBLabel);
1603
1604 paint.drawText(xbase + 5 + labelwidth + minwidth - metrics.width(timeMinText),
1605 ybase + 5 + fontAscent, timeMinText);
1606
1607 paint.drawText(xbase + 5 + labelwidth + minwidth - metrics.width(freqMinText),
1608 ybase + 7 + fontAscent + fontHeight, freqMinText);
1609
1610 paint.drawText(xbase + 5 + labelwidth + minwidth - metrics.width(dBMinText),
1611 ybase + 9 + fontAscent + fontHeight * 2, dBMinText);
1612
1613 paint.drawText(xbase + 5 + labelwidth + minwidth,
1614 ybase + 5 + fontAscent, timeMaxText);
1615
1616 paint.drawText(xbase + 5 + labelwidth + minwidth,
1617 ybase + 7 + fontAscent + fontHeight, freqMaxText);
1618
1619 paint.drawText(xbase + 5 + labelwidth + minwidth,
1620 ybase + 9 + fontAscent + fontHeight * 2, dBMaxText);
1621 }
1622
1623 int 1486 int
1624 SpectrogramLayer::getNearestFeatureFrame(int frame, 1487 SpectrogramLayer::getNearestFeatureFrame(int frame,
1625 size_t &resolution, 1488 size_t &resolution,
1626 bool snapRight) const 1489 bool snapRight) const
1627 { 1490 {
1629 int snapFrame = (frame / resolution) * resolution; 1492 int snapFrame = (frame / resolution) * resolution;
1630 if (snapRight) snapFrame += resolution; 1493 if (snapRight) snapFrame += resolution;
1631 return snapFrame; 1494 return snapFrame;
1632 } 1495 }
1633 1496
1634 /*!!! 1497 QString
1635 1498 SpectrogramLayer::getFeatureDescription(QPoint &pos) const
1636 bool 1499 {
1637 SpectrogramLayer::identifyLocalFeatures(bool on, int x, int y) 1500 int x = pos.x();
1638 { 1501 int y = pos.y();
1639 return true; //!!! 1502
1640 1503 if (!m_model || !m_model->isOK()) return "";
1641 m_identify = on;
1642 m_identifyX = x;
1643 m_identifyY = y;
1644
1645 m_view->update();
1646 */
1647 /*
1648 if (!m_model || !m_model->isOK()) return false;
1649
1650 std::cerr << "SpectrogramLayer::identifyLocalFeatures(" << on << "," << x << "," << y << ")" << std::endl;
1651 1504
1652 float dbMin = 0, dbMax = 0; 1505 float dbMin = 0, dbMax = 0;
1653 float freqMin = 0, freqMax = 0; 1506 float freqMin = 0, freqMax = 0;
1507 QString pitchMin, pitchMax;
1654 RealTime rtMin, rtMax; 1508 RealTime rtMin, rtMax;
1655 1509
1656 if (getXBinSourceRange(x, rtMin, rtMax)) { 1510 bool haveDb = false;
1657 std::cerr << "Times: " << rtMin << " -> " << rtMax << std::endl; 1511
1658 } else return false; 1512 if (!getXBinSourceRange(x, rtMin, rtMax)) return "";
1659 1513 if (!getYBinSourceRange(y, freqMin, freqMax)) return "";
1660 if (getYBinSourceRange(y, freqMin, freqMax)) { 1514 if (getXYBinSourceRange(x, y, dbMin, dbMax)) haveDb = true;
1661 std::cerr << "Frequencies: " << freqMin << " -> " << freqMax << std::endl; 1515
1662 } else return false; 1516 //!!! want to actually do a one-off FFT to recalculate the dB value!
1663 1517
1664 if (getXYBinSourceRange(x, y, dbMin, dbMax)) { 1518 QString text;
1665 std::cerr << "dB: " << dbMin << " -> " << dbMax << std::endl; 1519
1666 } 1520 if (rtMin != rtMax) {
1667 1521 text += tr("Time:\t%1 - %2\n")
1668 m_identifyX = x; 1522 .arg(rtMin.toText(true).c_str())
1669 m_identifyY = y; 1523 .arg(rtMax.toText(true).c_str());
1670 m_identify = true; 1524 } else {
1671 */ 1525 text += tr("Time:\t%1\n")
1672 /*!!! 1526 .arg(rtMin.toText(true).c_str());
1673 return true; 1527 }
1674 } 1528
1675 */ 1529 if (freqMin != freqMax) {
1530 text += tr("Frequency:\t%1 - %2 Hz\nPitch:\t%3 - %4\n")
1531 .arg(freqMin)
1532 .arg(freqMax)
1533 .arg(Pitch::getPitchLabelForFrequency(freqMin))
1534 .arg(Pitch::getPitchLabelForFrequency(freqMax));
1535 } else {
1536 text += tr("Frequency:\t%1 Hz\nPitch:\t%2\n")
1537 .arg(freqMin)
1538 .arg(Pitch::getPitchLabelForFrequency(freqMin));
1539 }
1540
1541 if (haveDb) {
1542 if (lrintf(dbMin) != lrintf(dbMax)) {
1543 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax));
1544 } else {
1545 text += tr("dB:\t%1").arg(lrintf(dbMin));
1546 }
1547 }
1548
1549 return text;
1550 }
1551
1676 int 1552 int
1677 SpectrogramLayer::getVerticalScaleWidth(QPainter &paint) const 1553 SpectrogramLayer::getVerticalScaleWidth(QPainter &paint) const
1678 { 1554 {
1679 if (!m_model || !m_model->isOK()) return 0; 1555 if (!m_model || !m_model->isOK()) return 0;
1680 1556