comparison layer/TimeValueLayer.cpp @ 374:64e84e5efb76 spectrogram-cache-rejig

* Merge from trunk
author Chris Cannam
date Wed, 27 Feb 2008 11:59:42 +0000
parents 4f4f38a11cd2
children
comparison
equal deleted inserted replaced
332:6440e280122e 374:64e84e5efb76
21 #include "base/LogRange.h" 21 #include "base/LogRange.h"
22 #include "base/ColourDatabase.h" 22 #include "base/ColourDatabase.h"
23 #include "view/View.h" 23 #include "view/View.h"
24 24
25 #include "data/model/SparseTimeValueModel.h" 25 #include "data/model/SparseTimeValueModel.h"
26 #include "data/model/Labeller.h"
26 27
27 #include "widgets/ItemEditDialog.h" 28 #include "widgets/ItemEditDialog.h"
28 #include "widgets/ListInputDialog.h" 29 #include "widgets/ListInputDialog.h"
29 30
30 #include "SpectrogramLayer.h" // for optional frequency alignment 31 #include "SpectrogramLayer.h" // for optional frequency alignment
33 #include <QPainter> 34 #include <QPainter>
34 #include <QPainterPath> 35 #include <QPainterPath>
35 #include <QMouseEvent> 36 #include <QMouseEvent>
36 #include <QRegExp> 37 #include <QRegExp>
37 #include <QTextStream> 38 #include <QTextStream>
39 #include <QMessageBox>
40 #include <QInputDialog>
38 41
39 #include <iostream> 42 #include <iostream>
40 #include <cmath> 43 #include <cmath>
41 44
42 TimeValueLayer::TimeValueLayer() : 45 TimeValueLayer::TimeValueLayer() :
536 if (!m_model || !m_model->isOK()) return; 539 if (!m_model || !m_model->isOK()) return;
537 540
538 int sampleRate = m_model->getSampleRate(); 541 int sampleRate = m_model->getSampleRate();
539 if (!sampleRate) return; 542 if (!sampleRate) return;
540 543
544 paint.setRenderHint(QPainter::Antialiasing, false);
545
541 // Profiler profiler("TimeValueLayer::paint", true); 546 // Profiler profiler("TimeValueLayer::paint", true);
542 547
543 int x0 = rect.left(), x1 = rect.right(); 548 int x0 = rect.left(), x1 = rect.right();
544 long frame0 = v->getFrameForX(x0); 549 long frame0 = v->getFrameForX(x0);
545 long frame1 = v->getFrameForX(x1); 550 long frame1 = v->getFrameForX(x1);
597 int y = getYForValue(v, p.value); 602 int y = getYForValue(v, p.value);
598 603
599 if (m_plotStyle != PlotSegmentation) { 604 if (m_plotStyle != PlotSegmentation) {
600 textY = y - paint.fontMetrics().height() 605 textY = y - paint.fontMetrics().height()
601 + paint.fontMetrics().ascent(); 606 + paint.fontMetrics().ascent();
607 if (textY < paint.fontMetrics().ascent() + 1) {
608 textY = paint.fontMetrics().ascent() + 1;
609 }
602 } 610 }
603 611
604 bool haveNext = false; 612 bool haveNext = false;
605 int nx = v->getXForFrame(v->getModelsEndFrame()); 613 int nx = v->getXForFrame(v->getModelsEndFrame());
606 // m_model->getEndFrame()); 614 // m_model->getEndFrame());
938 m_editingCommand = 0; 946 m_editingCommand = 0;
939 m_editing = false; 947 m_editing = false;
940 } 948 }
941 949
942 void 950 void
951 TimeValueLayer::eraseStart(View *v, QMouseEvent *e)
952 {
953 if (!m_model) return;
954
955 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
956 if (points.empty()) return;
957
958 m_editingPoint = *points.begin();
959
960 if (m_editingCommand) {
961 m_editingCommand->finish();
962 m_editingCommand = 0;
963 }
964
965 m_editing = true;
966 }
967
968 void
969 TimeValueLayer::eraseDrag(View *v, QMouseEvent *e)
970 {
971 }
972
973 void
974 TimeValueLayer::eraseEnd(View *v, QMouseEvent *e)
975 {
976 if (!m_model || !m_editing) return;
977
978 m_editing = false;
979
980 SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
981 if (points.empty()) return;
982 if (points.begin()->frame != m_editingPoint.frame ||
983 points.begin()->value != m_editingPoint.value) return;
984
985 m_editingCommand = new SparseTimeValueModel::EditCommand
986 (m_model, tr("Erase Point"));
987
988 m_editingCommand->deletePoint(m_editingPoint);
989
990 m_editingCommand->finish();
991 m_editingCommand = 0;
992 m_editing = false;
993 }
994
995 void
943 TimeValueLayer::editStart(View *v, QMouseEvent *e) 996 TimeValueLayer::editStart(View *v, QMouseEvent *e)
944 { 997 {
945 // std::cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; 998 // std::cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
946 999
947 if (!m_model) return; 1000 if (!m_model) return;
1134 1187
1135 command->finish(); 1188 command->finish();
1136 } 1189 }
1137 1190
1138 void 1191 void
1139 TimeValueLayer::copy(Selection s, Clipboard &to) 1192 TimeValueLayer::copy(View *v, Selection s, Clipboard &to)
1140 { 1193 {
1141 if (!m_model) return; 1194 if (!m_model) return;
1142 1195
1143 SparseTimeValueModel::PointList points = 1196 SparseTimeValueModel::PointList points =
1144 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 1197 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
1145 1198
1146 for (SparseTimeValueModel::PointList::iterator i = points.begin(); 1199 for (SparseTimeValueModel::PointList::iterator i = points.begin();
1147 i != points.end(); ++i) { 1200 i != points.end(); ++i) {
1148 if (s.contains(i->frame)) { 1201 if (s.contains(i->frame)) {
1149 Clipboard::Point point(i->frame, i->value, i->label); 1202 Clipboard::Point point(i->frame, i->value, i->label);
1203 point.setReferenceFrame(alignToReference(v, i->frame));
1150 to.addPoint(point); 1204 to.addPoint(point);
1151 } 1205 }
1152 } 1206 }
1153 } 1207 }
1154 1208
1155 bool 1209 bool
1156 TimeValueLayer::paste(const Clipboard &from, int frameOffset, 1210 TimeValueLayer::paste(View *v, const Clipboard &from, int frameOffset,
1157 bool interactive) 1211 bool interactive)
1158 { 1212 {
1159 if (!m_model) return false; 1213 if (!m_model) return false;
1160 1214
1161 const Clipboard::PointList &points = from.getPoints(); 1215 const Clipboard::PointList &points = from.getPoints();
1216
1217 bool realign = false;
1218
1219 if (clipboardHasDifferentAlignment(v, from)) {
1220
1221 QMessageBox::StandardButton button =
1222 QMessageBox::question(v, tr("Re-align pasted items?"),
1223 tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"),
1224 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1225 QMessageBox::Yes);
1226
1227 if (button == QMessageBox::Cancel) {
1228 return false;
1229 }
1230
1231 if (button == QMessageBox::Yes) {
1232 realign = true;
1233 }
1234 }
1162 1235
1163 SparseTimeValueModel::EditCommand *command = 1236 SparseTimeValueModel::EditCommand *command =
1164 new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); 1237 new SparseTimeValueModel::EditCommand(m_model, tr("Paste"));
1165
1166 //!!! Replace all this with a use of Labeller
1167 1238
1168 enum ValueAvailability { 1239 enum ValueAvailability {
1169 UnknownAvailability, 1240 UnknownAvailability,
1170 NoValues, 1241 NoValues,
1171 SomeValues, 1242 SomeValues,
1172 AllValues 1243 AllValues
1173 }; 1244 };
1174 enum ValueGeneration { 1245
1175 GenerateNone, 1246 Labeller::ValueType generation = Labeller::ValueNone;
1176 GenerateFromCounter,
1177 GenerateFromFrameNumber,
1178 GenerateFromRealTime,
1179 GenerateFromRealTimeDifference,
1180 GenerateFromTempo,
1181 GenerateFromExistingNeighbour,
1182 GenerateFromLabels
1183 };
1184
1185 ValueGeneration generation = GenerateNone;
1186 1247
1187 bool haveUsableLabels = false; 1248 bool haveUsableLabels = false;
1188 bool haveExistingItems = !(m_model->isEmpty()); 1249 bool haveExistingItems = !(m_model->isEmpty());
1250 Labeller labeller;
1251 labeller.setSampleRate(m_model->getSampleRate());
1189 1252
1190 if (interactive) { 1253 if (interactive) {
1191 1254
1192 ValueAvailability availability = UnknownAvailability; 1255 ValueAvailability availability = UnknownAvailability;
1193 1256
1230 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?"); 1293 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?");
1231 } else { 1294 } else {
1232 text = tr("Some of the items you are pasting do not have values.\nWhat values do you want to use for these items?"); 1295 text = tr("Some of the items you are pasting do not have values.\nWhat values do you want to use for these items?");
1233 } 1296 }
1234 1297
1298 Labeller::TypeNameMap names = labeller.getTypeNames();
1299
1235 QStringList options; 1300 QStringList options;
1236 std::vector<int> genopts; 1301 std::vector<Labeller::ValueType> genopts;
1237 1302
1238 options << tr("Zero for all items"); 1303 for (Labeller::TypeNameMap::const_iterator i = names.begin();
1239 genopts.push_back(int(GenerateNone)); 1304 i != names.end(); ++i) {
1240 1305 if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
1241 options << tr("Whole numbers counting from 1"); 1306 else options << i->second;
1242 genopts.push_back(int(GenerateFromCounter)); 1307 genopts.push_back(i->first);
1243 1308 }
1244 options << tr("Item's audio sample frame number");
1245 genopts.push_back(int(GenerateFromFrameNumber));
1246
1247 options << tr("Item's time in seconds");
1248 genopts.push_back(int(GenerateFromRealTime));
1249
1250 options << tr("Duration from the item to the following item");
1251 genopts.push_back(int(GenerateFromRealTimeDifference));
1252
1253 options << tr("Tempo in bpm derived from the duration");
1254 genopts.push_back(int(GenerateFromTempo));
1255
1256 if (haveExistingItems) {
1257 options << tr("Value of the nearest existing item");
1258 genopts.push_back(int(GenerateFromExistingNeighbour));
1259 }
1260
1261 if (haveUsableLabels) {
1262 options << tr("Value extracted from the item's label (where possible)");
1263 genopts.push_back(int(GenerateFromLabels));
1264 }
1265
1266 1309
1267 static int prevSelection = 0; 1310 static int prevSelection = 0;
1268 1311
1269 bool ok = false; 1312 bool ok = false;
1270 QString selected = ListInputDialog::getItem 1313 QString selected = ListInputDialog::getItem
1271 (0, tr("Choose value calculation"), 1314 (0, tr("Choose value calculation"),
1272 text, options, prevSelection, &ok); 1315 text, options, prevSelection, &ok);
1273 1316
1274 if (!ok) return false; 1317 if (!ok) return false;
1275 int selection = 0; 1318 int selection = 0;
1276 generation = GenerateNone; 1319 generation = Labeller::ValueNone;
1277 1320
1278 for (QStringList::const_iterator i = options.begin(); 1321 for (QStringList::const_iterator i = options.begin();
1279 i != options.end(); ++i) { 1322 i != options.end(); ++i) {
1280 if (selected == *i) { 1323 if (selected == *i) {
1281 generation = ValueGeneration(genopts[selection]); 1324 generation = genopts[selection];
1282 break; 1325 break;
1283 } 1326 }
1284 ++selection; 1327 ++selection;
1285 } 1328 }
1329
1330 labeller.setType(generation);
1331
1332 if (generation == Labeller::ValueFromCyclicalCounter ||
1333 generation == Labeller::ValueFromTwoLevelCounter) {
1334 int cycleSize = QInputDialog::getInteger
1335 (0, tr("Select cycle size"),
1336 tr("Cycle size:"), 4, 2, 16, 1);
1337 labeller.setCounterCycleSize(cycleSize);
1338 }
1286 1339
1287 prevSelection = selection; 1340 prevSelection = selection;
1288 } 1341 }
1289 } 1342 }
1290 1343
1291 int counter = 1; 1344 SparseTimeValueModel::Point prevPoint(0);
1292 float prevBpm = 120.f;
1293 1345
1294 for (Clipboard::PointList::const_iterator i = points.begin(); 1346 for (Clipboard::PointList::const_iterator i = points.begin();
1295 i != points.end(); ++i) { 1347 i != points.end(); ++i) {
1296 1348
1297 if (!i->haveFrame()) continue; 1349 if (!i->haveFrame()) continue;
1350
1298 size_t frame = 0; 1351 size_t frame = 0;
1299 if (frameOffset > 0 || -frameOffset < i->getFrame()) { 1352
1300 frame = i->getFrame() + frameOffset; 1353 if (!realign) {
1301 } 1354
1355 frame = i->getFrame();
1356
1357 } else {
1358
1359 if (i->haveReferenceFrame()) {
1360 frame = i->getReferenceFrame();
1361 frame = alignFromReference(v, frame);
1362 } else {
1363 frame = i->getFrame();
1364 }
1365 }
1366
1302 SparseTimeValueModel::Point newPoint(frame); 1367 SparseTimeValueModel::Point newPoint(frame);
1303 1368
1304 if (i->haveLabel()) { 1369 if (i->haveLabel()) {
1305 newPoint.label = i->getLabel(); 1370 newPoint.label = i->getLabel();
1306 } else if (i->haveValue()) { 1371 } else if (i->haveValue()) {
1307 newPoint.label = QString("%1").arg(i->getValue()); 1372 newPoint.label = QString("%1").arg(i->getValue());
1308 } 1373 }
1309 1374
1375 bool usePrev = false;
1376 SparseTimeValueModel::Point formerPrevPoint = prevPoint;
1377
1310 if (i->haveValue()) { 1378 if (i->haveValue()) {
1311 newPoint.value = i->getValue(); 1379 newPoint.value = i->getValue();
1312 } else { 1380 } else {
1313 1381 // std::cerr << "Setting value on point at " << newPoint.frame << " from labeller";
1314 switch (generation) { 1382 // if (i == points.begin()) {
1315 1383 // std::cerr << ", no prev point" << std::endl;
1316 case GenerateNone: 1384 // } else {
1317 newPoint.value = 0; 1385 // std::cerr << ", prev point is at " << prevPoint.frame << std::endl;
1318 break; 1386 // }
1319 1387 labeller.setValue<SparseTimeValueModel::Point>
1320 case GenerateFromCounter: 1388 (newPoint, (i == points.begin()) ? 0 : &prevPoint);
1321 newPoint.value = counter; 1389 // std::cerr << "New point value = " << newPoint.value << std::endl;
1322 break; 1390 if (labeller.actingOnPrevPoint() && i != points.begin()) {
1323 1391 usePrev = true;
1324 case GenerateFromFrameNumber: 1392 }
1325 newPoint.value = frame; 1393 }
1326 break; 1394
1327 1395 if (usePrev) {
1328 case GenerateFromRealTime: 1396 command->deletePoint(formerPrevPoint);
1329 newPoint.value = float(frame) / float(m_model->getSampleRate()); 1397 command->addPoint(prevPoint);
1330 break; 1398 }
1331 1399
1332 case GenerateFromRealTimeDifference: 1400 prevPoint = newPoint;
1333 case GenerateFromTempo:
1334 {
1335 size_t nextFrame = frame;
1336 Clipboard::PointList::const_iterator j = i;
1337 for (; j != points.end(); ++j) {
1338 if (!j->haveFrame()) continue;
1339 if (j != i) break;
1340 }
1341 if (j != points.end()) {
1342 nextFrame = j->getFrame();
1343 }
1344 if (generation == GenerateFromRealTimeDifference) {
1345 newPoint.value = float(nextFrame - frame) /
1346 float(m_model->getSampleRate());
1347 } else {
1348 float bpm = prevBpm;
1349 if (nextFrame > frame) {
1350 bpm = (60.f * m_model->getSampleRate()) /
1351 (nextFrame - frame);
1352 }
1353 newPoint.value = bpm;
1354 prevBpm = bpm;
1355 }
1356 break;
1357 }
1358
1359 case GenerateFromExistingNeighbour:
1360 {
1361 SparseTimeValueModel::PointList points =
1362 m_model->getPoints(frame);
1363 if (points.empty()) points = m_model->getPreviousPoints(frame);
1364 if (points.empty()) points = m_model->getNextPoints(frame);
1365 if (points.empty()) {
1366 newPoint.value = 0.f;
1367 } else {
1368 newPoint.value = points.begin()->value;
1369 }
1370 }
1371
1372 case GenerateFromLabels:
1373 if (i->haveLabel()) {
1374 // more forgiving than QString::toFloat()
1375 newPoint.value = atof(i->getLabel().toLocal8Bit());
1376 } else {
1377 newPoint.value = 0.f;
1378 }
1379 }
1380 }
1381
1382 command->addPoint(newPoint); 1401 command->addPoint(newPoint);
1383
1384 ++counter;
1385 } 1402 }
1386 1403
1387 command->finish(); 1404 command->finish();
1388 return true; 1405 return true;
1389 } 1406 }