annotate data/fileio/CSVFileReader.cpp @ 316:3a6725f285d6

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