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