Mercurial > hg > svgui
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 } |