annotate data/fileio/CSVFileReader.cpp @ 148:1a42221a1522

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