annotate data/fileio/CSVFileReader.cpp @ 390:21e79997e80f

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