Chris@148: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@148: Chris@148: /* Chris@148: Sonic Visualiser Chris@148: An audio file viewer and annotation editor. Chris@148: Centre for Digital Music, Queen Mary, University of London. Chris@148: This file copyright 2006 Chris Cannam. Chris@148: Chris@148: This program is free software; you can redistribute it and/or Chris@148: modify it under the terms of the GNU General Public License as Chris@148: published by the Free Software Foundation; either version 2 of the Chris@148: License, or (at your option) any later version. See the file Chris@148: COPYING included with this distribution for more information. Chris@148: */ Chris@148: Chris@148: #include "CSVFileReader.h" Chris@148: Chris@150: #include "model/Model.h" Chris@148: #include "base/RealTime.h" Chris@148: #include "model/SparseOneDimensionalModel.h" Chris@148: #include "model/SparseTimeValueModel.h" Chris@152: #include "model/EditableDenseThreeDimensionalModel.h" Chris@308: #include "DataFileReaderFactory.h" Chris@148: Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: #include Chris@148: Chris@148: #include Chris@148: Chris@148: CSVFileReader::CSVFileReader(QString path, size_t mainModelSampleRate) : Chris@148: m_file(0), Chris@148: m_mainModelSampleRate(mainModelSampleRate) Chris@148: { Chris@148: m_file = new QFile(path); Chris@148: bool good = false; Chris@148: Chris@148: if (!m_file->exists()) { Chris@148: m_error = QFile::tr("File \"%1\" does not exist").arg(path); Chris@148: } else if (!m_file->open(QIODevice::ReadOnly | QIODevice::Text)) { Chris@148: m_error = QFile::tr("Failed to open file \"%1\"").arg(path); Chris@148: } else { Chris@148: good = true; Chris@148: } Chris@148: Chris@148: if (!good) { Chris@148: delete m_file; Chris@148: m_file = 0; Chris@148: } Chris@148: } Chris@148: Chris@148: CSVFileReader::~CSVFileReader() Chris@148: { Chris@148: std::cerr << "CSVFileReader::~CSVFileReader: file is " << m_file << std::endl; Chris@148: Chris@148: if (m_file) { Chris@148: std::cerr << "CSVFileReader::CSVFileReader: Closing file" << std::endl; Chris@148: m_file->close(); Chris@148: } Chris@148: delete m_file; Chris@148: } Chris@148: Chris@148: bool Chris@148: CSVFileReader::isOK() const Chris@148: { Chris@148: return (m_file != 0); Chris@148: } Chris@148: Chris@148: QString Chris@148: CSVFileReader::getError() const Chris@148: { Chris@148: return m_error; Chris@148: } Chris@148: Chris@148: Model * Chris@148: CSVFileReader::load() const Chris@148: { Chris@148: if (!m_file) return 0; Chris@148: Chris@148: CSVFormatDialog *dialog = new CSVFormatDialog Chris@148: (0, m_file, m_mainModelSampleRate); Chris@148: Chris@148: if (dialog->exec() == QDialog::Rejected) { Chris@148: delete dialog; Chris@308: throw DataFileReaderFactory::ImportCancelled; Chris@148: } Chris@148: Chris@148: CSVFormatDialog::ModelType modelType = dialog->getModelType(); Chris@148: CSVFormatDialog::TimingType timingType = dialog->getTimingType(); Chris@148: CSVFormatDialog::TimeUnits timeUnits = dialog->getTimeUnits(); Chris@148: QString separator = dialog->getSeparator(); Chris@148: size_t sampleRate = dialog->getSampleRate(); Chris@148: size_t windowSize = dialog->getWindowSize(); Chris@148: Chris@148: delete dialog; Chris@148: Chris@148: if (timingType == CSVFormatDialog::ExplicitTiming) { Chris@148: windowSize = 1; Chris@148: if (timeUnits == CSVFormatDialog::TimeSeconds) { Chris@148: sampleRate = m_mainModelSampleRate; Chris@148: } Chris@148: } Chris@148: Chris@148: SparseOneDimensionalModel *model1 = 0; Chris@148: SparseTimeValueModel *model2 = 0; Chris@152: EditableDenseThreeDimensionalModel *model3 = 0; Chris@148: Model *model = 0; Chris@148: Chris@148: QTextStream in(m_file); Chris@148: in.seek(0); Chris@148: Chris@148: unsigned int warnings = 0, warnLimit = 10; Chris@148: unsigned int lineno = 0; Chris@148: Chris@148: float min = 0.0, max = 0.0; Chris@148: Chris@148: size_t frameNo = 0; Chris@148: Chris@148: while (!in.atEnd()) { Chris@148: Chris@283: // QTextStream's readLine doesn't cope with old-style Mac Chris@283: // CR-only line endings. Why did they bother making the class Chris@283: // cope with more than one sort of line ending, if it still Chris@283: // can't be configured to cope with all the common sorts? Chris@148: Chris@283: // For the time being we'll deal with this case (which is Chris@283: // relatively uncommon for us, but still necessary to handle) Chris@283: // by reading the entire file using a single readLine, and Chris@283: // splitting it. For CR and CR/LF line endings this will just Chris@283: // read a line at a time, and that's obviously OK. Chris@148: Chris@283: QString chunk = in.readLine(); Chris@283: QStringList lines = chunk.split('\r', QString::SkipEmptyParts); Chris@283: Chris@283: for (size_t li = 0; li < lines.size(); ++li) { Chris@148: Chris@283: QString line = lines[li]; Chris@148: Chris@283: if (line.startsWith("#")) continue; Chris@283: Chris@283: QStringList list = line.split(separator, QString::KeepEmptyParts); Chris@283: Chris@283: if (!model) { Chris@283: Chris@283: switch (modelType) { Chris@283: Chris@283: case CSVFormatDialog::OneDimensionalModel: Chris@283: model1 = new SparseOneDimensionalModel(sampleRate, windowSize); Chris@283: model = model1; Chris@283: break; Chris@148: Chris@283: case CSVFormatDialog::TwoDimensionalModel: Chris@283: model2 = new SparseTimeValueModel(sampleRate, windowSize, false); Chris@283: model = model2; Chris@283: break; Chris@148: Chris@283: case CSVFormatDialog::ThreeDimensionalModel: Chris@283: model3 = new EditableDenseThreeDimensionalModel(sampleRate, Chris@283: windowSize, Chris@283: list.size()); Chris@283: model = model3; Chris@283: break; Chris@283: } Chris@283: } Chris@148: Chris@283: QStringList tidyList; Chris@283: QRegExp nonNumericRx("[^0-9.,+-]"); Chris@148: Chris@283: for (int i = 0; i < list.size(); ++i) { Chris@148: Chris@283: QString s(list[i].trimmed()); Chris@148: Chris@283: if (s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"")) { Chris@283: s = s.mid(1, s.length() - 2); Chris@283: } else if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) { Chris@283: s = s.mid(1, s.length() - 2); Chris@283: } Chris@148: Chris@283: if (i == 0 && timingType == CSVFormatDialog::ExplicitTiming) { Chris@148: Chris@283: bool ok = false; Chris@283: QString numeric = s; Chris@283: numeric.remove(nonNumericRx); Chris@148: Chris@283: if (timeUnits == CSVFormatDialog::TimeSeconds) { Chris@148: Chris@283: double time = numeric.toDouble(&ok); Chris@283: frameNo = int(time * sampleRate + 0.00001); Chris@148: Chris@283: } else { Chris@148: Chris@283: frameNo = numeric.toInt(&ok); Chris@148: Chris@283: if (timeUnits == CSVFormatDialog::TimeWindows) { Chris@283: frameNo *= windowSize; Chris@283: } Chris@283: } Chris@148: Chris@283: if (!ok) { Chris@283: if (warnings < warnLimit) { Chris@283: std::cerr << "WARNING: CSVFileReader::load: " Chris@283: << "Bad time format (\"" << s.toStdString() Chris@283: << "\") in data line " Chris@283: << lineno << ":" << std::endl; Chris@283: std::cerr << line.toStdString() << std::endl; Chris@283: } else if (warnings == warnLimit) { Chris@283: std::cerr << "WARNING: Too many warnings" << std::endl; Chris@283: } Chris@283: ++warnings; Chris@283: } Chris@283: } else { Chris@283: tidyList.push_back(s); Chris@283: } Chris@283: } Chris@148: Chris@283: if (modelType == CSVFormatDialog::OneDimensionalModel) { Chris@148: Chris@283: SparseOneDimensionalModel::Point point Chris@283: (frameNo, Chris@283: tidyList.size() > 0 ? tidyList[tidyList.size()-1] : Chris@283: QString("%1").arg(lineno)); Chris@148: Chris@283: model1->addPoint(point); Chris@148: Chris@283: } else if (modelType == CSVFormatDialog::TwoDimensionalModel) { Chris@148: Chris@283: SparseTimeValueModel::Point point Chris@283: (frameNo, Chris@283: tidyList.size() > 0 ? tidyList[0].toFloat() : 0.0, Chris@283: tidyList.size() > 1 ? tidyList[1] : QString("%1").arg(lineno)); Chris@148: Chris@283: model2->addPoint(point); Chris@148: Chris@283: } else if (modelType == CSVFormatDialog::ThreeDimensionalModel) { Chris@148: Chris@283: DenseThreeDimensionalModel::Column values; Chris@148: Chris@283: for (int i = 0; i < tidyList.size(); ++i) { Chris@148: Chris@283: bool ok = false; Chris@283: float value = list[i].toFloat(&ok); Chris@283: values.push_back(value); Chris@148: Chris@283: if ((lineno == 0 && i == 0) || value < min) min = value; Chris@283: if ((lineno == 0 && i == 0) || value > max) max = value; Chris@148: Chris@283: if (!ok) { Chris@283: if (warnings < warnLimit) { Chris@283: std::cerr << "WARNING: CSVFileReader::load: " Chris@283: << "Non-numeric value in data line " << lineno Chris@283: << ":" << std::endl; Chris@283: std::cerr << line.toStdString() << std::endl; Chris@283: ++warnings; Chris@283: } else if (warnings == warnLimit) { Chris@283: std::cerr << "WARNING: Too many warnings" << std::endl; Chris@283: } Chris@283: } Chris@283: } Chris@148: Chris@283: std::cerr << "Setting bin values for count " << lineno << ", frame " Chris@283: << frameNo << ", time " << RealTime::frame2RealTime(frameNo, sampleRate) << std::endl; Chris@148: Chris@283: model3->setColumn(frameNo / model3->getResolution(), values); Chris@283: } Chris@148: Chris@283: ++lineno; Chris@283: if (timingType == CSVFormatDialog::ImplicitTiming || Chris@283: list.size() == 0) { Chris@283: frameNo += windowSize; Chris@283: } Chris@283: } Chris@148: } Chris@148: Chris@148: if (modelType == CSVFormatDialog::ThreeDimensionalModel) { Chris@148: model3->setMinimumLevel(min); Chris@148: model3->setMaximumLevel(max); Chris@148: } Chris@148: Chris@148: return model; Chris@148: } Chris@148: Chris@148: Chris@148: CSVFormatDialog::CSVFormatDialog(QWidget *parent, QFile *file, Chris@148: size_t defaultSampleRate) : Chris@148: QDialog(parent), Chris@148: m_modelType(OneDimensionalModel), Chris@148: m_timingType(ExplicitTiming), Chris@148: m_timeUnits(TimeAudioFrames), Chris@148: m_separator("") Chris@148: { Chris@148: setModal(true); Chris@148: setWindowTitle(tr("Select Data Format")); Chris@148: Chris@148: (void)guessFormat(file); Chris@148: Chris@148: QGridLayout *layout = new QGridLayout; Chris@148: Chris@308: layout->addWidget(new QLabel(tr("Select Data Format

