comparison layer/TimeValueLayer.cpp @ 373:0895517bb2d1 1.2-stable

* merge from trunk (1.2 ended up being tracked from trunk, but we may want this branch for fixes later)
author Chris Cannam
date Wed, 27 Feb 2008 10:32:45 +0000
parents 2f83b6e3b8ca
children
comparison
equal deleted inserted replaced
337:813170c57b13 373:0895517bb2d1
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());
1179 1187
1180 command->finish(); 1188 command->finish();
1181 } 1189 }
1182 1190
1183 void 1191 void
1184 TimeValueLayer::copy(Selection s, Clipboard &to) 1192 TimeValueLayer::copy(View *v, Selection s, Clipboard &to)
1185 { 1193 {
1186 if (!m_model) return; 1194 if (!m_model) return;
1187 1195
1188 SparseTimeValueModel::PointList points = 1196 SparseTimeValueModel::PointList points =
1189 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 1197 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
1190 1198
1191 for (SparseTimeValueModel::PointList::iterator i = points.begin(); 1199 for (SparseTimeValueModel::PointList::iterator i = points.begin();
1192 i != points.end(); ++i) { 1200 i != points.end(); ++i) {
1193 if (s.contains(i->frame)) { 1201 if (s.contains(i->frame)) {
1194 Clipboard::Point point(i->frame, i->value, i->label); 1202 Clipboard::Point point(i->frame, i->value, i->label);
1195 point.setReferenceFrame(m_model->alignToReference(i->frame)); 1203 point.setReferenceFrame(alignToReference(v, i->frame));
1196 to.addPoint(point); 1204 to.addPoint(point);
1197 } 1205 }
1198 } 1206 }
1199 } 1207 }
1200 1208
1201 bool 1209 bool
1202 TimeValueLayer::paste(const Clipboard &from, int frameOffset, 1210 TimeValueLayer::paste(View *v, const Clipboard &from, int frameOffset,
1203 bool interactive) 1211 bool interactive)
1204 { 1212 {
1205 if (!m_model) return false; 1213 if (!m_model) return false;
1206 1214
1207 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 }
1208 1235
1209 SparseTimeValueModel::EditCommand *command = 1236 SparseTimeValueModel::EditCommand *command =
1210 new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); 1237 new SparseTimeValueModel::EditCommand(m_model, tr("Paste"));
1211
1212 //!!! Replace all this with a use of Labeller
1213 1238
1214 enum ValueAvailability { 1239 enum ValueAvailability {
1215 UnknownAvailability, 1240 UnknownAvailability,
1216 NoValues, 1241 NoValues,
1217 SomeValues, 1242 SomeValues,
1218 AllValues 1243 AllValues
1219 }; 1244 };
1220 enum ValueGeneration { 1245
1221 GenerateNone, 1246 Labeller::ValueType generation = Labeller::ValueNone;
1222 GenerateFromCounter,
1223 GenerateFromFrameNumber,
1224 GenerateFromRealTime,
1225 GenerateFromRealTimeDifference,
1226 GenerateFromTempo,
1227 GenerateFromExistingNeighbour,
1228 GenerateFromLabels
1229 };
1230
1231 ValueGeneration generation = GenerateNone;
1232 1247
1233 bool haveUsableLabels = false; 1248 bool haveUsableLabels = false;
1234 bool haveExistingItems = !(m_model->isEmpty()); 1249 bool haveExistingItems = !(m_model->isEmpty());
1250 Labeller labeller;
1251 labeller.setSampleRate(m_model->getSampleRate());
1235 1252
1236 if (interactive) { 1253 if (interactive) {
1237 1254
1238 ValueAvailability availability = UnknownAvailability; 1255 ValueAvailability availability = UnknownAvailability;
1239 1256
1276 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?");
1277 } else { 1294 } else {
1278 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?");
1279 } 1296 }
1280 1297
1298 Labeller::TypeNameMap names = labeller.getTypeNames();
1299
1281 QStringList options; 1300 QStringList options;
1282 std::vector<int> genopts; 1301 std::vector<Labeller::ValueType> genopts;
1283 1302
1284 options << tr("Zero for all items"); 1303 for (Labeller::TypeNameMap::const_iterator i = names.begin();
1285 genopts.push_back(int(GenerateNone)); 1304 i != names.end(); ++i) {
1286 1305 if (i->first == Labeller::ValueNone) options << tr("Zero for all items");
1287 options << tr("Whole numbers counting from 1"); 1306 else options << i->second;
1288 genopts.push_back(int(GenerateFromCounter)); 1307 genopts.push_back(i->first);
1289 1308 }
1290 options << tr("Item's audio sample frame number");
1291 genopts.push_back(int(GenerateFromFrameNumber));
1292
1293 options << tr("Item's time in seconds");
1294 genopts.push_back(int(GenerateFromRealTime));
1295
1296 options << tr("Duration from the item to the following item");
1297 genopts.push_back(int(GenerateFromRealTimeDifference));
1298
1299 options << tr("Tempo in bpm derived from the duration");
1300 genopts.push_back(int(GenerateFromTempo));
1301
1302 if (haveExistingItems) {
1303 options << tr("Value of the nearest existing item");
1304 genopts.push_back(int(GenerateFromExistingNeighbour));
1305 }
1306
1307 if (haveUsableLabels) {
1308 options << tr("Value extracted from the item's label (where possible)");
1309 genopts.push_back(int(GenerateFromLabels));
1310 }
1311
1312 1309
1313 static int prevSelection = 0; 1310 static int prevSelection = 0;
1314 1311
1315 bool ok = false; 1312 bool ok = false;
1316 QString selected = ListInputDialog::getItem 1313 QString selected = ListInputDialog::getItem
1317 (0, tr("Choose value calculation"), 1314 (0, tr("Choose value calculation"),
1318 text, options, prevSelection, &ok); 1315 text, options, prevSelection, &ok);
1319 1316
1320 if (!ok) return false; 1317 if (!ok) return false;
1321 int selection = 0; 1318 int selection = 0;
1322 generation = GenerateNone; 1319 generation = Labeller::ValueNone;
1323 1320
1324 for (QStringList::const_iterator i = options.begin(); 1321 for (QStringList::const_iterator i = options.begin();
1325 i != options.end(); ++i) { 1322 i != options.end(); ++i) {
1326 if (selected == *i) { 1323 if (selected == *i) {
1327 generation = ValueGeneration(genopts[selection]); 1324 generation = genopts[selection];
1328 break; 1325 break;
1329 } 1326 }
1330 ++selection; 1327 ++selection;
1331 } 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 }
1332 1339
1333 prevSelection = selection; 1340 prevSelection = selection;
1334 } 1341 }
1335 } 1342 }
1336 1343
1337 int counter = 1; 1344 SparseTimeValueModel::Point prevPoint(0);
1338 float prevBpm = 120.f;
1339 1345
1340 for (Clipboard::PointList::const_iterator i = points.begin(); 1346 for (Clipboard::PointList::const_iterator i = points.begin();
1341 i != points.end(); ++i) { 1347 i != points.end(); ++i) {
1342 1348
1343 if (!i->haveFrame()) continue; 1349 if (!i->haveFrame()) continue;
1350
1344 size_t frame = 0; 1351 size_t frame = 0;
1345 if (frameOffset > 0 || -frameOffset < i->getFrame()) { 1352
1346 frame = i->getFrame() + frameOffset; 1353 if (!realign) {
1347 } 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
1348 SparseTimeValueModel::Point newPoint(frame); 1367 SparseTimeValueModel::Point newPoint(frame);
1349 1368
1350 if (i->haveLabel()) { 1369 if (i->haveLabel()) {
1351 newPoint.label = i->getLabel(); 1370 newPoint.label = i->getLabel();
1352 } else if (i->haveValue()) { 1371 } else if (i->haveValue()) {
1353 newPoint.label = QString("%1").arg(i->getValue()); 1372 newPoint.label = QString("%1").arg(i->getValue());
1354 } 1373 }
1355 1374
1375 bool usePrev = false;
1376 SparseTimeValueModel::Point formerPrevPoint = prevPoint;
1377
1356 if (i->haveValue()) { 1378 if (i->haveValue()) {
1357 newPoint.value = i->getValue(); 1379 newPoint.value = i->getValue();
1358 } else { 1380 } else {
1359 1381 // std::cerr << "Setting value on point at " << newPoint.frame << " from labeller";
1360 switch (generation) { 1382 // if (i == points.begin()) {
1361 1383 // std::cerr << ", no prev point" << std::endl;
1362 case GenerateNone: 1384 // } else {
1363 newPoint.value = 0; 1385 // std::cerr << ", prev point is at " << prevPoint.frame << std::endl;
1364 break; 1386 // }
1365 1387 labeller.setValue<SparseTimeValueModel::Point>
1366 case GenerateFromCounter: 1388 (newPoint, (i == points.begin()) ? 0 : &prevPoint);
1367 newPoint.value = counter; 1389 // std::cerr << "New point value = " << newPoint.value << std::endl;
1368 break; 1390 if (labeller.actingOnPrevPoint() && i != points.begin()) {
1369 1391 usePrev = true;
1370 case GenerateFromFrameNumber: 1392 }
1371 newPoint.value = frame; 1393 }
1372 break; 1394
1373 1395 if (usePrev) {
1374 case GenerateFromRealTime: 1396 command->deletePoint(formerPrevPoint);
1375 newPoint.value = float(frame) / float(m_model->getSampleRate()); 1397 command->addPoint(prevPoint);
1376 break; 1398 }
1377 1399
1378 case GenerateFromRealTimeDifference: 1400 prevPoint = newPoint;
1379 case GenerateFromTempo:
1380 {
1381 size_t nextFrame = frame;
1382 Clipboard::PointList::const_iterator j = i;
1383 for (; j != points.end(); ++j) {
1384 if (!j->haveFrame()) continue;
1385 if (j != i) break;
1386 }
1387 if (j != points.end()) {
1388 nextFrame = j->getFrame();
1389 }
1390 if (generation == GenerateFromRealTimeDifference) {
1391 newPoint.value = float(nextFrame - frame) /
1392 float(m_model->getSampleRate());
1393 } else {
1394 float bpm = prevBpm;
1395 if (nextFrame > frame) {
1396 bpm = (60.f * m_model->getSampleRate()) /
1397 (nextFrame - frame);
1398 }
1399 newPoint.value = bpm;
1400 prevBpm = bpm;
1401 }
1402 break;
1403 }
1404
1405 case GenerateFromExistingNeighbour:
1406 {
1407 SparseTimeValueModel::PointList points =
1408 m_model->getPoints(frame);
1409 if (points.empty()) points = m_model->getPreviousPoints(frame);
1410 if (points.empty()) points = m_model->getNextPoints(frame);
1411 if (points.empty()) {
1412 newPoint.value = 0.f;
1413 } else {
1414 newPoint.value = points.begin()->value;
1415 }
1416 }
1417
1418 case GenerateFromLabels:
1419 if (i->haveLabel()) {
1420 // more forgiving than QString::toFloat()
1421 newPoint.value = atof(i->getLabel().toLocal8Bit());
1422 } else {
1423 newPoint.value = 0.f;
1424 }
1425 }
1426 }
1427
1428 command->addPoint(newPoint); 1401 command->addPoint(newPoint);
1429
1430 ++counter;
1431 } 1402 }
1432 1403
1433 command->finish(); 1404 command->finish();
1434 return true; 1405 return true;
1435 } 1406 }