annotate data/fileio/CSVFormat.cpp @ 1871:bed42ce4d3ab csv-import-headers

Include the header row in the example output after all - it's less confusing I think to have it displayed (but e.g. italicised) than to have it appear and disappear
author Chris Cannam
date Thu, 18 Jun 2020 11:55:17 +0100
parents 1b8c4ee06f6d
children
rev   line source
Chris@392 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@392 2
Chris@392 3 /*
Chris@392 4 Sonic Visualiser
Chris@392 5 An audio file viewer and annotation editor.
Chris@392 6 Centre for Digital Music, Queen Mary, University of London.
Chris@392 7 This file copyright 2006 Chris Cannam.
Chris@392 8
Chris@392 9 This program is free software; you can redistribute it and/or
Chris@392 10 modify it under the terms of the GNU General Public License as
Chris@392 11 published by the Free Software Foundation; either version 2 of the
Chris@392 12 License, or (at your option) any later version. See the file
Chris@392 13 COPYING included with this distribution for more information.
Chris@392 14 */
Chris@392 15
Chris@392 16 #include "CSVFormat.h"
Chris@392 17
Chris@629 18 #include "base/StringBits.h"
Chris@629 19
Chris@392 20 #include <QFile>
Chris@392 21 #include <QString>
Chris@392 22 #include <QRegExp>
Chris@392 23 #include <QStringList>
Chris@392 24 #include <QTextStream>
Chris@392 25
Chris@392 26 #include <iostream>
Chris@392 27
Chris@1362 28 #include "base/Debug.h"
Chris@1362 29
Chris@629 30 CSVFormat::CSVFormat(QString path) :
Chris@629 31 m_separator(""),
Chris@392 32 m_sampleRate(44100),
Chris@392 33 m_windowSize(1024),
Chris@1870 34 m_headerStatus(HeaderUnknown),
Chris@1870 35 m_allowQuoting(true),
Chris@1870 36 m_maxExampleCols(0)
Chris@392 37 {
Chris@1524 38 (void)guessFormatFor(path);
Chris@629 39 }
Chris@629 40
Chris@1524 41 bool
Chris@629 42 CSVFormat::guessFormatFor(QString path)
Chris@629 43 {
Chris@629 44 m_modelType = TwoDimensionalModel;
Chris@629 45 m_timingType = ExplicitTiming;
Chris@629 46 m_timeUnits = TimeSeconds;
Chris@629 47
Chris@629 48 m_maxExampleCols = 0;
Chris@629 49 m_columnCount = 0;
Chris@629 50 m_variableColumnCount = false;
Chris@629 51
Chris@629 52 m_example.clear();
Chris@629 53 m_columnQualities.clear();
Chris@629 54 m_columnPurposes.clear();
Chris@629 55 m_prevValues.clear();
Chris@629 56
Chris@629 57 QFile file(path);
Chris@1524 58 if (!file.exists()) {
Chris@1524 59 SVCERR << "CSVFormat::guessFormatFor(" << path
Chris@1524 60 << "): File does not exist" << endl;
Chris@1524 61 return false;
Chris@1524 62 }
Chris@1524 63 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@1524 64 SVCERR << "CSVFormat::guessFormatFor(" << path
Chris@1524 65 << "): File could not be opened for reading" << endl;
Chris@1524 66 return false;
Chris@1524 67 }
Chris@1524 68 SVDEBUG << "CSVFormat::guessFormatFor(" << path << ")" << endl;
Chris@392 69
Chris@392 70 QTextStream in(&file);
Chris@392 71 in.seek(0);
Chris@392 72
Chris@629 73 int lineno = 0;
Chris@392 74
Chris@392 75 while (!in.atEnd()) {
Chris@392 76
Chris@392 77 // See comment about line endings in CSVFileReader::load()
Chris@392 78
Chris@392 79 QString chunk = in.readLine();
Chris@392 80 QStringList lines = chunk.split('\r', QString::SkipEmptyParts);
Chris@392 81
Chris@897 82 for (int li = 0; li < lines.size(); ++li) {
Chris@392 83
Chris@392 84 QString line = lines[li];
Chris@1512 85 if (line.startsWith("#") || line == "") {
Chris@1512 86 continue;
Chris@1512 87 }
Chris@392 88
Chris@629 89 guessQualities(line, lineno);
Chris@392 90
Chris@840 91 ++lineno;
Chris@629 92 }
Chris@840 93
Chris@1512 94 if (lineno >= 150) break;
Chris@629 95 }
Chris@392 96
Chris@629 97 guessPurposes();
Chris@1515 98 guessAudioSampleRange();
Chris@1524 99
Chris@1524 100 return true;
Chris@629 101 }
Chris@629 102
Chris@629 103 void
Chris@629 104 CSVFormat::guessSeparator(QString line)
Chris@629 105 {
Chris@1524 106 QString candidates = "\t|,/: ";
Chris@1524 107
Chris@1524 108 for (int i = 0; i < candidates.length(); ++i) {
Chris@1524 109 auto bits = StringBits::split(line, candidates[i], m_allowQuoting);
Chris@1524 110 if (bits.size() >= 2) {
Chris@1585 111 m_plausibleSeparators.insert(candidates[i]);
Chris@1585 112 if (m_separator == "") {
Chris@1585 113 m_separator = candidates[i];
Chris@1585 114 SVDEBUG << "Estimated column separator: '" << m_separator
Chris@1585 115 << "'" << endl;
Chris@1524 116 }
Chris@629 117 }
Chris@629 118 }
Chris@629 119 }
Chris@629 120
Chris@629 121 void
Chris@629 122 CSVFormat::guessQualities(QString line, int lineno)
Chris@629 123 {
Chris@1585 124 guessSeparator(line);
Chris@629 125
Chris@1362 126 QStringList list = StringBits::split(line, getSeparator(), m_allowQuoting);
Chris@629 127
Chris@629 128 int cols = list.size();
Chris@1870 129
Chris@1870 130 int firstLine = 0;
Chris@1870 131 if (m_headerStatus == HeaderPresent) {
Chris@1870 132 firstLine = 1;
Chris@1870 133 }
Chris@1870 134
Chris@1870 135 if (lineno == firstLine || (cols > m_columnCount)) {
Chris@1870 136 m_columnCount = cols;
Chris@1870 137 }
Chris@1870 138 if (cols != m_columnCount) {
Chris@1870 139 m_variableColumnCount = true;
Chris@1870 140 }
Chris@629 141
Chris@629 142 // All columns are regarded as having these qualities until we see
Chris@629 143 // something that indicates otherwise:
Chris@629 144
Chris@629 145 ColumnQualities defaultQualities =
Chris@1512 146 ColumnNumeric | ColumnIntegral | ColumnSmall |
Chris@1512 147 ColumnIncreasing | ColumnNearEmpty;
Chris@629 148
Chris@629 149 for (int i = 0; i < cols; ++i) {
Chris@1854 150
Chris@1854 151 SVDEBUG << "line no " << lineno << ": column " << i << " contains: \"" << list[i] << "\"" << endl;
Chris@1870 152
Chris@1870 153 if (m_columnQualities.find(i) == m_columnQualities.end()) {
Chris@1870 154 m_columnQualities[i] = defaultQualities;
Chris@1870 155 m_prevValues[i] = 0.f;
Chris@629 156 }
Chris@629 157
Chris@629 158 QString s(list[i]);
Chris@629 159 bool ok = false;
Chris@629 160
Chris@629 161 ColumnQualities qualities = m_columnQualities[i];
Chris@629 162
Chris@1523 163 // Looks like this is defined on Windows
Chris@1523 164 #undef small
Chris@1523 165
Chris@629 166 bool numeric = (qualities & ColumnNumeric);
Chris@629 167 bool integral = (qualities & ColumnIntegral);
Chris@629 168 bool increasing = (qualities & ColumnIncreasing);
Chris@1512 169 bool small = (qualities & ColumnSmall);
Chris@629 170 bool large = (qualities & ColumnLarge); // this one defaults to off
Chris@1512 171 bool signd = (qualities & ColumnSigned); // also defaults to off
Chris@1021 172 bool emptyish = (qualities & ColumnNearEmpty);
Chris@629 173
Chris@1854 174 if (s.trimmed() != "") {
Chris@1021 175
Chris@1870 176 if (lineno > firstLine) {
Chris@1854 177 emptyish = false;
Chris@1854 178 }
Chris@1854 179
Chris@1854 180 float value = 0.f;
Chris@629 181
Chris@1854 182 if (numeric) {
Chris@1854 183 value = s.toFloat(&ok);
Chris@1854 184 if (!ok) {
Chris@1854 185 value = (float)StringBits::stringToDoubleLocaleFree(s, &ok);
Chris@1512 186 }
Chris@1854 187 if (ok) {
Chris@1870 188 if (lineno < firstLine + 2 && value > 1000.f) {
Chris@1854 189 large = true;
Chris@1854 190 }
Chris@1854 191 if (value < 0.f) {
Chris@1854 192 signd = true;
Chris@1854 193 }
Chris@1854 194 if (value < -1.f || value > 1.f) {
Chris@1854 195 small = false;
Chris@1854 196 }
Chris@1854 197 } else {
Chris@1854 198 numeric = false;
Chris@1854 199
Chris@1854 200 // If the column is not numeric, it can't be any of
Chris@1854 201 // these things either
Chris@1854 202 integral = false;
Chris@1854 203 increasing = false;
Chris@1512 204 small = false;
Chris@1854 205 large = false;
Chris@1854 206 signd = false;
Chris@392 207 }
Chris@392 208 }
Chris@392 209
Chris@1854 210 if (numeric) {
Chris@1854 211
Chris@1854 212 if (integral) {
Chris@1854 213 if (s.contains('.') || s.contains(',')) {
Chris@1854 214 integral = false;
Chris@1854 215 }
Chris@392 216 }
Chris@1854 217
Chris@1854 218 if (increasing) {
Chris@1870 219 if (lineno > firstLine && value <= m_prevValues[i]) {
Chris@1854 220 increasing = false;
Chris@1854 221 }
Chris@1854 222 }
Chris@1854 223
Chris@1854 224 m_prevValues[i] = value;
Chris@392 225 }
Chris@629 226 }
Chris@1524 227
Chris@629 228 m_columnQualities[i] =
Chris@629 229 (numeric ? ColumnNumeric : 0) |
Chris@629 230 (integral ? ColumnIntegral : 0) |
Chris@629 231 (increasing ? ColumnIncreasing : 0) |
Chris@1512 232 (small ? ColumnSmall : 0) |
Chris@1021 233 (large ? ColumnLarge : 0) |
Chris@1512 234 (signd ? ColumnSigned : 0) |
Chris@1021 235 (emptyish ? ColumnNearEmpty : 0);
Chris@629 236 }
Chris@392 237
Chris@1870 238 if (lineno == 0 && m_headerStatus == HeaderUnknown) {
Chris@1870 239 // If we have at least one column, and every column has
Chris@1870 240 // quality == ColumnNearEmpty, i.e. not empty and not numeric,
Chris@1870 241 // then we probably have a header row
Chris@1870 242 bool couldBeHeader = (cols > 0);
Chris@1870 243 std::map<int, QString> headings;
Chris@1870 244 for (int i = 0; i < cols; ++i) {
Chris@1870 245 if (m_columnQualities[i] != ColumnNearEmpty) {
Chris@1870 246 couldBeHeader = false;
Chris@1870 247 } else {
Chris@1870 248 headings[i] = list[i].trimmed().toLower();
Chris@1870 249 }
Chris@1870 250 }
Chris@1870 251 if (couldBeHeader) {
Chris@1870 252 m_headerStatus = HeaderPresent;
Chris@1870 253 m_columnHeadings = headings;
Chris@1870 254 } else {
Chris@1870 255 m_headerStatus = HeaderAbsent;
Chris@1870 256 }
Chris@1870 257 }
Chris@1870 258
Chris@1870 259 if (lineno == 0 && m_headerStatus == HeaderPresent) {
Chris@1870 260 // Start again with the qualities:
Chris@1870 261 m_columnQualities.clear();
Chris@1870 262 m_prevValues.clear();
Chris@1871 263 }
Chris@1871 264
Chris@1871 265 if (lineno < firstLine + 10) {
Chris@629 266 m_example.push_back(list);
Chris@1871 267 if (lineno == 0 || cols > m_maxExampleCols) {
Chris@629 268 m_maxExampleCols = cols;
Chris@392 269 }
Chris@392 270 }
Chris@392 271
Chris@1870 272 if (lineno < firstLine + 10) {
Chris@1362 273 SVDEBUG << "Estimated column qualities for line " << lineno << " (reporting up to first 10): ";
Chris@1870 274 if (lineno == 0 && m_headerStatus == HeaderPresent &&
Chris@1870 275 m_columnCount > 0 && m_columnQualities.empty()) {
Chris@1870 276 SVDEBUG << "[whole line classified as a header row]";
Chris@1870 277 } else {
Chris@1870 278 for (int i = 0; i < cols; ++i) {
Chris@1870 279 if (m_columnQualities.find(i) == m_columnQualities.end()) {
Chris@1870 280 SVDEBUG << "(not set) ";
Chris@1870 281 } else {
Chris@1870 282 SVDEBUG << int(m_columnQualities[i]) << " ";
Chris@1870 283 }
Chris@1870 284 }
Chris@1362 285 }
Chris@1362 286 SVDEBUG << endl;
Chris@1870 287 SVDEBUG << "Estimated header status: " << m_headerStatus << endl;
Chris@1362 288 }
Chris@629 289 }
Chris@629 290
Chris@629 291 void
Chris@629 292 CSVFormat::guessPurposes()
Chris@629 293 {
Chris@629 294 m_timingType = CSVFormat::ImplicitTiming;
Chris@629 295 m_timeUnits = CSVFormat::TimeWindows;
Chris@1429 296
Chris@629 297 int timingColumnCount = 0;
Chris@1525 298 bool haveDurationOrEndTime = false;
Chris@1021 299
Chris@1510 300 SVDEBUG << "Estimated column qualities overall: ";
Chris@1510 301 for (int i = 0; i < m_columnCount; ++i) {
Chris@1870 302 if (m_columnQualities.find(i) == m_columnQualities.end()) {
Chris@1870 303 SVDEBUG << "(not set) ";
Chris@1870 304 } else {
Chris@1870 305 SVDEBUG << int(m_columnQualities[i]) << " ";
Chris@1870 306 }
Chris@1510 307 }
Chris@1510 308 SVDEBUG << endl;
Chris@1510 309
Chris@1021 310 // if our first column has zero or one entries in it and the rest
Chris@1021 311 // have more, then we'll default to ignoring the first column and
Chris@1021 312 // counting the next one as primary. (e.g. Sonic Annotator output
Chris@1021 313 // with filename at start of first column.)
Chris@1021 314
Chris@1021 315 int primaryColumnNo = 0;
Chris@1021 316
Chris@1021 317 if (m_columnCount >= 2) {
Chris@1021 318 if ( (m_columnQualities[0] & ColumnNearEmpty) &&
Chris@1021 319 !(m_columnQualities[1] & ColumnNearEmpty)) {
Chris@1021 320 primaryColumnNo = 1;
Chris@1021 321 }
Chris@1021 322 }
Chris@629 323
Chris@629 324 for (int i = 0; i < m_columnCount; ++i) {
Chris@629 325
Chris@629 326 ColumnPurpose purpose = ColumnUnknown;
Chris@1021 327
Chris@1021 328 if (i < primaryColumnNo) {
Chris@1021 329 setColumnPurpose(i, purpose);
Chris@1021 330 continue;
Chris@1021 331 }
Chris@1021 332
Chris@1021 333 bool primary = (i == primaryColumnNo);
Chris@392 334
Chris@629 335 ColumnQualities qualities = m_columnQualities[i];
Chris@392 336
Chris@629 337 bool numeric = (qualities & ColumnNumeric);
Chris@629 338 bool integral = (qualities & ColumnIntegral);
Chris@629 339 bool increasing = (qualities & ColumnIncreasing);
Chris@629 340 bool large = (qualities & ColumnLarge);
Chris@629 341
Chris@629 342 bool timingColumn = (numeric && increasing);
Chris@629 343
Chris@1870 344 QString heading;
Chris@1870 345 if (m_columnHeadings.find(i) != m_columnHeadings.end()) {
Chris@1870 346 heading = m_columnHeadings[i];
Chris@1870 347 }
Chris@1870 348
Chris@1870 349 if (heading == "time" || heading == "frame" ||
Chris@1870 350 heading == "duration" || heading == "endtime") {
Chris@1870 351 timingColumn = true;
Chris@1870 352 }
Chris@1870 353
Chris@1870 354 if (heading == "value" || heading == "height" || heading == "label") {
Chris@1870 355 timingColumn = false;
Chris@1870 356 }
Chris@1870 357
Chris@629 358 if (timingColumn) {
Chris@629 359
Chris@629 360 ++timingColumnCount;
Chris@1870 361
Chris@1870 362 if (heading == "endtime") {
Chris@1870 363
Chris@1870 364 purpose = ColumnEndTime;
Chris@1870 365 haveDurationOrEndTime = true;
Chris@1870 366
Chris@1870 367 } else if (heading == "duration") {
Chris@1870 368
Chris@1870 369 purpose = ColumnDuration;
Chris@1870 370 haveDurationOrEndTime = true;
Chris@629 371
Chris@1870 372 } else if (primary || heading == "time" || heading == "frame") {
Chris@629 373
Chris@629 374 purpose = ColumnStartTime;
Chris@629 375 m_timingType = ExplicitTiming;
Chris@629 376
Chris@1870 377 if ((integral && large) || heading == "frame") {
Chris@629 378 m_timeUnits = TimeAudioFrames;
Chris@629 379 } else {
Chris@629 380 m_timeUnits = TimeSeconds;
Chris@629 381 }
Chris@629 382
Chris@1870 383 } else if (timingColumnCount == 2 &&
Chris@1870 384 m_timingType == ExplicitTiming) {
Chris@1870 385 purpose = ColumnEndTime;
Chris@1870 386 haveDurationOrEndTime = true;
Chris@629 387 }
Chris@629 388 }
Chris@629 389
Chris@629 390 if (purpose == ColumnUnknown) {
Chris@1870 391 if (heading == "label") {
Chris@1870 392 purpose = ColumnLabel;
Chris@1870 393 } else if (numeric || heading == "value" || heading == "height") {
Chris@629 394 purpose = ColumnValue;
Chris@629 395 } else {
Chris@629 396 purpose = ColumnLabel;
Chris@629 397 }
Chris@629 398 }
Chris@629 399
Chris@631 400 setColumnPurpose(i, purpose);
Chris@629 401 }
Chris@629 402
Chris@629 403 int valueCount = 0;
Chris@629 404 for (int i = 0; i < m_columnCount; ++i) {
Chris@1870 405 if (m_columnPurposes[i] == ColumnValue) {
Chris@1870 406 ++valueCount;
Chris@1870 407 }
Chris@629 408 }
Chris@629 409
Chris@630 410 if (valueCount == 2 && timingColumnCount == 1) {
Chris@630 411 // If we have exactly two apparent value columns and only one
Chris@630 412 // timing column, but one value column is integral and the
Chris@630 413 // other is not, guess that whichever one matches the integral
Chris@630 414 // status of the time column is either duration or end time
Chris@630 415 if (m_timingType == ExplicitTiming) {
Chris@630 416 int a = -1, b = -1;
Chris@630 417 for (int i = 0; i < m_columnCount; ++i) {
Chris@630 418 if (m_columnPurposes[i] == ColumnValue) {
Chris@630 419 if (a == -1) a = i;
Chris@630 420 else b = i;
Chris@630 421 }
Chris@630 422 }
Chris@630 423 if ((m_columnQualities[a] & ColumnIntegral) !=
Chris@630 424 (m_columnQualities[b] & ColumnIntegral)) {
Chris@630 425 int timecol = a;
Chris@630 426 if ((m_columnQualities[a] & ColumnIntegral) !=
Chris@630 427 (m_columnQualities[0] & ColumnIntegral)) {
Chris@630 428 timecol = b;
Chris@630 429 }
Chris@630 430 if (m_columnQualities[timecol] & ColumnIncreasing) {
Chris@630 431 // This shouldn't happen; should have been settled above
Chris@630 432 m_columnPurposes[timecol] = ColumnEndTime;
Chris@1525 433 haveDurationOrEndTime = true;
Chris@630 434 } else {
Chris@630 435 m_columnPurposes[timecol] = ColumnDuration;
Chris@1525 436 haveDurationOrEndTime = true;
Chris@630 437 }
Chris@630 438 --valueCount;
Chris@630 439 }
Chris@630 440 }
Chris@630 441 }
Chris@630 442
Chris@1525 443 if (timingColumnCount > 1 || haveDurationOrEndTime) {
Chris@631 444 m_modelType = TwoDimensionalModelWithDuration;
Chris@392 445 } else {
Chris@631 446 if (valueCount == 0) {
Chris@631 447 m_modelType = OneDimensionalModel;
Chris@631 448 } else if (valueCount == 1) {
Chris@631 449 m_modelType = TwoDimensionalModel;
Chris@631 450 } else {
Chris@631 451 m_modelType = ThreeDimensionalModel;
Chris@631 452 }
Chris@629 453 }
Chris@392 454
Chris@1362 455 SVDEBUG << "Estimated column purposes: ";
Chris@1362 456 for (int i = 0; i < m_columnCount; ++i) {
Chris@1362 457 SVDEBUG << int(m_columnPurposes[i]) << " ";
Chris@1362 458 }
Chris@1362 459 SVDEBUG << endl;
Chris@392 460
Chris@1362 461 SVDEBUG << "Estimated model type: " << m_modelType << endl;
Chris@1362 462 SVDEBUG << "Estimated timing type: " << m_timingType << endl;
Chris@1362 463 SVDEBUG << "Estimated units: " << m_timeUnits << endl;
Chris@392 464 }
Chris@392 465
Chris@1515 466 void
Chris@1515 467 CSVFormat::guessAudioSampleRange()
Chris@1515 468 {
Chris@1515 469 AudioSampleRange range = SampleRangeSigned1;
Chris@1515 470
Chris@1515 471 range = SampleRangeSigned1;
Chris@1515 472 bool knownSigned = false;
Chris@1515 473 bool knownNonIntegral = false;
Chris@1521 474
Chris@1521 475 SVDEBUG << "CSVFormat::guessAudioSampleRange: starting with assumption of "
Chris@1521 476 << range << endl;
Chris@1515 477
Chris@1515 478 for (int i = 0; i < m_columnCount; ++i) {
Chris@1521 479 if (m_columnPurposes[i] != ColumnValue) {
Chris@1521 480 SVDEBUG << "... column " << i
Chris@1521 481 << " is not apparently a value, ignoring" << endl;
Chris@1521 482 continue;
Chris@1521 483 }
Chris@1515 484 if (!(m_columnQualities[i] & ColumnIntegral)) {
Chris@1515 485 knownNonIntegral = true;
Chris@1515 486 if (range == SampleRangeUnsigned255 ||
Chris@1515 487 range == SampleRangeSigned32767) {
Chris@1515 488 range = SampleRangeOther;
Chris@1515 489 }
Chris@1521 490 SVDEBUG << "... column " << i
Chris@1521 491 << " is non-integral, updating range to " << range << endl;
Chris@1515 492 }
Chris@1515 493 if (m_columnQualities[i] & ColumnLarge) {
Chris@1515 494 if (range == SampleRangeSigned1 ||
Chris@1515 495 range == SampleRangeUnsigned255) {
Chris@1515 496 if (knownNonIntegral) {
Chris@1515 497 range = SampleRangeOther;
Chris@1515 498 } else {
Chris@1515 499 range = SampleRangeSigned32767;
Chris@1515 500 }
Chris@1515 501 }
Chris@1521 502 SVDEBUG << "... column " << i << " is large, updating range to "
Chris@1521 503 << range << endl;
Chris@1515 504 }
Chris@1515 505 if (m_columnQualities[i] & ColumnSigned) {
Chris@1515 506 knownSigned = true;
Chris@1515 507 if (range == SampleRangeUnsigned255) {
Chris@1515 508 range = SampleRangeSigned32767;
Chris@1515 509 }
Chris@1521 510 SVDEBUG << "... column " << i << " is signed, updating range to "
Chris@1521 511 << range << endl;
Chris@1515 512 }
Chris@1515 513 if (!(m_columnQualities[i] & ColumnSmall)) {
Chris@1515 514 if (range == SampleRangeSigned1) {
Chris@1515 515 if (knownNonIntegral) {
Chris@1515 516 range = SampleRangeOther;
Chris@1515 517 } else if (knownSigned) {
Chris@1515 518 range = SampleRangeSigned32767;
Chris@1515 519 } else {
Chris@1515 520 range = SampleRangeUnsigned255;
Chris@1515 521 }
Chris@1515 522 }
Chris@1521 523 SVDEBUG << "... column " << i << " is not small, updating range to "
Chris@1521 524 << range << endl;
Chris@1515 525 }
Chris@1515 526 }
Chris@1515 527
Chris@1521 528 SVDEBUG << "CSVFormat::guessAudioSampleRange: ended up with range "
Chris@1521 529 << range << endl;
Chris@1521 530
Chris@1515 531 m_audioSampleRange = range;
Chris@1515 532 }
Chris@1515 533
Chris@1870 534 QList<CSVFormat::ColumnPurpose>
Chris@1870 535 CSVFormat::getColumnPurposes() const
Chris@631 536 {
Chris@1870 537 QList<ColumnPurpose> purposes;
Chris@1870 538 for (int i = 0; i < m_columnCount; ++i) {
Chris@1870 539 purposes.push_back(getColumnPurpose(i));
Chris@631 540 }
Chris@1870 541 return purposes;
Chris@1870 542 }
Chris@1870 543
Chris@1870 544 void
Chris@1870 545 CSVFormat::setColumnPurposes(QList<ColumnPurpose> cl)
Chris@1870 546 {
Chris@1870 547 m_columnPurposes.clear();
Chris@1870 548 for (int i = 0; in_range_for(cl, i); ++i) {
Chris@1870 549 m_columnPurposes[i] = cl[i];
Chris@1870 550 }
Chris@631 551 }
Chris@629 552
Chris@631 553 CSVFormat::ColumnPurpose
Chris@631 554 CSVFormat::getColumnPurpose(int i) const
Chris@631 555 {
Chris@1870 556 if (m_columnPurposes.find(i) == m_columnPurposes.end()) {
Chris@668 557 return ColumnUnknown;
Chris@1870 558 } else {
Chris@1870 559 return m_columnPurposes.at(i);
Chris@668 560 }
Chris@631 561 }
Chris@631 562
Chris@631 563 void
Chris@631 564 CSVFormat::setColumnPurpose(int i, ColumnPurpose p)
Chris@631 565 {
Chris@631 566 m_columnPurposes[i] = p;
Chris@631 567 }
Chris@631 568
Chris@1870 569 QList<CSVFormat::ColumnQualities>
Chris@1870 570 CSVFormat::getColumnQualities() const
Chris@1870 571 {
Chris@1870 572 QList<ColumnQualities> qualities;
Chris@1870 573 for (int i = 0; i < m_columnCount; ++i) {
Chris@1870 574 if (m_columnQualities.find(i) == m_columnQualities.end()) {
Chris@1870 575 qualities.push_back(0);
Chris@1870 576 } else {
Chris@1870 577 qualities.push_back(m_columnQualities.at(i));
Chris@1870 578 }
Chris@1870 579 }
Chris@1870 580 return qualities;
Chris@1870 581 }