Please select the correct data format for this file.")), Chris@148: 0, 0, 1, 4); Chris@148: Chris@148: layout->addWidget(new QLabel(tr("Each row specifies:")), 1, 0); Chris@148: Chris@148: m_modelTypeCombo = new QComboBox; Chris@148: m_modelTypeCombo->addItem(tr("A point in time")); Chris@148: m_modelTypeCombo->addItem(tr("A value at a time")); Chris@148: m_modelTypeCombo->addItem(tr("A set of values")); Chris@148: layout->addWidget(m_modelTypeCombo, 1, 1, 1, 2); Chris@148: connect(m_modelTypeCombo, SIGNAL(activated(int)), Chris@148: this, SLOT(modelTypeChanged(int))); Chris@148: m_modelTypeCombo->setCurrentIndex(int(m_modelType)); Chris@148: Chris@148: layout->addWidget(new QLabel(tr("The first column contains:")), 2, 0); Chris@148: Chris@148: m_timingTypeCombo = new QComboBox; Chris@148: m_timingTypeCombo->addItem(tr("Time, in seconds")); Chris@148: m_timingTypeCombo->addItem(tr("Time, in audio sample frames")); Chris@148: m_timingTypeCombo->addItem(tr("Data (rows are consecutive in time)")); Chris@148: layout->addWidget(m_timingTypeCombo, 2, 1, 1, 2); Chris@148: connect(m_timingTypeCombo, SIGNAL(activated(int)), Chris@148: this, SLOT(timingTypeChanged(int))); Chris@148: m_timingTypeCombo->setCurrentIndex(m_timingType == ExplicitTiming ? Chris@148: m_timeUnits == TimeSeconds ? 0 : 1 : 2); Chris@148: Chris@148: m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):")); Chris@148: layout->addWidget(m_sampleRateLabel, 3, 0); Chris@148: Chris@148: size_t sampleRates[] = { Chris@148: 8000, 11025, 12000, 22050, 24000, 32000, Chris@148: 44100, 48000, 88200, 96000, 176400, 192000 Chris@148: }; Chris@148: Chris@148: m_sampleRateCombo = new QComboBox; Chris@148: m_sampleRate = defaultSampleRate; Chris@148: for (size_t i = 0; i < sizeof(sampleRates) / sizeof(sampleRates[0]); ++i) { Chris@148: m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i])); Chris@148: if (sampleRates[i] == m_sampleRate) m_sampleRateCombo->setCurrentIndex(i); Chris@148: } Chris@148: m_sampleRateCombo->setEditable(true); Chris@148: Chris@148: layout->addWidget(m_sampleRateCombo, 3, 1); Chris@148: connect(m_sampleRateCombo, SIGNAL(activated(QString)), Chris@148: this, SLOT(sampleRateChanged(QString))); Chris@148: connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)), Chris@148: this, SLOT(sampleRateChanged(QString))); Chris@148: Chris@148: m_windowSizeLabel = new QLabel(tr("Frame increment between rows:")); Chris@148: layout->addWidget(m_windowSizeLabel, 4, 0); Chris@148: Chris@148: m_windowSizeCombo = new QComboBox; Chris@148: m_windowSize = 1024; Chris@148: for (int i = 0; i <= 16; ++i) { Chris@148: int value = 1 << i; Chris@148: m_windowSizeCombo->addItem(QString("%1").arg(value)); Chris@259: if (value == int(m_windowSize)) m_windowSizeCombo->setCurrentIndex(i); Chris@148: } Chris@148: m_windowSizeCombo->setEditable(true); Chris@148: Chris@148: layout->addWidget(m_windowSizeCombo, 4, 1); Chris@148: connect(m_windowSizeCombo, SIGNAL(activated(QString)), Chris@148: this, SLOT(windowSizeChanged(QString))); Chris@148: connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)), Chris@148: this, SLOT(windowSizeChanged(QString))); Chris@148: Chris@148: layout->addWidget(new QLabel(tr("\nExample data from file:")), 5, 0, 1, 4); Chris@148: Chris@148: m_exampleWidget = new QTableWidget Chris@148: (std::min(10, m_example.size()), m_maxExampleCols); Chris@148: Chris@148: layout->addWidget(m_exampleWidget, 6, 0, 1, 4); Chris@148: layout->setColumnStretch(3, 10); Chris@148: layout->setRowStretch(4, 10); Chris@148: Chris@148: QPushButton *ok = new QPushButton(tr("OK")); Chris@148: connect(ok, SIGNAL(clicked()), this, SLOT(accept())); Chris@148: ok->setDefault(true); Chris@148: Chris@148: QPushButton *cancel = new QPushButton(tr("Cancel")); Chris@148: connect(cancel, SIGNAL(clicked()), this, SLOT(reject())); Chris@148: Chris@148: QHBoxLayout *buttonLayout = new QHBoxLayout; Chris@148: buttonLayout->addStretch(1); Chris@148: buttonLayout->addWidget(ok); Chris@148: buttonLayout->addWidget(cancel); Chris@148: Chris@148: QVBoxLayout *mainLayout = new QVBoxLayout; Chris@148: mainLayout->addLayout(layout); Chris@148: mainLayout->addLayout(buttonLayout); Chris@148: Chris@148: setLayout(mainLayout); Chris@148: Chris@148: timingTypeChanged(m_timingTypeCombo->currentIndex()); Chris@148: } Chris@148: Chris@148: CSVFormatDialog::~CSVFormatDialog() Chris@148: { Chris@148: } Chris@148: Chris@148: void Chris@148: CSVFormatDialog::populateExample() Chris@148: { Chris@148: m_exampleWidget->setColumnCount Chris@148: (m_timingType == ExplicitTiming ? Chris@148: m_maxExampleCols - 1 : m_maxExampleCols); Chris@148: Chris@148: m_exampleWidget->setHorizontalHeaderLabels(QStringList()); Chris@148: Chris@148: for (int i = 0; i < m_example.size(); ++i) { Chris@148: for (int j = 0; j < m_example[i].size(); ++j) { Chris@148: Chris@148: QTableWidgetItem *item = new QTableWidgetItem(m_example[i][j]); Chris@148: Chris@148: if (j == 0) { Chris@148: if (m_timingType == ExplicitTiming) { Chris@148: m_exampleWidget->setVerticalHeaderItem(i, item); Chris@148: continue; Chris@148: } else { Chris@148: QTableWidgetItem *header = Chris@148: new QTableWidgetItem(QString("%1").arg(i)); Chris@148: header->setFlags(Qt::ItemIsEnabled); Chris@148: m_exampleWidget->setVerticalHeaderItem(i, header); Chris@148: } Chris@148: } Chris@148: int index = j; Chris@148: if (m_timingType == ExplicitTiming) --index; Chris@148: item->setFlags(Qt::ItemIsEnabled); Chris@148: m_exampleWidget->setItem(i, index, item); Chris@148: } Chris@148: } Chris@148: } Chris@148: Chris@148: void Chris@148: CSVFormatDialog::modelTypeChanged(int type) Chris@148: { Chris@148: m_modelType = (ModelType)type; Chris@148: Chris@148: if (m_modelType == ThreeDimensionalModel) { Chris@148: // We can't load 3d models with explicit timing, because the 3d Chris@148: // model is dense so we need a fixed sample increment Chris@148: m_timingTypeCombo->setCurrentIndex(2); Chris@148: timingTypeChanged(2); Chris@148: } Chris@148: } Chris@148: Chris@148: void Chris@148: CSVFormatDialog::timingTypeChanged(int type) Chris@148: { Chris@148: switch (type) { Chris@148: Chris@148: case 0: Chris@148: m_timingType = ExplicitTiming; Chris@148: m_timeUnits = TimeSeconds; Chris@148: m_sampleRateCombo->setEnabled(false); Chris@148: m_sampleRateLabel->setEnabled(false); Chris@148: m_windowSizeCombo->setEnabled(false); Chris@148: m_windowSizeLabel->setEnabled(false); Chris@148: if (m_modelType == ThreeDimensionalModel) { Chris@148: m_modelTypeCombo->setCurrentIndex(1); Chris@148: modelTypeChanged(1); Chris@148: } Chris@148: break; Chris@148: Chris@148: case 1: Chris@148: m_timingType = ExplicitTiming; Chris@148: m_timeUnits = TimeAudioFrames; Chris@148: m_sampleRateCombo->setEnabled(true); Chris@148: m_sampleRateLabel->setEnabled(true); Chris@148: m_windowSizeCombo->setEnabled(false); Chris@148: m_windowSizeLabel->setEnabled(false); Chris@148: if (m_modelType == ThreeDimensionalModel) { Chris@148: m_modelTypeCombo->setCurrentIndex(1); Chris@148: modelTypeChanged(1); Chris@148: } Chris@148: break; Chris@148: Chris@148: case 2: Chris@148: m_timingType = ImplicitTiming; Chris@148: m_timeUnits = TimeWindows; Chris@148: m_sampleRateCombo->setEnabled(true); Chris@148: m_sampleRateLabel->setEnabled(true); Chris@148: m_windowSizeCombo->setEnabled(true); Chris@148: m_windowSizeLabel->setEnabled(true); Chris@148: break; Chris@148: } Chris@148: Chris@148: populateExample(); Chris@148: } Chris@148: Chris@148: void Chris@148: CSVFormatDialog::sampleRateChanged(QString rateString) Chris@148: { Chris@148: bool ok = false; Chris@148: int sampleRate = rateString.toInt(&ok); Chris@148: if (ok) m_sampleRate = sampleRate; Chris@148: } Chris@148: Chris@148: void Chris@148: CSVFormatDialog::windowSizeChanged(QString sizeString) Chris@148: { Chris@148: bool ok = false; Chris@148: int size = sizeString.toInt(&ok); Chris@148: if (ok) m_windowSize = size; Chris@148: } Chris@148: Chris@148: bool Chris@148: CSVFormatDialog::guessFormat(QFile *file) Chris@148: { Chris@148: QTextStream in(file); Chris@148: in.seek(0); Chris@148: Chris@148: unsigned int lineno = 0; Chris@148: Chris@148: bool nonIncreasingPrimaries = false; Chris@148: bool nonNumericPrimaries = false; Chris@148: bool floatPrimaries = false; Chris@148: bool variableItemCount = false; Chris@148: int itemCount = 1; Chris@148: int earliestNonNumericItem = -1; Chris@148: Chris@148: float prevPrimary = 0.0; Chris@148: Chris@148: m_maxExampleCols = 0; Chris@148: Chris@148: while (!in.atEnd()) { Chris@148: Chris@283: // See comment about line endings in load() above Chris@148: Chris@283: QString chunk = in.readLine(); Chris@283: QStringList lines = chunk.split('\r', QString::SkipEmptyParts); Chris@148: Chris@283: for (size_t li = 0; li < lines.size(); ++li) { Chris@283: Chris@283: QString line = lines[li]; Chris@283: Chris@283: if (line.startsWith("#")) continue; Chris@283: Chris@283: if (m_separator == "") { Chris@283: //!!! to do: ask the user Chris@283: if (line.split(",").size() >= 2) m_separator = ","; Chris@283: else if (line.split("\t").size() >= 2) m_separator = "\t"; Chris@283: else if (line.split("|").size() >= 2) m_separator = "|"; Chris@283: else if (line.split("/").size() >= 2) m_separator = "/"; Chris@283: else if (line.split(":").size() >= 2) m_separator = ":"; Chris@283: else m_separator = " "; Chris@283: } Chris@283: Chris@283: QStringList list = line.split(m_separator); Chris@283: QStringList tidyList; Chris@283: Chris@283: for (int i = 0; i < list.size(); ++i) { Chris@148: Chris@283: QString s(list[i]); Chris@283: bool numeric = false; Chris@148: Chris@283: if (s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"")) { Chris@283: s = s.mid(1, s.length() - 2); Chris@283: } else if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) { Chris@283: s = s.mid(1, s.length() - 2); Chris@283: } else { Chris@283: (void)s.toFloat(&numeric); Chris@283: } Chris@148: Chris@283: tidyList.push_back(s); Chris@148: Chris@283: if (lineno == 0 || (list.size() < itemCount)) { Chris@283: itemCount = list.size(); Chris@283: } else { Chris@283: if (itemCount != list.size()) { Chris@283: variableItemCount = true; Chris@283: } Chris@283: } Chris@148: Chris@283: if (i == 0) { // primary Chris@148: Chris@283: if (numeric) { Chris@148: Chris@283: float primary = s.toFloat(); Chris@148: Chris@283: if (lineno > 0 && primary <= prevPrimary) { Chris@283: nonIncreasingPrimaries = true; Chris@283: } Chris@148: Chris@283: if (s.contains(".") || s.contains(",")) { Chris@283: floatPrimaries = true; Chris@283: } Chris@148: Chris@283: prevPrimary = primary; Chris@148: Chris@283: } else { Chris@283: nonNumericPrimaries = true; Chris@283: } Chris@283: } else { // secondary Chris@148: Chris@283: if (!numeric) { Chris@283: if (earliestNonNumericItem < 0 || Chris@283: i < earliestNonNumericItem) { Chris@283: earliestNonNumericItem = i; Chris@283: } Chris@283: } Chris@283: } Chris@283: } Chris@148: Chris@283: if (lineno < 10) { Chris@283: m_example.push_back(tidyList); Chris@283: if (lineno == 0 || tidyList.size() > m_maxExampleCols) { Chris@283: m_maxExampleCols = tidyList.size(); Chris@283: } Chris@283: } Chris@148: Chris@283: ++lineno; Chris@148: Chris@283: if (lineno == 50) break; Chris@283: } Chris@148: } Chris@148: Chris@148: if (nonNumericPrimaries || nonIncreasingPrimaries) { Chris@148: Chris@148: // Primaries are probably not a series of times Chris@148: Chris@148: m_timingType = ImplicitTiming; Chris@148: m_timeUnits = TimeWindows; Chris@148: Chris@148: if (nonNumericPrimaries) { Chris@148: m_modelType = OneDimensionalModel; Chris@148: } else if (itemCount == 1 || variableItemCount || Chris@148: (earliestNonNumericItem != -1)) { Chris@148: m_modelType = TwoDimensionalModel; Chris@148: } else { Chris@148: m_modelType = ThreeDimensionalModel; Chris@148: } Chris@148: Chris@148: } else { Chris@148: Chris@148: // Increasing numeric primaries -- likely to be time Chris@148: Chris@148: m_timingType = ExplicitTiming; Chris@148: Chris@148: if (floatPrimaries) { Chris@148: m_timeUnits = TimeSeconds; Chris@148: } else { Chris@148: m_timeUnits = TimeAudioFrames; Chris@148: } Chris@148: Chris@148: if (itemCount == 1) { Chris@148: m_modelType = OneDimensionalModel; Chris@148: } else if (variableItemCount || (earliestNonNumericItem != -1)) { Chris@148: if (earliestNonNumericItem != -1 && earliestNonNumericItem < 2) { Chris@148: m_modelType = OneDimensionalModel; Chris@148: } else { Chris@148: m_modelType = TwoDimensionalModel; Chris@148: } Chris@148: } else { Chris@148: m_modelType = ThreeDimensionalModel; Chris@148: } Chris@148: } Chris@148: Chris@148: std::cerr << "Estimated model type: " << m_modelType << std::endl; Chris@148: std::cerr << "Estimated timing type: " << m_timingType << std::endl; Chris@148: std::cerr << "Estimated units: " << m_timeUnits << std::endl; Chris@148: Chris@148: in.seek(0); Chris@148: return true; Chris@148: }