annotate data/fileio/CSVFormat.cpp @ 1881:b504df98c3be

Ensure completion on output model is started at zero, so if it's checked before the input model has become ready and the transform has begun, it is not accidentally reported as complete (affected re-aligning models in Sonic Lineup when replacing the session)
author Chris Cannam
date Fri, 26 Jun 2020 11:45:39 +0100
parents bed42ce4d3ab
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 }