Mercurial > hg > svgui
comparison layer/TimeValueLayer.cpp @ 125:999ae0f7d10c
* Change preferences dialog to ok/apply/cancel model
* Make preferences persist in a config file
* Change instance() to getInstance() for all singleton types
* Make pasting to time-value layer with no values in clipboard ask you how to
  generate the values
* Fix bad behaviour caused by importing "data"-type (i.e. 3d dense) model from
  annotation file without a fixed window size available
| author | Chris Cannam | 
|---|---|
| date | Thu, 27 Jul 2006 16:06:32 +0000 | 
| parents | 0f36cdf407a6 | 
| children | 33929e0c3c6b | 
   comparison
  equal
  deleted
  inserted
  replaced
| 124:bd6e85b3d88b | 125:999ae0f7d10c | 
|---|---|
| 21 #include "base/View.h" | 21 #include "base/View.h" | 
| 22 | 22 | 
| 23 #include "model/SparseTimeValueModel.h" | 23 #include "model/SparseTimeValueModel.h" | 
| 24 | 24 | 
| 25 #include "widgets/ItemEditDialog.h" | 25 #include "widgets/ItemEditDialog.h" | 
| 26 #include "widgets/ListInputDialog.h" | |
| 26 | 27 | 
| 27 #include "SpectrogramLayer.h" // for optional frequency alignment | 28 #include "SpectrogramLayer.h" // for optional frequency alignment | 
| 28 | 29 | 
| 29 #include <QPainter> | 30 #include <QPainter> | 
| 30 #include <QPainterPath> | 31 #include <QPainterPath> | 
| 31 #include <QMouseEvent> | 32 #include <QMouseEvent> | 
| 33 #include <QRegExp> | |
| 32 | 34 | 
| 33 #include <iostream> | 35 #include <iostream> | 
| 34 #include <cmath> | 36 #include <cmath> | 
| 35 | 37 | 
| 36 TimeValueLayer::TimeValueLayer() : | 38 TimeValueLayer::TimeValueLayer() : | 
| 1129 to.addPoint(point); | 1131 to.addPoint(point); | 
| 1130 } | 1132 } | 
| 1131 } | 1133 } | 
| 1132 } | 1134 } | 
| 1133 | 1135 | 
| 1134 void | 1136 bool | 
| 1135 TimeValueLayer::paste(const Clipboard &from, int frameOffset) | 1137 TimeValueLayer::paste(const Clipboard &from, int frameOffset, | 
| 1136 { | 1138 bool interactive) | 
| 1137 if (!m_model) return; | 1139 { | 
| 1140 if (!m_model) return false; | |
| 1138 | 1141 | 
| 1139 const Clipboard::PointList &points = from.getPoints(); | 1142 const Clipboard::PointList &points = from.getPoints(); | 
| 1140 | 1143 | 
| 1141 SparseTimeValueModel::EditCommand *command = | 1144 SparseTimeValueModel::EditCommand *command = | 
| 1142 new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); | 1145 new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); | 
| 1146 | |
| 1147 enum ValueAvailability { | |
| 1148 UnknownAvailability, | |
| 1149 NoValues, | |
| 1150 SomeValues, | |
| 1151 AllValues | |
| 1152 }; | |
| 1153 enum ValueGeneration { | |
| 1154 GenerateNone, | |
| 1155 GenerateFromCounter, | |
| 1156 GenerateFromFrameNumber, | |
| 1157 GenerateFromRealTime, | |
| 1158 GenerateFromRealTimeDifference, | |
| 1159 GenerateFromTempo, | |
| 1160 GenerateFromExistingNeighbour, | |
| 1161 GenerateFromLabels | |
| 1162 }; | |
| 1163 | |
| 1164 ValueGeneration generation = GenerateNone; | |
| 1165 | |
| 1166 bool haveUsableLabels = false; | |
| 1167 bool haveExistingItems = !(m_model->isEmpty()); | |
| 1168 | |
| 1169 if (interactive) { | |
| 1170 | |
| 1171 ValueAvailability availability = UnknownAvailability; | |
| 1172 | |
| 1173 for (Clipboard::PointList::const_iterator i = points.begin(); | |
| 1174 i != points.end(); ++i) { | |
| 1175 | |
| 1176 if (!i->haveFrame()) continue; | |
| 1177 | |
| 1178 if (availability == UnknownAvailability) { | |
| 1179 if (i->haveValue()) availability = AllValues; | |
| 1180 else availability = NoValues; | |
| 1181 continue; | |
| 1182 } | |
| 1183 | |
| 1184 if (i->haveValue()) { | |
| 1185 if (availability == NoValues) { | |
| 1186 availability = SomeValues; | |
| 1187 } | |
| 1188 } else { | |
| 1189 if (availability == AllValues) { | |
| 1190 availability = SomeValues; | |
| 1191 } | |
| 1192 } | |
| 1193 | |
| 1194 if (!haveUsableLabels) { | |
| 1195 if (i->haveLabel()) { | |
| 1196 if (i->getLabel().contains(QRegExp("[0-9]"))) { | |
| 1197 haveUsableLabels = true; | |
| 1198 } | |
| 1199 } | |
| 1200 } | |
| 1201 | |
| 1202 if (availability == SomeValues && haveUsableLabels) break; | |
| 1203 } | |
| 1204 | |
| 1205 if (availability == NoValues || availability == SomeValues) { | |
| 1206 | |
| 1207 QString text; | |
| 1208 if (availability == NoValues) { | |
| 1209 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?"); | |
| 1210 } else { | |
| 1211 text = tr("Some of the items you are pasting do not have values.\nWhat values do you want to use for these items?"); | |
| 1212 } | |
| 1213 | |
| 1214 QStringList options; | |
| 1215 std::vector<int> genopts; | |
| 1216 | |
| 1217 options << tr("Zero for all items"); | |
| 1218 genopts.push_back(int(GenerateNone)); | |
| 1219 | |
| 1220 options << tr("Whole numbers counting from 1"); | |
| 1221 genopts.push_back(int(GenerateFromCounter)); | |
| 1222 | |
| 1223 options << tr("Item's audio sample frame number"); | |
| 1224 genopts.push_back(int(GenerateFromFrameNumber)); | |
| 1225 | |
| 1226 options << tr("Item's time in seconds"); | |
| 1227 genopts.push_back(int(GenerateFromRealTime)); | |
| 1228 | |
| 1229 options << tr("Duration from the item to the following item"); | |
| 1230 genopts.push_back(int(GenerateFromRealTimeDifference)); | |
| 1231 | |
| 1232 options << tr("Tempo in bpm derived from the duration"); | |
| 1233 genopts.push_back(int(GenerateFromTempo)); | |
| 1234 | |
| 1235 if (haveExistingItems) { | |
| 1236 options << tr("Value of the nearest existing item"); | |
| 1237 genopts.push_back(int(GenerateFromExistingNeighbour)); | |
| 1238 } | |
| 1239 | |
| 1240 if (haveUsableLabels) { | |
| 1241 options << tr("Value extracted from the item's label (where possible)"); | |
| 1242 genopts.push_back(int(GenerateFromLabels)); | |
| 1243 } | |
| 1244 | |
| 1245 | |
| 1246 static int prevSelection = 0; | |
| 1247 | |
| 1248 bool ok = false; | |
| 1249 QString selected = ListInputDialog::getItem | |
| 1250 (0, tr("Choose value calculation"), | |
| 1251 text, options, prevSelection, &ok); | |
| 1252 | |
| 1253 if (!ok) return false; | |
| 1254 int selection = 0; | |
| 1255 generation = GenerateNone; | |
| 1256 | |
| 1257 for (QStringList::const_iterator i = options.begin(); | |
| 1258 i != options.end(); ++i) { | |
| 1259 if (selected == *i) { | |
| 1260 generation = ValueGeneration(genopts[selection]); | |
| 1261 break; | |
| 1262 } | |
| 1263 ++selection; | |
| 1264 } | |
| 1265 | |
| 1266 prevSelection = selection; | |
| 1267 } | |
| 1268 } | |
| 1269 | |
| 1270 int counter = 1; | |
| 1271 float prevBpm = 120.f; | |
| 1143 | 1272 | 
| 1144 for (Clipboard::PointList::const_iterator i = points.begin(); | 1273 for (Clipboard::PointList::const_iterator i = points.begin(); | 
| 1145 i != points.end(); ++i) { | 1274 i != points.end(); ++i) { | 
| 1146 | 1275 | 
| 1147 if (!i->haveFrame()) continue; | 1276 if (!i->haveFrame()) continue; | 
| 1149 if (frameOffset > 0 || -frameOffset < i->getFrame()) { | 1278 if (frameOffset > 0 || -frameOffset < i->getFrame()) { | 
| 1150 frame = i->getFrame() + frameOffset; | 1279 frame = i->getFrame() + frameOffset; | 
| 1151 } | 1280 } | 
| 1152 SparseTimeValueModel::Point newPoint(frame); | 1281 SparseTimeValueModel::Point newPoint(frame); | 
| 1153 | 1282 | 
| 1154 if (i->haveLabel()) newPoint.label = i->getLabel(); | 1283 if (i->haveLabel()) { | 
| 1155 if (i->haveValue()) newPoint.value = i->getValue(); | 1284 newPoint.label = i->getLabel(); | 
| 1156 else newPoint.value = (m_model->getValueMinimum() + | 1285 } else if (i->haveValue()) { | 
| 1157 m_model->getValueMaximum()) / 2; | 1286 newPoint.label = QString("%1").arg(i->getValue()); | 
| 1287 } | |
| 1288 | |
| 1289 if (i->haveValue()) { | |
| 1290 newPoint.value = i->getValue(); | |
| 1291 } else { | |
| 1292 | |
| 1293 switch (generation) { | |
| 1294 | |
| 1295 case GenerateNone: | |
| 1296 newPoint.value = 0; | |
| 1297 break; | |
| 1298 | |
| 1299 case GenerateFromCounter: | |
| 1300 newPoint.value = counter; | |
| 1301 break; | |
| 1302 | |
| 1303 case GenerateFromFrameNumber: | |
| 1304 newPoint.value = frame; | |
| 1305 break; | |
| 1306 | |
| 1307 case GenerateFromRealTime: | |
| 1308 newPoint.value = float(frame) / float(m_model->getSampleRate()); | |
| 1309 break; | |
| 1310 | |
| 1311 case GenerateFromRealTimeDifference: | |
| 1312 case GenerateFromTempo: | |
| 1313 { | |
| 1314 size_t nextFrame = frame; | |
| 1315 Clipboard::PointList::const_iterator j = i; | |
| 1316 for (; j != points.end(); ++j) { | |
| 1317 if (!j->haveFrame()) continue; | |
| 1318 if (j != i) break; | |
| 1319 } | |
| 1320 if (j != points.end()) { | |
| 1321 nextFrame = j->getFrame(); | |
| 1322 } | |
| 1323 if (generation == GenerateFromRealTimeDifference) { | |
| 1324 newPoint.value = float(nextFrame - frame) / | |
| 1325 float(m_model->getSampleRate()); | |
| 1326 } else { | |
| 1327 float bpm = prevBpm; | |
| 1328 if (nextFrame > frame) { | |
| 1329 bpm = (60.f * m_model->getSampleRate()) / | |
| 1330 (nextFrame - frame); | |
| 1331 } | |
| 1332 newPoint.value = bpm; | |
| 1333 prevBpm = bpm; | |
| 1334 } | |
| 1335 break; | |
| 1336 } | |
| 1337 | |
| 1338 case GenerateFromExistingNeighbour: | |
| 1339 { | |
| 1340 SparseTimeValueModel::PointList points = | |
| 1341 m_model->getPoints(frame); | |
| 1342 if (points.empty()) points = m_model->getPreviousPoints(frame); | |
| 1343 if (points.empty()) points = m_model->getNextPoints(frame); | |
| 1344 if (points.empty()) { | |
| 1345 newPoint.value = 0.f; | |
| 1346 } else { | |
| 1347 newPoint.value = points.begin()->value; | |
| 1348 } | |
| 1349 } | |
| 1350 | |
| 1351 case GenerateFromLabels: | |
| 1352 if (i->haveLabel()) { | |
| 1353 // more forgiving than QString::toFloat() | |
| 1354 newPoint.value = atof(i->getLabel().toLocal8Bit()); | |
| 1355 } else { | |
| 1356 newPoint.value = 0.f; | |
| 1357 } | |
| 1358 } | |
| 1359 } | |
| 1158 | 1360 | 
| 1159 command->addPoint(newPoint); | 1361 command->addPoint(newPoint); | 
| 1362 | |
| 1363 ++counter; | |
| 1160 } | 1364 } | 
| 1161 | 1365 | 
| 1162 command->finish(); | 1366 command->finish(); | 
| 1367 return true; | |
| 1163 } | 1368 } | 
| 1164 | 1369 | 
| 1165 QString | 1370 QString | 
| 1166 TimeValueLayer::toXmlString(QString indent, QString extraAttributes) const | 1371 TimeValueLayer::toXmlString(QString indent, QString extraAttributes) const | 
| 1167 { | 1372 { | 
