annotate data/fileio/CSVFileReader.cpp @ 263:71dfc6ab3b54

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