18 #include "base/AudioLevel.h" 20 #include "base/Profiler.h" 21 #include "base/RangeMapper.h" 22 #include "base/Strings.h" 27 #include "data/model/WaveformOversampler.h" 30 #include <QPainterPath> 32 #include <QTextStream> 48 m_autoNormalize(false),
50 m_channelMode(SeparateChannels),
54 m_middleLineHeight(0.5),
66 const ZoomConstraint *
69 auto model = ModelById::get(
m_model);
70 if (model)
return model->getZoomConstraint();
77 auto oldModel = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
78 auto newModel = ModelById::getAs<RangeSummarisableTimeValueModel>(modelId);
80 if (!modelId.isNone() && !newModel) {
81 throw std::logic_error(
"Not a RangeSummarisableTimeValueModel");
91 bool channelsChanged =
false;
95 channelsChanged =
true;
99 oldModel->getChannelCount() != newModel->getChannelCount()) {
100 channelsChanged =
true;
119 list.push_back(
"Scale");
120 list.push_back(
"Gain");
121 list.push_back(
"Normalize Visible Area");
123 list.push_back(
"Channels");
132 if (name ==
"Scale")
return tr(
"Scale");
133 if (name ==
"Gain")
return tr(
"Gain");
134 if (name ==
"Normalize Visible Area")
return tr(
"Normalize Visible Area");
135 if (name ==
"Channels")
return tr(
"Channels");
142 if (name ==
"Normalize Visible Area")
return "normalise";
149 if (name ==
"Gain")
return RangeProperty;
150 if (name ==
"Normalize Visible Area")
return ToggleProperty;
151 if (name ==
"Channels")
return ValueProperty;
152 if (name ==
"Scale")
return ValueProperty;
159 if (name ==
"Gain" ||
160 name ==
"Normalize Visible Area" ||
161 name ==
"Scale")
return tr(
"Scale");
167 int *min,
int *max,
int *deflt)
const 171 int garbage0, garbage1, garbage2;
172 if (!min) min = &garbage0;
173 if (!max) max = &garbage1;
174 if (!deflt) deflt = &garbage2;
176 if (name ==
"Gain") {
182 val = int(lrint(log10(
m_gain) * 20.0));
183 if (val < *min) val = *min;
184 if (val > *max) val = *max;
186 }
else if (name ==
"Normalize Visible Area") {
191 }
else if (name ==
"Channels") {
200 }
else if (name ==
"Scale") {
219 if (name ==
"Scale") {
222 case 0:
return tr(
"Linear");
223 case 1:
return tr(
"Meter");
224 case 2:
return tr(
"dB");
227 if (name ==
"Channels") {
230 case 0:
return tr(
"Separate");
231 case 1:
return tr(
"Mean");
232 case 2:
return tr(
"Butterfly");
241 if (name ==
"Gain") {
242 return new LinearRangeMapper(-50, 50, -25, 25, tr(
"dB"));
250 if (name ==
"Gain") {
251 setGain(
float(pow(10,
float(value)/20.0)));
252 }
else if (name ==
"Normalize Visible Area") {
254 }
else if (name ==
"Channels") {
258 }
else if (name ==
"Scale") {
273 if (
m_gain == gain)
return;
348 int completion = 100;
349 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
350 if (!model || !model->isOK())
return completion;
351 if (model->isReady(&completion))
return 100;
357 bool &log, QString &unit)
const 428 min = AudioLevel::dB_to_multiplier(
m_dBMin);
438 if (sample < 0.0)
return dBscale(-sample, m);
439 double dB = AudioLevel::multiplier_to_dB(sample);
441 if (dB > 0.0)
return m;
447 bool &merging,
bool &mixing)
451 if (channels == 0)
return 0;
453 int rawChannels = channels;
495 static float meterdbs[] = { -40, -30, -20, -15, -10,
496 -5, -3, -2, -1, -0.5, 0 };
500 int x,
int modelZoomLevel,
501 sv_frame_t &f0, sv_frame_t &f1)
const 503 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
504 if (!model)
return false;
514 f0 = f0 / modelZoomLevel;
515 f0 = f0 * modelZoomLevel;
517 if (v->
getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
522 f1 = f1 / modelZoomLevel;
523 f1 = f1 * modelZoomLevel;
526 return (f0 < model->getEndFrame());
532 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
533 if (!model)
return 0.f;
538 sv_frame_t modelStart = model->getStartFrame();
539 sv_frame_t modelEnd = model->getEndFrame();
541 sv_frame_t rangeStart, rangeEnd;
543 if (startFrame < modelStart) rangeStart = modelStart;
544 else rangeStart = startFrame;
546 if (endFrame < 0) rangeEnd = 0;
547 else if (endFrame > modelEnd) rangeEnd = modelEnd;
548 else rangeEnd = endFrame;
550 if (rangeEnd < rangeStart) rangeEnd = rangeStart;
552 RangeSummarisableTimeValueModel::Range range =
553 model->getSummary(channel, rangeStart, rangeEnd - rangeStart);
555 int minChannel = 0, maxChannel = 0;
556 bool mergingChannels =
false, mixingChannels =
false;
559 mergingChannels, mixingChannels);
561 if (mergingChannels || mixingChannels) {
563 RangeSummarisableTimeValueModel::Range otherRange =
564 model->getSummary(1, rangeStart, rangeEnd - rangeStart);
565 range.setMax(std::max(range.max(), otherRange.max()));
566 range.setMin(std::min(range.min(), otherRange.min()));
567 range.setAbsmean(std::min(range.absmean(), otherRange.absmean()));
571 return float(1.0 / std::max(fabs(range.max()), fabs(range.min())));
577 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
578 if (!model || !model->isOK()) {
584 #ifdef DEBUG_WAVEFORM_PAINT 585 Profiler profiler(
"WaveformLayer::paint",
true);
586 SVCERR <<
"WaveformLayer::paint (" << rect.x() <<
"," << rect.y()
587 <<
") [" << rect.width() <<
"x" << rect.height() <<
"]: zoom " << zoomLevel << endl;
590 int channels = 0, minChannel = 0, maxChannel = 0;
591 bool mergingChannels =
false, mixingChannels =
false;
594 mergingChannels, mixingChannels);
595 if (channels == 0)
return;
604 #ifdef DEBUG_WAVEFORM_PAINT 605 SVCERR <<
"WaveformLayer::paint: aggressive is true" << endl;
615 #ifdef DEBUG_WAVEFORM_PAINT 617 SVCERR <<
"WaveformLayer::paint: cache size " <<
m_cache->width() <<
"x" <<
m_cache->height() <<
" differs from view size " << w <<
"x" << h <<
": regenerating aggressive cache" << endl;
626 viewPainter.drawPixmap(rect, *
m_cache, rect);
632 paint->setPen(Qt::NoPen);
634 paint->drawRect(rect);
637 paint->setBrush(Qt::NoBrush);
640 paint = &viewPainter;
643 paint->setRenderHint(QPainter::Antialiasing,
true);
648 if (space > 1.0) space = 2.0 - space;
650 paint->translate(QPointF(0, yt));
651 paint->scale(1.0, space);
654 int x0 = 0, x1 = w - 1;
660 rect.adjust(-1, 0, 0, 0);
665 rect.adjust(0, 0, 1, 0);
678 int desiredBlockSize = 1;
679 if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
680 desiredBlockSize = zoomLevel.level;
682 int blockSize = model->getSummaryBlockSize(desiredBlockSize);
691 #ifdef DEBUG_WAVEFORM_PAINT 692 SVCERR <<
"Painting waveform from " << frame0 <<
" to " << frame1 <<
" (" << (x1-x0+1) <<
" pixels at zoom " << zoomLevel <<
" and model zoom " << blockSize <<
")" << endl;
700 for (
int ch = minChannel; ch <= maxChannel; ++ch) {
707 if (v->
getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
709 mixingChannels || mergingChannels,
714 mixingChannels || mergingChannels,
719 if (!ranges.empty()) {
720 for (
int ch = minChannel; ch <= maxChannel; ++ch) {
737 viewPainter.drawPixmap(rect, *
m_cache, rect);
743 bool mixingOrMerging,
744 sv_frame_t frame0, sv_frame_t frame1,
748 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
751 for (
int ch = minChannel; ch <= maxChannel; ++ch) {
752 ranges.push_back({});
753 model->getSummaries(ch, frame0, frame1 - frame0,
754 ranges[ch - minChannel], blockSize);
755 #ifdef DEBUG_WAVEFORM_PAINT 756 SVCERR <<
"channel " << ch <<
": " << ranges[ch - minChannel].size() <<
" ranges from " << frame0 <<
" to " << frame1 <<
" at zoom level " << blockSize << endl;
760 if (mixingOrMerging) {
761 if (minChannel != 0 || maxChannel != 0) {
762 throw std::logic_error(
"Internal error: min & max channels should be 0 when merging or mixing all channels");
764 ranges.push_back({});
766 (1, frame0, frame1 - frame0, ranges[1], blockSize);
768 ranges.push_back(ranges[0]);
775 bool mixingOrMerging,
776 sv_frame_t frame0, sv_frame_t frame1,
780 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
783 if (mixingOrMerging) {
784 if (minChannel != 0 || maxChannel != 0) {
785 throw std::logic_error(
"Internal error: min & max channels should be 0 when merging or mixing all channels");
791 (0, 1,
false, frame0, frame1, oversampleBy, ranges);
796 (0, 0,
false, frame0, frame1, oversampleBy, ranges);
797 ranges.push_back(ranges[0]);
805 sv_frame_t tail = 16;
806 sv_frame_t startFrame = model->getStartFrame();
807 sv_frame_t endFrame = model->getEndFrame();
809 sv_frame_t rf0 = frame0 - tail;
810 if (rf0 < startFrame) {
814 sv_frame_t rf1 = frame1 + tail;
815 if (rf1 >= endFrame) {
819 SVCERR <<
"WARNING: getOversampledRanges: rf1 (" << rf1 <<
") <= rf0 (" 820 << rf0 <<
")" << endl;
824 for (
int ch = minChannel; ch <= maxChannel; ++ch) {
825 floatvec_t oversampled = WaveformOversampler::getOversampledData
826 (*model, ch, frame0, frame1 - frame0, oversampleBy);
827 RangeSummarisableTimeValueModel::RangeBlock rr;
828 for (
float v: oversampled) {
829 RangeSummarisableTimeValueModel::Range r;
833 ranges.push_back(rr);
835 #ifdef DEBUG_WAVEFORM_PAINT 836 SVCERR <<
"getOversampledRanges: " << frame0 <<
" -> " << frame1
837 <<
" (" << frame1 - frame0 <<
"-frame range) at ratio " 838 << oversampleBy <<
" with tail " << tail
839 <<
" -> got " << oversampled.size()
840 <<
" oversampled values for channel " << ch
841 <<
", from which returning " << rr.size() <<
" ranges" << endl;
858 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
861 int x0 = rect.left();
864 int x1 = rect.right();
865 int y1 = rect.bottom();
869 int channels = 0, minChannel = 0, maxChannel = 0;
870 bool mergingChannels =
false, mixingChannels =
false;
873 mergingChannels, mixingChannels);
874 if (channels == 0)
return;
877 QColor midColour = baseColour;
879 if (midColour == Qt::black) {
880 midColour = Qt::gray;
882 midColour = midColour.lighter(150);
884 midColour = midColour.lighter(50);
889 int m = (h / channels) / 2;
890 int my = m + (((ch - minChannel) * h) / channels);
892 #ifdef DEBUG_WAVEFORM_PAINT 893 SVCERR <<
"ch = " << ch <<
", channels = " << channels <<
", m = " << m <<
", my = " << my <<
", h = " << h << endl;
896 if (my - m > y1 || my + m < y0)
return;
901 my = m + (((ch - minChannel) * h) / channels);
905 paint->setPen(QPen(midColour, 0));
906 paint->drawLine(QPointF(x0, my + 0.5), QPointF(x1, my + 0.5));
910 int rangeix = ch - minChannel;
912 #ifdef DEBUG_WAVEFORM_PAINT 913 SVCERR <<
"paint channel " << ch <<
": frame0 = " << frame0 <<
", frame1 = " << frame1 <<
", blockSize = " << blockSize <<
", have " << ranges.size() <<
" range blocks of which ours is index " << rangeix <<
" with " << ranges[rangeix].size() <<
" ranges in it" << endl;
918 QPainterPath waveformPath;
919 QPainterPath meanPath;
920 QPainterPath clipPath;
921 vector<QPointF> individualSamplePoints;
923 bool firstPoint =
true;
924 double prevRangeBottom = 0, prevRangeTop = 0;
926 for (
int x = x0; x <= x1; ++x) {
931 bool showIndividualSample =
false;
933 if (v->
getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
938 i0 = (f0 - frame0) / blockSize;
939 i1 = (f1 - frame0) / blockSize;
944 showIndividualSample = (x == xf0);
945 i0 = i1 = (f0 - frame0) * oversampleBy + (x - xf0);
954 #ifdef DEBUG_WAVEFORM_PAINT_BY_PIXEL 955 SVCERR <<
"WaveformLayer::paint: pixel " << x <<
": i0 " << i0 <<
" (f " << f0 <<
"), i1 " << i1 <<
" (f " << f1 <<
")" << endl;
959 SVCERR <<
"WaveformLayer::paint: ERROR: i1 " << i1 <<
" > i0 " << i0 <<
" plus one (zoom = " << v->
getZoomLevel() <<
", model zoom = " << blockSize <<
")" << endl;
962 const auto &r = ranges[rangeix];
963 RangeSummarisableTimeValueModel::Range range;
965 if (in_range_for(r, i0)) {
969 if (i1 > i0 && in_range_for(r, i1)) {
970 range.setMax(std::max(range.max(), r[i1].max()));
971 range.setMin(std::min(range.min(), r[i1].min()));
972 range.setAbsmean((range.absmean() + r[i1].absmean()) / 2);
976 #ifdef DEBUG_WAVEFORM_PAINT 977 SVCERR <<
"No (or not enough) ranges for index i0 = " << i0 <<
" (there are " << r.size() <<
" range(s))" << endl;
982 double rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
984 if (mergingChannels && ranges.size() > 1) {
986 const auto &other = ranges[1];
988 if (in_range_for(other, i0)) {
990 range.setMax(fabsf(range.max()));
991 range.setMin(-fabsf(other[i0].max()));
993 ((range.absmean() + other[i0].absmean()) / 2);
995 if (i1 > i0 && in_range_for(other, i1)) {
997 range.setMin(std::min(range.min(),
998 -fabsf(other[i1].max())));
1002 }
else if (mixingChannels && ranges.size() > 1) {
1004 const auto &other = ranges[1];
1006 if (in_range_for(other, i0)) {
1008 range.setMax((range.max() + other[i0].max()) / 2);
1009 range.setMin((range.min() + other[i0].min()) / 2);
1010 range.setAbsmean((range.absmean() + other[i0].absmean()) / 2);
1017 rangeBottom = range.min() * gain * m;
1018 rangeTop = range.max() * gain * m;
1019 meanBottom = range.absmean() * gain * (-m);
1020 meanTop = range.absmean() * gain * m;
1024 if (!mergingChannels) {
1025 double db0 =
dBscale(range.min() * gain, m);
1026 double db1 =
dBscale(range.max() * gain, m);
1027 rangeTop = std::max(db0, db1);
1028 meanTop = std::min(db0, db1);
1029 if (mixingChannels) rangeBottom = meanTop;
1030 else rangeBottom =
dBscale(range.absmean() * gain, m);
1031 meanBottom = rangeBottom;
1033 rangeBottom = -
dBscale(range.min() * gain, m);
1034 rangeTop =
dBscale(range.max() * gain, m);
1035 meanBottom = -
dBscale(range.absmean() * gain, m);
1036 meanTop =
dBscale(range.absmean() * gain, m);
1041 if (!mergingChannels) {
1042 double r0 = std::abs(AudioLevel::multiplier_to_preview
1043 (range.min() * gain, m));
1044 double r1 = std::abs(AudioLevel::multiplier_to_preview
1045 (range.max() * gain, m));
1046 rangeTop = std::max(r0, r1);
1047 meanTop = std::min(r0, r1);
1048 if (mixingChannels) rangeBottom = meanTop;
1049 else rangeBottom = AudioLevel::multiplier_to_preview
1050 (range.absmean() * gain, m);
1051 meanBottom = rangeBottom;
1053 rangeBottom = -AudioLevel::multiplier_to_preview
1054 (range.min() * gain, m);
1055 rangeTop = AudioLevel::multiplier_to_preview
1056 (range.max() * gain, m);
1057 meanBottom = -AudioLevel::multiplier_to_preview
1058 (range.absmean() * gain, m);
1059 meanTop = AudioLevel::multiplier_to_preview
1060 (range.absmean() * gain, m);
1065 rangeBottom = my - rangeBottom;
1066 rangeTop = my - rangeTop;
1067 meanBottom = my - meanBottom;
1068 meanTop = my - meanTop;
1070 bool clipped =
false;
1072 if (rangeTop < my - m) { rangeTop = my - m; }
1073 if (rangeTop > my + m) { rangeTop = my + m; }
1074 if (rangeBottom < my - m) { rangeBottom = my - m; }
1075 if (rangeBottom > my + m) { rangeBottom = my + m; }
1077 if (range.max() <= -1.0 || range.max() >= 1.0) {
1083 meanTop = meanTop - 0.5;
1084 meanBottom = meanBottom + 0.5;
1086 if (meanTop <= rangeTop + 1.0) {
1087 meanTop = rangeTop + 1.0;
1090 meanBottom = rangeBottom - 1.0;
1092 if (meanTop > meanBottom - 1.0) {
1096 #ifdef DEBUG_WAVEFORM_PAINT_BY_PIXEL 1097 SVCERR <<
"range " << rangeBottom <<
" -> " << rangeTop <<
", means " << meanBottom <<
" -> " << meanTop <<
", raw range " << range.min() <<
" -> " << range.max() << endl;
1100 double rangeMiddle = (rangeTop + rangeBottom) / 2.0;
1101 bool trivialRange = (fabs(rangeTop - rangeBottom) < 1.0);
1102 double px = x + 0.5;
1104 if (showIndividualSample) {
1105 individualSamplePoints.push_back(QPointF(px, rangeTop));
1106 if (!trivialRange) {
1108 individualSamplePoints.push_back(QPointF(px, rangeBottom));
1112 bool contiguous =
true;
1113 if (rangeTop > prevRangeBottom + 0.5 ||
1114 rangeBottom < prevRangeTop - 0.5) {
1118 if (firstPoint || (contiguous && !trivialRange)) {
1119 waveformPath.moveTo(QPointF(px, rangeTop));
1120 waveformPath.lineTo(QPointF(px, rangeBottom));
1121 waveformPath.moveTo(QPointF(px, rangeMiddle));
1123 waveformPath.lineTo(QPointF(px, rangeMiddle));
1124 if (!trivialRange) {
1125 waveformPath.lineTo(QPointF(px, rangeTop));
1126 waveformPath.lineTo(QPointF(px, rangeBottom));
1127 waveformPath.lineTo(QPointF(px, rangeMiddle));
1132 prevRangeTop = rangeTop;
1133 prevRangeBottom = rangeBottom;
1136 meanPath.moveTo(QPointF(px, meanBottom));
1137 meanPath.lineTo(QPointF(px, meanTop));
1142 clipPath.moveTo(QPointF(px, rangeMiddle));
1143 clipPath.lineTo(QPointF(px+1, rangeMiddle));
1145 clipPath.moveTo(QPointF(px, rangeBottom));
1146 clipPath.lineTo(QPointF(px, rangeTop));
1151 double penWidth = 1.0;
1152 if (v->
getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
1156 if (model->isReady()) {
1157 paint->setPen(QPen(baseColour, penWidth));
1159 paint->setPen(QPen(midColour, penWidth));
1161 paint->drawPath(waveformPath);
1163 if (!clipPath.isEmpty()) {
1166 getContrastingColour(
m_colour), penWidth));
1167 paint->drawPath(clipPath);
1171 if (!meanPath.isEmpty()) {
1173 paint->setPen(QPen(midColour, penWidth));
1174 paint->drawPath(meanPath);
1178 if (!individualSamplePoints.empty()) {
1180 if (v->
getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
1186 paint->setPen(QPen(baseColour, penWidth));
1187 for (QPointF p: individualSamplePoints) {
1188 paint->drawRect(QRectF(p.x() - sz/2, p.y() - sz/2, sz, sz));
1200 int x0 = rect.left();
1201 int x1 = rect.right();
1212 paint->setPen(QColor(240, 240, 240));
1214 for (
int i = 1; i < n; ++i) {
1216 double val = 0.0, nval = 0.0;
1221 val = (i * gain) / n;
1222 if (i > 0) nval = -val;
1226 val = AudioLevel::dB_to_multiplier(
meterdbs[i]) * gain;
1230 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
1234 if (val < -1.0 || val > 1.0)
continue;
1238 if (py >= 0 && abs(y - py) < 10)
continue;
1246 paint->drawLine(x0, y, x1, y);
1248 paint->drawLine(x0, ny, x1, ny);
1259 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
1260 if (!model || !model->isOK())
return "";
1264 int desiredBlockSize = 1;
1265 if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
1266 desiredBlockSize = zoomLevel.level;
1269 int blockSize = model->getSummaryBlockSize(desiredBlockSize);
1276 RealTime rt0 = RealTime::frame2RealTime(f0, model->getSampleRate());
1277 RealTime rt1 = RealTime::frame2RealTime(f1, model->getSampleRate());
1279 if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
1280 text += tr(
"Time:\t%1 - %2")
1281 .arg(rt0.toText(
true).c_str())
1282 .arg(rt1.toText(
true).c_str());
1284 text += tr(
"Time:\t%1")
1285 .arg(rt0.toText(
true).c_str());
1288 int channels = 0, minChannel = 0, maxChannel = 0;
1289 bool mergingChannels =
false, mixingChannels =
false;
1292 mergingChannels, mixingChannels);
1293 if (channels == 0)
return "";
1295 for (
int ch = minChannel; ch <= maxChannel; ++ch) {
1297 RangeSummarisableTimeValueModel::RangeBlock ranges;
1298 model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
1300 if (ranges.empty())
continue;
1302 RangeSummarisableTimeValueModel::Range range = ranges[0];
1304 QString label = tr(
"Level:");
1305 if (minChannel != maxChannel) {
1306 if (ch == 0) label = tr(
"Left:");
1307 else if (ch == 1) label = tr(
"Right:");
1308 else label = tr(
"Channel %1").arg(ch + 1);
1311 bool singleValue =
false;
1314 if (fabs(range.min()) < 0.01) {
1317 singleValue = (min == max);
1319 int imin = int(lrint(range.min() * 10000));
1320 int imax = int(lrint(range.max() * 10000));
1321 singleValue = (imin == imax);
1322 min = double(imin)/10000;
1323 max = double(imax)/10000;
1326 int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()),
1327 fabsf(range.max())))
1331 text += tr(
"\n%1\t%2 - %3 (%4 dB peak)")
1332 .arg(label).arg(min).arg(max).arg(
double(db)/100);
1334 text += tr(
"\n%1\t%2 (%3 dB peak)")
1335 .arg(label).arg(min).arg(
double(db)/100);
1345 int channels = 0, minChannel = 0, maxChannel = 0;
1346 bool mergingChannels =
false, mixingChannels =
false;
1349 mergingChannels, mixingChannels);
1350 if (channels == 0)
return 0;
1351 if (maxChannel < minChannel || channel < minChannel)
return 0;
1354 int m = (h / channels) / 2;
1361 int my = m + (((channel - minChannel) * h) / channels);
1368 vy = int(m * value);
1372 vy = AudioLevel::multiplier_to_preview(value, m);
1388 int channels = 0, minChannel = 0, maxChannel = 0;
1389 bool mergingChannels =
false, mixingChannels =
false;
1392 mergingChannels, mixingChannels);
1393 if (channels == 0)
return 0;
1394 if (maxChannel < minChannel)
return 0;
1397 int m = (h / channels) / 2;
1404 channel = (y * channels) / h + minChannel;
1406 int my = m + (((channel - minChannel) * h) / channels);
1415 value = double(vy) / m;
1419 value = AudioLevel::preview_to_multiplier(vy, m);
1423 value = (-thresh * double(vy)) / m + thresh;
1424 value = AudioLevel::dB_to_multiplier(value);
1433 double &value, QString &unit)
const 1444 value = 10.0 * log10(value);
1445 if (value < thresh) value = thresh;
1446 }
else value = thresh;
1459 double &diff, QString &unit)
const 1476 if (v1 == v0) diff = thresh;
1478 if (v1 > v0) diff = v0 / v1;
1479 else diff = v1 / v0;
1481 diff = 10.0 * log10(diff);
1482 if (diff < thresh) diff = thresh;
1488 diff = fabs(v1 - v0);
1501 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 1504 return paint.fontMetrics().width(
"0.0") + 13;
1506 return std::max(paint.fontMetrics().width(tr(
"0dB")),
1507 paint.fontMetrics().width(Strings::minus_infinity)) + 13;
1514 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(
m_model);
1515 if (!model || !model->isOK()) {
1519 int channels = 0, minChannel = 0, maxChannel = 0;
1520 bool mergingChannels =
false, mixingChannels =
false;
1523 mergingChannels, mixingChannels);
1524 if (channels == 0)
return;
1526 int h = rect.height(), w = rect.width();
1527 int textHeight = paint.fontMetrics().height();
1528 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
1532 for (
int ch = minChannel; ch <= maxChannel; ++ch) {
1534 int lastLabelledY = -1;
1540 for (
int i = 0; i <= n; ++i) {
1542 double val = 0.0, nval = 0.0;
1548 val = (i * gain) / n;
1549 text = QString(
"%1").arg(
double(i) / n);
1550 if (i == 0) text =
"0.0";
1553 if (i == n) text =
"1.0";
1558 val = AudioLevel::dB_to_multiplier(
meterdbs[i]) * gain;
1559 text = QString(
"%1").arg(
meterdbs[i]);
1560 if (i == n) text = tr(
"0dB");
1562 text = Strings::minus_infinity;
1568 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
1569 text = QString(
"%1").arg(-(10*n) + i * 10);
1570 if (i == n) text = tr(
"0dB");
1572 text = Strings::minus_infinity;
1578 if (val < -1.0 || val > 1.0)
continue;
1587 bool spaceForLabel = (i == 0 ||
1588 abs(y - lastLabelledY) >= textHeight - 1);
1590 if (spaceForLabel) {
1594 tx = w - 10 - paint.fontMetrics().width(text);
1598 if (ty < paint.fontMetrics().ascent()) {
1599 ty = paint.fontMetrics().ascent();
1600 }
else if (ty > h - paint.fontMetrics().descent()) {
1601 ty = h - paint.fontMetrics().descent();
1605 paint.drawText(tx, ty, text);
1607 lastLabelledY = ty - toff;
1611 if (ty < paint.fontMetrics().ascent()) {
1612 ty = paint.fontMetrics().ascent();
1613 }
else if (ty > h - paint.fontMetrics().descent()) {
1614 ty = h - paint.fontMetrics().descent();
1618 paint.drawText(tx, ty, text);
1621 paint.drawLine(w - 7, y, w, y);
1622 if (ny != y) paint.drawLine(w - 7, ny, w, ny);
1626 paint.drawLine(w - 4, y, w, y);
1627 if (ny != y) paint.drawLine(w - 4, ny, w, ny);
1635 QString indent, QString extraAttributes)
const 1639 QString colourName, colourSpec, darkbg;
1641 (
m_colour, colourName, colourSpec, darkbg);
1643 s += QString(
"gain=\"%1\" " 1646 "channelMode=\"%4\" " 1649 "middleLineHeight=\"%7\" " 1650 "aggressive=\"%8\" " 1651 "autoNormalize=\"%9\"")
1673 float gain = attributes.value(
"gain").toFloat(&ok);
1676 bool showMeans = (attributes.value(
"showMeans") ==
"1" ||
1677 attributes.value(
"showMeans") ==
"true");
1681 attributes.value(
"channelMode").toInt(&ok);
1684 int channel = attributes.value(
"channel").toInt(&ok);
1687 Scale scale = (
Scale)attributes.value(
"scale").toInt(&ok);
1690 float middleLineHeight = attributes.value(
"middleLineHeight").toFloat(&ok);
1693 bool aggressive = (attributes.value(
"aggressive") ==
"1" ||
1694 attributes.value(
"aggressive") ==
"true");
1697 bool autoNormalize = (attributes.value(
"autoNormalize") ==
"1" ||
1698 attributes.value(
"autoNormalize") ==
"true");
1712 int val = int(lrint(log10(
m_gain) * 20.0) + 50);
1713 if (val < 0) val = 0;
1714 if (val > 100) val = 100;
1721 setGain(powf(10,
float(step - 50) / 20.f));
PropertyList getProperties() const override
virtual ZoomLevel getZoomLevel() const =0
Return the zoom level, i.e.
virtual QColor getForegroundQColor(LayerGeometryProvider *v) const
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
virtual double scaleSize(double size) const =0
virtual QColor getBaseQColor() const
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
virtual QRect getPaintRect() const =0
To be called from a layer, to obtain the extent of the surface that the layer is currently painting t...
void layerParametersChanged()
virtual sv_frame_t getStartFrame() const =0
Retrieve the first visible sample frame on the widget.
void verticalZoomChanged()
void getStringValues(int index, QString &colourName, QString &colourSpec, QString &darkbg) const
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
void connectSignals(ModelId)
virtual int getPaintHeight() const
PropertyType getPropertyType(const PropertyName &) const override
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
virtual ViewManager * getViewManager() const =0
bool shouldShowScaleGuides() const
QString getPropertyValueLabel(const PropertyName &, int value) const override
virtual QColor getBackgroundQColor(LayerGeometryProvider *v) const
virtual sv_frame_t getEndFrame() const =0
Retrieve the last visible sample frame on the widget.
virtual bool hasLightBackground() const =0
void setProperty(const PropertyName &, int value) override
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
virtual int getPaintWidth() const
QString getPropertyLabel(const PropertyName &) const override
static ColourDatabase * getInstance()