annotate data/fileio/CSVFileReader.cpp @ 294:2c1e57ad86e7

* Show colour swatch next to layer name in pane (if available) * Fix for incorrect layer name prefix handling (was making some layers appear to have the same model name in cases where the model names differed by the final character only)
author Chris Cannam
date Wed, 05 Sep 2007 15:17:15 +0000
parents 7336fe3a7caa
children 14e0f60435b8
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@148 7 This file copyright 2006 Chris Cannam.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #include "CSVFileReader.h"
Chris@148 17
Chris@150 18 #include "model/Model.h"
Chris@148 19 #include "base/RealTime.h"
Chris@148 20 #include "model/SparseOneDimensionalModel.h"
Chris@148 21 #include "model/SparseTimeValueModel.h"
Chris@152 22 #include "model/EditableDenseThreeDimensionalModel.h"
Chris@148 23
Chris@148 24 #include <QFile>
Chris@148 25 #include <QString>
Chris@148 26 #include <QRegExp>
Chris@148 27 #include <QStringList>
Chris@148 28 #include <QTextStream>
Chris@148 29 #include <QFrame>
Chris@148 30 #include <QGridLayout>
Chris@148 31 #include <QPushButton>
Chris@148 32 #include <QHBoxLayout>
Chris@148 33 #include <QVBoxLayout>
Chris@148 34 #include <QTableWidget>
Chris@148 35 #include <QComboBox>
Chris@148 36 #include <QLabel>
Chris@148 37
Chris@148 38 #include <iostream>
Chris@148 39
Chris@148 40 CSVFileReader::CSVFileReader(QString path, size_t mainModelSampleRate) :
Chris@148 41 m_file(0),
Chris@148 42 m_mainModelSampleRate(mainModelSampleRate)
Chris@148 43 {
Chris@148 44 m_file = new QFile(path);
Chris@148 45 bool good = false;
Chris@148 46
Chris@148 47 if (!m_file->exists()) {
Chris@148 48 m_error = QFile::tr("File \"%1\" does not exist").arg(path);
Chris@148 49 } else if (!m_file->open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@148 50 m_error = QFile::tr("Failed to open file \"%1\"").arg(path);
Chris@148 51 } else {
Chris@148 52 good = true;
Chris@148 53 }
Chris@148 54
Chris@148 55 if (!good) {
Chris@148 56 delete m_file;
Chris@148 57 m_file = 0;
Chris@148 58 }
Chris@148 59 }
Chris@148 60
Chris@148 61 CSVFileReader::~CSVFileReader()
Chris@148 62 {
Chris@148 63 std::cerr << "CSVFileReader::~CSVFileReader: file is " << m_file << std::endl;
Chris@148 64
Chris@148 65 if (m_file) {
Chris@148 66 std::cerr << "CSVFileReader::CSVFileReader: Closing file" << std::endl;
Chris@148 67 m_file->close();
Chris@148 68 }
Chris@148 69 delete m_file;
Chris@148 70 }
Chris@148 71
Chris@148 72 bool
Chris@148 73 CSVFileReader::isOK() const
Chris@148 74 {
Chris@148 75 return (m_file != 0);
Chris@148 76 }
Chris@148 77
Chris@148 78 QString
Chris@148 79 CSVFileReader::getError() const
Chris@148 80 {
Chris@148 81 return m_error;
Chris@148 82 }
Chris@148 83
Chris@148 84 Model *
Chris@148 85 CSVFileReader::load() const
Chris@148 86 {
Chris@148 87 if (!m_file) return 0;
Chris@148 88
Chris@148 89 CSVFormatDialog *dialog = new CSVFormatDialog
Chris@148 90 (0, m_file, m_mainModelSampleRate);
Chris@148 91
Chris@148 92 if (dialog->exec() == QDialog::Rejected) {
Chris@148 93 delete dialog;
Chris@148 94 return 0;
Chris@148 95 }
Chris@148 96
Chris@148 97 CSVFormatDialog::ModelType modelType = dialog->getModelType();
Chris@148 98 CSVFormatDialog::TimingType timingType = dialog->getTimingType();
Chris@148 99 CSVFormatDialog::TimeUnits timeUnits = dialog->getTimeUnits();
Chris@148 100 QString separator = dialog->getSeparator();
Chris@148 101 size_t sampleRate = dialog->getSampleRate();
Chris@148 102 size_t windowSize = dialog->getWindowSize();
Chris@148 103
Chris@148 104 delete dialog;
Chris@148 105
Chris@148 106 if (timingType == CSVFormatDialog::ExplicitTiming) {
Chris@148 107 windowSize = 1;
Chris@148 108 if (timeUnits == CSVFormatDialog::TimeSeconds) {
Chris@148 109 sampleRate = m_mainModelSampleRate;
Chris@148 110 }
Chris@148 111 }
Chris@148 112
Chris@148 113 SparseOneDimensionalModel *model1 = 0;
Chris@148 114 SparseTimeValueModel *model2 = 0;
Chris@152 115 EditableDenseThreeDimensionalModel *model3 = 0;
Chris@148 116 Model *model = 0;
Chris@148 117
Chris@148 118 QTextStream in(m_file);
Chris@148 119 in.seek(0);
Chris@148 120
Chris@148 121 unsigned int warnings = 0, warnLimit = 10;
Chris@148 122 unsigned int lineno = 0;
Chris@148 123
Chris@148 124 float min = 0.0, max = 0.0;
Chris@148 125
Chris@148 126 size_t frameNo = 0;
Chris@148 127
Chris@148 128 while (!in.atEnd()) {
Chris@148 129
Chris@283 130 // QTextStream's readLine doesn't cope with old-style Mac
Chris@283 131 // CR-only line endings. Why did they bother making the class
Chris@283 132 // cope with more than one sort of line ending, if it still
Chris@283 133 // can't be configured to cope with all the common sorts?
Chris@148 134
Chris@283 135 // For the time being we'll deal with this case (which is
Chris@283 136 // relatively uncommon for us, but still necessary to handle)
Chris@283 137 // by reading the entire file using a single readLine, and
Chris@283 138 // splitting it. For CR and CR/LF line endings this will just
Chris@283 139 // read a line at a time, and that's obviously OK.
Chris@148 140
Chris@283 141 QString chunk = in.readLine();
Chris@283 142 QStringList lines = chunk.split('\r', QString::SkipEmptyParts);
Chris@283 143
Chris@283 144 for (size_t li = 0; li < lines.size(); ++li) {
Chris@148 145
Chris@283 146 QString line = lines[li];
Chris@148 147
Chris@283 148 if (line.startsWith("#")) continue;
Chris@283 149
Chris@283 150 QStringList list = line.split(separator, QString::KeepEmptyParts);
Chris@283 151
Chris@283 152 if (!model) {
Chris@283 153
Chris@283 154 switch (modelType) {
Chris@283 155
Chris@283 156 case CSVFormatDialog::OneDimensionalModel:
Chris@283 157 model1 = new SparseOneDimensionalModel(sampleRate, windowSize);
Chris@283 158 model = model1;
Chris@283 159 break;
Chris@148 160
Chris@283 161 case CSVFormatDialog::TwoDimensionalModel:
Chris@283 162 model2 = new SparseTimeValueModel(sampleRate, windowSize, false);
Chris@283 163 model = model2;
Chris@283 164 break;
Chris@148 165
Chris@283 166 case CSVFormatDialog::ThreeDimensionalModel:
Chris@283 167 model3 = new EditableDenseThreeDimensionalModel(sampleRate,
Chris@283 168 windowSize,
Chris@283 169 list.size());
Chris@283 170 model = model3;
Chris@283 171 break;
Chris@283 172 }
Chris@283 173 }
Chris@148 174
Chris@283 175 QStringList tidyList;
Chris@283 176 QRegExp nonNumericRx("[^0-9.,+-]");
Chris@148 177
Chris@283 178 for (int i = 0; i < list.size(); ++i) {
Chris@148 179
Chris@283 180 QString s(list[i].trimmed());
Chris@148 181
Chris@283 182 if (s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"")) {
Chris@283 183 s = s.mid(1, s.length() - 2);
Chris@283 184 } else if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) {
Chris@283 185 s = s.mid(1, s.length() - 2);
Chris@283 186 }
Chris@148 187
Chris@283 188 if (i == 0 && timingType == CSVFormatDialog::ExplicitTiming) {
Chris@148 189
Chris@283 190 bool ok = false;
Chris@283 191 QString numeric = s;
Chris@283 192 numeric.remove(nonNumericRx);
Chris@148 193
Chris@283 194 if (timeUnits == CSVFormatDialog::TimeSeconds) {
Chris@148 195
Chris@283 196 double time = numeric.toDouble(&ok);
Chris@283 197 frameNo = int(time * sampleRate + 0.00001);
Chris@148 198
Chris@283 199 } else {
Chris@148 200
Chris@283 201 frameNo = numeric.toInt(&ok);
Chris@148 202
Chris@283 203 if (timeUnits == CSVFormatDialog::TimeWindows) {
Chris@283 204 frameNo *= windowSize;
Chris@283 205 }
Chris@283 206 }
Chris@148 207
Chris@283 208 if (!ok) {
Chris@283 209 if (warnings < warnLimit) {
Chris@283 210 std::cerr << "WARNING: CSVFileReader::load: "
Chris@283 211 << "Bad time format (\"" << s.toStdString()
Chris@283 212 << "\") in data line "
Chris@283 213 << lineno << ":" << std::endl;
Chris@283 214 std::cerr << line.toStdString() << std::endl;
Chris@283 215 } else if (warnings == warnLimit) {
Chris@283 216 std::cerr << "WARNING: Too many warnings" << std::endl;
Chris@283 217 }
Chris@283 218 ++warnings;
Chris@283 219 }
Chris@283 220 } else {
Chris@283 221 tidyList.push_back(s);
Chris@283 222 }
Chris@283 223 }
Chris@148 224
Chris@283 225 if (modelType == CSVFormatDialog::OneDimensionalModel) {
Chris@148 226
Chris@283 227 SparseOneDimensionalModel::Point point
Chris@283 228 (frameNo,
Chris@283 229 tidyList.size() > 0 ? tidyList[tidyList.size()-1] :
Chris@283 230 QString("%1").arg(lineno));
Chris@148 231
Chris@283 232 model1->addPoint(point);
Chris@148 233
Chris@283 234 } else if (modelType == CSVFormatDialog::TwoDimensionalModel) {
Chris@148 235
Chris@283 236 SparseTimeValueModel::Point point
Chris@283 237 (frameNo,
Chris@283 238 tidyList.size() > 0 ? tidyList[0].toFloat() : 0.0,
Chris@283 239 tidyList.size() > 1 ? tidyList[1] : QString("%1").arg(lineno));
Chris@148 240
Chris@283 241 model2->addPoint(point);
Chris@148 242
Chris@283 243 } else if (modelType == CSVFormatDialog::ThreeDimensionalModel) {
Chris@148 244
Chris@283 245 DenseThreeDimensionalModel::Column values;
Chris@148 246
Chris@283 247 for (int i = 0; i < tidyList.size(); ++i) {
Chris@148 248
Chris@283 249 bool ok = false;
Chris@283 250 float value = list[i].toFloat(&ok);
Chris@283 251 values.push_back(value);
Chris@148 252
Chris@283 253 if ((lineno == 0 && i == 0) || value < min) min = value;
Chris@283 254 if ((lineno == 0 && i == 0) || value > max) max = value;
Chris@148 255
Chris@283 256 if (!ok) {
Chris@283 257 if (warnings < warnLimit) {
Chris@283 258 std::cerr << "WARNING: CSVFileReader::load: "
Chris@283 259 << "Non-numeric value in data line " << lineno
Chris@283 260 << ":" << std::endl;
Chris@283 261 std::cerr << line.toStdString() << std::endl;
Chris@283 262 ++warnings;
Chris@283 263 } else if (warnings == warnLimit) {
Chris@283 264 std::cerr << "WARNING: Too many warnings" << std::endl;
Chris@283 265 }
Chris@283 266 }
Chris@283 267 }
Chris@148 268
Chris@283 269 std::cerr << "Setting bin values for count " << lineno << ", frame "
Chris@283 270 << frameNo << ", time " << RealTime::frame2RealTime(frameNo, sampleRate) << std::endl;
Chris@148 271
Chris@283 272 model3->setColumn(frameNo / model3->getResolution(), values);
Chris@283 273 }
Chris@148 274
Chris@283 275 ++lineno;
Chris@283 276 if (timingType == CSVFormatDialog::ImplicitTiming ||
Chris@283 277 list.size() == 0) {
Chris@283 278 frameNo += windowSize;
Chris@283 279 }
Chris@283 280 }
Chris@148 281 }
Chris@148 282
Chris@148 283 if (modelType == CSVFormatDialog::ThreeDimensionalModel) {
Chris@148 284 model3->setMinimumLevel(min);
Chris@148 285 model3->setMaximumLevel(max);
Chris@148 286 }
Chris@148 287
Chris@148 288 return model;
Chris@148 289 }
Chris@148 290
Chris@148 291
Chris@148 292 CSVFormatDialog::CSVFormatDialog(QWidget *parent, QFile *file,
Chris@148 293 size_t defaultSampleRate) :
Chris@148 294 QDialog(parent),
Chris@148 295 m_modelType(OneDimensionalModel),
Chris@148 296 m_timingType(ExplicitTiming),
Chris@148 297 m_timeUnits(TimeAudioFrames),
Chris@148 298 m_separator("")
Chris@148 299 {
Chris@148 300 setModal(true);
Chris@148 301 setWindowTitle(tr("Select Data Format"));
Chris@148 302
Chris@148 303 (void)guessFormat(file);
Chris@148 304
Chris@148 305 QGridLayout *layout = new QGridLayout;
Chris@148 306
Chris@148 307 layout->addWidget(new QLabel(tr("\nPlease select the correct data format for this file.\n")),
Chris@148 308 0, 0, 1, 4);
Chris@148 309
Chris@148 310 layout->addWidget(new QLabel(tr("Each row specifies:")), 1, 0);
Chris@148 311
Chris@148 312 m_modelTypeCombo = new QComboBox;
Chris@148 313 m_modelTypeCombo->addItem(tr("A point in time"));
Chris@148 314 m_modelTypeCombo->addItem(tr("A value at a time"));
Chris@148 315 m_modelTypeCombo->addItem(tr("A set of values"));
Chris@148 316 layout->addWidget(m_modelTypeCombo, 1, 1, 1, 2);
Chris@148 317 connect(m_modelTypeCombo, SIGNAL(activated(int)),
Chris@148 318 this, SLOT(modelTypeChanged(int)));
Chris@148 319 m_modelTypeCombo->setCurrentIndex(int(m_modelType));
Chris@148 320
Chris@148 321 layout->addWidget(new QLabel(tr("The first column contains:")), 2, 0);
Chris@148 322
Chris@148 323 m_timingTypeCombo = new QComboBox;
Chris@148 324 m_timingTypeCombo->addItem(tr("Time, in seconds"));
Chris@148 325 m_timingTypeCombo->addItem(tr("Time, in audio sample frames"));
Chris@148 326 m_timingTypeCombo->addItem(tr("Data (rows are consecutive in time)"));
Chris@148 327 layout->addWidget(m_timingTypeCombo, 2, 1, 1, 2);
Chris@148 328 connect(m_timingTypeCombo, SIGNAL(activated(int)),
Chris@148 329 this, SLOT(timingTypeChanged(int)));
Chris@148 330 m_timingTypeCombo->setCurrentIndex(m_timingType == ExplicitTiming ?
Chris@148 331 m_timeUnits == TimeSeconds ? 0 : 1 : 2);
Chris@148 332
Chris@148 333 m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):"));
Chris@148 334 layout->addWidget(m_sampleRateLabel, 3, 0);
Chris@148 335
Chris@148 336 size_t sampleRates[] = {
Chris@148 337 8000, 11025, 12000, 22050, 24000, 32000,
Chris@148 338 44100, 48000, 88200, 96000, 176400, 192000
Chris@148 339 };
Chris@148 340
Chris@148 341 m_sampleRateCombo = new QComboBox;
Chris@148 342 m_sampleRate = defaultSampleRate;
Chris@148 343 for (size_t i = 0; i < sizeof(sampleRates) / sizeof(sampleRates[0]); ++i) {
Chris@148 344 m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i]));
Chris@148 345 if (sampleRates[i] == m_sampleRate) m_sampleRateCombo->setCurrentIndex(i);
Chris@148 346 }
Chris@148 347 m_sampleRateCombo->setEditable(true);
Chris@148 348
Chris@148 349 layout->addWidget(m_sampleRateCombo, 3, 1);
Chris@148 350 connect(m_sampleRateCombo, SIGNAL(activated(QString)),
Chris@148 351 this, SLOT(sampleRateChanged(QString)));
Chris@148 352 connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)),
Chris@148 353 this, SLOT(sampleRateChanged(QString)));
Chris@148 354
Chris@148 355 m_windowSizeLabel = new QLabel(tr("Frame increment between rows:"));
Chris@148 356 layout->addWidget(m_windowSizeLabel, 4, 0);
Chris@148 357
Chris@148 358 m_windowSizeCombo = new QComboBox;
Chris@148 359 m_windowSize = 1024;
Chris@148 360 for (int i = 0; i <= 16; ++i) {
Chris@148 361 int value = 1 << i;
Chris@148 362 m_windowSizeCombo->addItem(QString("%1").arg(value));
Chris@259 363 if (value == int(m_windowSize)) m_windowSizeCombo->setCurrentIndex(i);
Chris@148 364 }
Chris@148 365 m_windowSizeCombo->setEditable(true);
Chris@148 366
Chris@148 367 layout->addWidget(m_windowSizeCombo, 4, 1);
Chris@148 368 connect(m_windowSizeCombo, SIGNAL(activated(QString)),
Chris@148 369 this, SLOT(windowSizeChanged(QString)));
Chris@148 370 connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)),
Chris@148 371 this, SLOT(windowSizeChanged(QString)));
Chris@148 372
Chris@148 373 layout->addWidget(new QLabel(tr("\nExample data from file:")), 5, 0, 1, 4);
Chris@148 374
Chris@148 375 m_exampleWidget = new QTableWidget
Chris@148 376 (std::min(10, m_example.size()), m_maxExampleCols);
Chris@148 377
Chris@148 378 layout->addWidget(m_exampleWidget, 6, 0, 1, 4);
Chris@148 379 layout->setColumnStretch(3, 10);
Chris@148 380 layout->setRowStretch(4, 10);
Chris@148 381
Chris@148 382 QPushButton *ok = new QPushButton(tr("OK"));
Chris@148 383 connect(ok, SIGNAL(clicked()), this, SLOT(accept()));
Chris@148 384 ok->setDefault(true);
Chris@148 385
Chris@148 386 QPushButton *cancel = new QPushButton(tr("Cancel"));
Chris@148 387 connect(cancel, SIGNAL(clicked()), this, SLOT(reject()));
Chris@148 388
Chris@148 389 QHBoxLayout *buttonLayout = new QHBoxLayout;
Chris@148 390 buttonLayout->addStretch(1);
Chris@148 391 buttonLayout->addWidget(ok);
Chris@148 392 buttonLayout->addWidget(cancel);
Chris@148 393
Chris@148 394 QVBoxLayout *mainLayout = new QVBoxLayout;
Chris@148 395 mainLayout->addLayout(layout);
Chris@148 396 mainLayout->addLayout(buttonLayout);
Chris@148 397
Chris@148 398 setLayout(mainLayout);
Chris@148 399
Chris@148 400 timingTypeChanged(m_timingTypeCombo->currentIndex());
Chris@148 401 }
Chris@148 402
Chris@148 403 CSVFormatDialog::~CSVFormatDialog()
Chris@148 404 {
Chris@148 405 }
Chris@148 406
Chris@148 407 void
Chris@148 408 CSVFormatDialog::populateExample()
Chris@148 409 {
Chris@148 410 m_exampleWidget->setColumnCount
Chris@148 411 (m_timingType == ExplicitTiming ?
Chris@148 412 m_maxExampleCols - 1 : m_maxExampleCols);
Chris@148 413
Chris@148 414 m_exampleWidget->setHorizontalHeaderLabels(QStringList());
Chris@148 415
Chris@148 416 for (int i = 0; i < m_example.size(); ++i) {
Chris@148 417 for (int j = 0; j < m_example[i].size(); ++j) {
Chris@148 418
Chris@148 419 QTableWidgetItem *item = new QTableWidgetItem(m_example[i][j]);
Chris@148 420
Chris@148 421 if (j == 0) {
Chris@148 422 if (m_timingType == ExplicitTiming) {
Chris@148 423 m_exampleWidget->setVerticalHeaderItem(i, item);
Chris@148 424 continue;
Chris@148 425 } else {
Chris@148 426 QTableWidgetItem *header =
Chris@148 427 new QTableWidgetItem(QString("%1").arg(i));
Chris@148 428 header->setFlags(Qt::ItemIsEnabled);
Chris@148 429 m_exampleWidget->setVerticalHeaderItem(i, header);
Chris@148 430 }
Chris@148 431 }
Chris@148 432 int index = j;
Chris@148 433 if (m_timingType == ExplicitTiming) --index;
Chris@148 434 item->setFlags(Qt::ItemIsEnabled);
Chris@148 435 m_exampleWidget->setItem(i, index, item);
Chris@148 436 }
Chris@148 437 }
Chris@148 438 }
Chris@148 439
Chris@148 440 void
Chris@148 441 CSVFormatDialog::modelTypeChanged(int type)
Chris@148 442 {
Chris@148 443 m_modelType = (ModelType)type;
Chris@148 444
Chris@148 445 if (m_modelType == ThreeDimensionalModel) {
Chris@148 446 // We can't load 3d models with explicit timing, because the 3d
Chris@148 447 // model is dense so we need a fixed sample increment
Chris@148 448 m_timingTypeCombo->setCurrentIndex(2);
Chris@148 449 timingTypeChanged(2);
Chris@148 450 }
Chris@148 451 }
Chris@148 452
Chris@148 453 void
Chris@148 454 CSVFormatDialog::timingTypeChanged(int type)
Chris@148 455 {
Chris@148 456 switch (type) {
Chris@148 457
Chris@148 458 case 0:
Chris@148 459 m_timingType = ExplicitTiming;
Chris@148 460 m_timeUnits = TimeSeconds;
Chris@148 461 m_sampleRateCombo->setEnabled(false);
Chris@148 462 m_sampleRateLabel->setEnabled(false);
Chris@148 463 m_windowSizeCombo->setEnabled(false);
Chris@148 464 m_windowSizeLabel->setEnabled(false);
Chris@148 465 if (m_modelType == ThreeDimensionalModel) {
Chris@148 466 m_modelTypeCombo->setCurrentIndex(1);
Chris@148 467 modelTypeChanged(1);
Chris@148 468 }
Chris@148 469 break;
Chris@148 470
Chris@148 471 case 1:
Chris@148 472 m_timingType = ExplicitTiming;
Chris@148 473 m_timeUnits = TimeAudioFrames;
Chris@148 474 m_sampleRateCombo->setEnabled(true);
Chris@148 475 m_sampleRateLabel->setEnabled(true);
Chris@148 476 m_windowSizeCombo->setEnabled(false);
Chris@148 477 m_windowSizeLabel->setEnabled(false);
Chris@148 478 if (m_modelType == ThreeDimensionalModel) {
Chris@148 479 m_modelTypeCombo->setCurrentIndex(1);
Chris@148 480 modelTypeChanged(1);
Chris@148 481 }
Chris@148 482 break;
Chris@148 483
Chris@148 484 case 2:
Chris@148 485 m_timingType = ImplicitTiming;
Chris@148 486 m_timeUnits = TimeWindows;
Chris@148 487 m_sampleRateCombo->setEnabled(true);
Chris@148 488 m_sampleRateLabel->setEnabled(true);
Chris@148 489 m_windowSizeCombo->setEnabled(true);
Chris@148 490 m_windowSizeLabel->setEnabled(true);
Chris@148 491 break;
Chris@148 492 }
Chris@148 493
Chris@148 494 populateExample();
Chris@148 495 }
Chris@148 496
Chris@148 497 void
Chris@148 498 CSVFormatDialog::sampleRateChanged(QString rateString)
Chris@148 499 {
Chris@148 500 bool ok = false;
Chris@148 501 int sampleRate = rateString.toInt(&ok);
Chris@148 502 if (ok) m_sampleRate = sampleRate;
Chris@148 503 }
Chris@148 504
Chris@148 505 void
Chris@148 506 CSVFormatDialog::windowSizeChanged(QString sizeString)
Chris@148 507 {
Chris@148 508 bool ok = false;
Chris@148 509 int size = sizeString.toInt(&ok);
Chris@148 510 if (ok) m_windowSize = size;
Chris@148 511 }
Chris@148 512
Chris@148 513 bool
Chris@148 514 CSVFormatDialog::guessFormat(QFile *file)
Chris@148 515 {
Chris@148 516 QTextStream in(file);
Chris@148 517 in.seek(0);
Chris@148 518
Chris@148 519 unsigned int lineno = 0;
Chris@148 520
Chris@148 521 bool nonIncreasingPrimaries = false;
Chris@148 522 bool nonNumericPrimaries = false;
Chris@148 523 bool floatPrimaries = false;
Chris@148 524 bool variableItemCount = false;
Chris@148 525 int itemCount = 1;
Chris@148 526 int earliestNonNumericItem = -1;
Chris@148 527
Chris@148 528 float prevPrimary = 0.0;
Chris@148 529
Chris@148 530 m_maxExampleCols = 0;
Chris@148 531
Chris@148 532 while (!in.atEnd()) {
Chris@148 533
Chris@283 534 // See comment about line endings in load() above
Chris@148 535
Chris@283 536 QString chunk = in.readLine();
Chris@283 537 QStringList lines = chunk.split('\r', QString::SkipEmptyParts);
Chris@148 538
Chris@283 539 for (size_t li = 0; li < lines.size(); ++li) {
Chris@283 540
Chris@283 541 QString line = lines[li];
Chris@283 542
Chris@283 543 if (line.startsWith("#")) continue;
Chris@283 544
Chris@283 545 if (m_separator == "") {
Chris@283 546 //!!! to do: ask the user
Chris@283 547 if (line.split(",").size() >= 2) m_separator = ",";
Chris@283 548 else if (line.split("\t").size() >= 2) m_separator = "\t";
Chris@283 549 else if (line.split("|").size() >= 2) m_separator = "|";
Chris@283 550 else if (line.split("/").size() >= 2) m_separator = "/";
Chris@283 551 else if (line.split(":").size() >= 2) m_separator = ":";
Chris@283 552 else m_separator = " ";
Chris@283 553 }
Chris@283 554
Chris@283 555 QStringList list = line.split(m_separator);
Chris@283 556 QStringList tidyList;
Chris@283 557
Chris@283 558 for (int i = 0; i < list.size(); ++i) {
Chris@148 559
Chris@283 560 QString s(list[i]);
Chris@283 561 bool numeric = false;
Chris@148 562
Chris@283 563 if (s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"")) {
Chris@283 564 s = s.mid(1, s.length() - 2);
Chris@283 565 } else if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) {
Chris@283 566 s = s.mid(1, s.length() - 2);
Chris@283 567 } else {
Chris@283 568 (void)s.toFloat(&numeric);
Chris@283 569 }
Chris@148 570
Chris@283 571 tidyList.push_back(s);
Chris@148 572
Chris@283 573 if (lineno == 0 || (list.size() < itemCount)) {
Chris@283 574 itemCount = list.size();
Chris@283 575 } else {
Chris@283 576 if (itemCount != list.size()) {
Chris@283 577 variableItemCount = true;
Chris@283 578 }
Chris@283 579 }
Chris@148 580
Chris@283 581 if (i == 0) { // primary
Chris@148 582
Chris@283 583 if (numeric) {
Chris@148 584
Chris@283 585 float primary = s.toFloat();
Chris@148 586
Chris@283 587 if (lineno > 0 && primary <= prevPrimary) {
Chris@283 588 nonIncreasingPrimaries = true;
Chris@283 589 }
Chris@148 590
Chris@283 591 if (s.contains(".") || s.contains(",")) {
Chris@283 592 floatPrimaries = true;
Chris@283 593 }
Chris@148 594
Chris@283 595 prevPrimary = primary;
Chris@148 596
Chris@283 597 } else {
Chris@283 598 nonNumericPrimaries = true;
Chris@283 599 }
Chris@283 600 } else { // secondary
Chris@148 601
Chris@283 602 if (!numeric) {
Chris@283 603 if (earliestNonNumericItem < 0 ||
Chris@283 604 i < earliestNonNumericItem) {
Chris@283 605 earliestNonNumericItem = i;
Chris@283 606 }
Chris@283 607 }
Chris@283 608 }
Chris@283 609 }
Chris@148 610
Chris@283 611 if (lineno < 10) {
Chris@283 612 m_example.push_back(tidyList);
Chris@283 613 if (lineno == 0 || tidyList.size() > m_maxExampleCols) {
Chris@283 614 m_maxExampleCols = tidyList.size();
Chris@283 615 }
Chris@283 616 }
Chris@148 617
Chris@283 618 ++lineno;
Chris@148 619
Chris@283 620 if (lineno == 50) break;
Chris@283 621 }
Chris@148 622 }
Chris@148 623
Chris@148 624 if (nonNumericPrimaries || nonIncreasingPrimaries) {
Chris@148 625
Chris@148 626 // Primaries are probably not a series of times
Chris@148 627
Chris@148 628 m_timingType = ImplicitTiming;
Chris@148 629 m_timeUnits = TimeWindows;
Chris@148 630
Chris@148 631 if (nonNumericPrimaries) {
Chris@148 632 m_modelType = OneDimensionalModel;
Chris@148 633 } else if (itemCount == 1 || variableItemCount ||
Chris@148 634 (earliestNonNumericItem != -1)) {
Chris@148 635 m_modelType = TwoDimensionalModel;
Chris@148 636 } else {
Chris@148 637 m_modelType = ThreeDimensionalModel;
Chris@148 638 }
Chris@148 639
Chris@148 640 } else {
Chris@148 641
Chris@148 642 // Increasing numeric primaries -- likely to be time
Chris@148 643
Chris@148 644 m_timingType = ExplicitTiming;
Chris@148 645
Chris@148 646 if (floatPrimaries) {
Chris@148 647 m_timeUnits = TimeSeconds;
Chris@148 648 } else {
Chris@148 649 m_timeUnits = TimeAudioFrames;
Chris@148 650 }
Chris@148 651
Chris@148 652 if (itemCount == 1) {
Chris@148 653 m_modelType = OneDimensionalModel;
Chris@148 654 } else if (variableItemCount || (earliestNonNumericItem != -1)) {
Chris@148 655 if (earliestNonNumericItem != -1 && earliestNonNumericItem < 2) {
Chris@148 656 m_modelType = OneDimensionalModel;
Chris@148 657 } else {
Chris@148 658 m_modelType = TwoDimensionalModel;
Chris@148 659 }
Chris@148 660 } else {
Chris@148 661 m_modelType = ThreeDimensionalModel;
Chris@148 662 }
Chris@148 663 }
Chris@148 664
Chris@148 665 std::cerr << "Estimated model type: " << m_modelType << std::endl;
Chris@148 666 std::cerr << "Estimated timing type: " << m_timingType << std::endl;
Chris@148 667 std::cerr << "Estimated units: " << m_timeUnits << std::endl;
Chris@148 668
Chris@148 669 in.seek(0);
Chris@148 670 return true;
Chris@148 671 }