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@1870
|
263 } else if (lineno < firstLine + 10) {
|
Chris@1870
|
264 // Not a header row, so add it to the example column output
|
Chris@629
|
265 m_example.push_back(list);
|
Chris@1870
|
266 if (lineno == firstLine || cols > m_maxExampleCols) {
|
Chris@629
|
267 m_maxExampleCols = cols;
|
Chris@392
|
268 }
|
Chris@392
|
269 }
|
Chris@392
|
270
|
Chris@1870
|
271 if (lineno < firstLine + 10) {
|
Chris@1362
|
272 SVDEBUG << "Estimated column qualities for line " << lineno << " (reporting up to first 10): ";
|
Chris@1870
|
273 if (lineno == 0 && m_headerStatus == HeaderPresent &&
|
Chris@1870
|
274 m_columnCount > 0 && m_columnQualities.empty()) {
|
Chris@1870
|
275 SVDEBUG << "[whole line classified as a header row]";
|
Chris@1870
|
276 } else {
|
Chris@1870
|
277 for (int i = 0; i < cols; ++i) {
|
Chris@1870
|
278 if (m_columnQualities.find(i) == m_columnQualities.end()) {
|
Chris@1870
|
279 SVDEBUG << "(not set) ";
|
Chris@1870
|
280 } else {
|
Chris@1870
|
281 SVDEBUG << int(m_columnQualities[i]) << " ";
|
Chris@1870
|
282 }
|
Chris@1870
|
283 }
|
Chris@1362
|
284 }
|
Chris@1362
|
285 SVDEBUG << endl;
|
Chris@1870
|
286 SVDEBUG << "Estimated header status: " << m_headerStatus << endl;
|
Chris@1362
|
287 }
|
Chris@629
|
288 }
|
Chris@629
|
289
|
Chris@629
|
290 void
|
Chris@629
|
291 CSVFormat::guessPurposes()
|
Chris@629
|
292 {
|
Chris@629
|
293 m_timingType = CSVFormat::ImplicitTiming;
|
Chris@629
|
294 m_timeUnits = CSVFormat::TimeWindows;
|
Chris@1429
|
295
|
Chris@629
|
296 int timingColumnCount = 0;
|
Chris@1525
|
297 bool haveDurationOrEndTime = false;
|
Chris@1021
|
298
|
Chris@1510
|
299 SVDEBUG << "Estimated column qualities overall: ";
|
Chris@1510
|
300 for (int i = 0; i < m_columnCount; ++i) {
|
Chris@1870
|
301 if (m_columnQualities.find(i) == m_columnQualities.end()) {
|
Chris@1870
|
302 SVDEBUG << "(not set) ";
|
Chris@1870
|
303 } else {
|
Chris@1870
|
304 SVDEBUG << int(m_columnQualities[i]) << " ";
|
Chris@1870
|
305 }
|
Chris@1510
|
306 }
|
Chris@1510
|
307 SVDEBUG << endl;
|
Chris@1510
|
308
|
Chris@1021
|
309 // if our first column has zero or one entries in it and the rest
|
Chris@1021
|
310 // have more, then we'll default to ignoring the first column and
|
Chris@1021
|
311 // counting the next one as primary. (e.g. Sonic Annotator output
|
Chris@1021
|
312 // with filename at start of first column.)
|
Chris@1021
|
313
|
Chris@1021
|
314 int primaryColumnNo = 0;
|
Chris@1021
|
315
|
Chris@1021
|
316 if (m_columnCount >= 2) {
|
Chris@1021
|
317 if ( (m_columnQualities[0] & ColumnNearEmpty) &&
|
Chris@1021
|
318 !(m_columnQualities[1] & ColumnNearEmpty)) {
|
Chris@1021
|
319 primaryColumnNo = 1;
|
Chris@1021
|
320 }
|
Chris@1021
|
321 }
|
Chris@629
|
322
|
Chris@629
|
323 for (int i = 0; i < m_columnCount; ++i) {
|
Chris@629
|
324
|
Chris@629
|
325 ColumnPurpose purpose = ColumnUnknown;
|
Chris@1021
|
326
|
Chris@1021
|
327 if (i < primaryColumnNo) {
|
Chris@1021
|
328 setColumnPurpose(i, purpose);
|
Chris@1021
|
329 continue;
|
Chris@1021
|
330 }
|
Chris@1021
|
331
|
Chris@1021
|
332 bool primary = (i == primaryColumnNo);
|
Chris@392
|
333
|
Chris@629
|
334 ColumnQualities qualities = m_columnQualities[i];
|
Chris@392
|
335
|
Chris@629
|
336 bool numeric = (qualities & ColumnNumeric);
|
Chris@629
|
337 bool integral = (qualities & ColumnIntegral);
|
Chris@629
|
338 bool increasing = (qualities & ColumnIncreasing);
|
Chris@629
|
339 bool large = (qualities & ColumnLarge);
|
Chris@629
|
340
|
Chris@629
|
341 bool timingColumn = (numeric && increasing);
|
Chris@629
|
342
|
Chris@1870
|
343 QString heading;
|
Chris@1870
|
344 if (m_columnHeadings.find(i) != m_columnHeadings.end()) {
|
Chris@1870
|
345 heading = m_columnHeadings[i];
|
Chris@1870
|
346 }
|
Chris@1870
|
347
|
Chris@1870
|
348 if (heading == "time" || heading == "frame" ||
|
Chris@1870
|
349 heading == "duration" || heading == "endtime") {
|
Chris@1870
|
350 timingColumn = true;
|
Chris@1870
|
351 }
|
Chris@1870
|
352
|
Chris@1870
|
353 if (heading == "value" || heading == "height" || heading == "label") {
|
Chris@1870
|
354 timingColumn = false;
|
Chris@1870
|
355 }
|
Chris@1870
|
356
|
Chris@629
|
357 if (timingColumn) {
|
Chris@629
|
358
|
Chris@629
|
359 ++timingColumnCount;
|
Chris@1870
|
360
|
Chris@1870
|
361 if (heading == "endtime") {
|
Chris@1870
|
362
|
Chris@1870
|
363 purpose = ColumnEndTime;
|
Chris@1870
|
364 haveDurationOrEndTime = true;
|
Chris@1870
|
365
|
Chris@1870
|
366 } else if (heading == "duration") {
|
Chris@1870
|
367
|
Chris@1870
|
368 purpose = ColumnDuration;
|
Chris@1870
|
369 haveDurationOrEndTime = true;
|
Chris@629
|
370
|
Chris@1870
|
371 } else if (primary || heading == "time" || heading == "frame") {
|
Chris@629
|
372
|
Chris@629
|
373 purpose = ColumnStartTime;
|
Chris@629
|
374 m_timingType = ExplicitTiming;
|
Chris@629
|
375
|
Chris@1870
|
376 if ((integral && large) || heading == "frame") {
|
Chris@629
|
377 m_timeUnits = TimeAudioFrames;
|
Chris@629
|
378 } else {
|
Chris@629
|
379 m_timeUnits = TimeSeconds;
|
Chris@629
|
380 }
|
Chris@629
|
381
|
Chris@1870
|
382 } else if (timingColumnCount == 2 &&
|
Chris@1870
|
383 m_timingType == ExplicitTiming) {
|
Chris@1870
|
384 purpose = ColumnEndTime;
|
Chris@1870
|
385 haveDurationOrEndTime = true;
|
Chris@629
|
386 }
|
Chris@629
|
387 }
|
Chris@629
|
388
|
Chris@629
|
389 if (purpose == ColumnUnknown) {
|
Chris@1870
|
390 if (heading == "label") {
|
Chris@1870
|
391 purpose = ColumnLabel;
|
Chris@1870
|
392 } else if (numeric || heading == "value" || heading == "height") {
|
Chris@629
|
393 purpose = ColumnValue;
|
Chris@629
|
394 } else {
|
Chris@629
|
395 purpose = ColumnLabel;
|
Chris@629
|
396 }
|
Chris@629
|
397 }
|
Chris@629
|
398
|
Chris@631
|
399 setColumnPurpose(i, purpose);
|
Chris@629
|
400 }
|
Chris@629
|
401
|
Chris@629
|
402 int valueCount = 0;
|
Chris@629
|
403 for (int i = 0; i < m_columnCount; ++i) {
|
Chris@1870
|
404 if (m_columnPurposes[i] == ColumnValue) {
|
Chris@1870
|
405 ++valueCount;
|
Chris@1870
|
406 }
|
Chris@629
|
407 }
|
Chris@629
|
408
|
Chris@630
|
409 if (valueCount == 2 && timingColumnCount == 1) {
|
Chris@630
|
410 // If we have exactly two apparent value columns and only one
|
Chris@630
|
411 // timing column, but one value column is integral and the
|
Chris@630
|
412 // other is not, guess that whichever one matches the integral
|
Chris@630
|
413 // status of the time column is either duration or end time
|
Chris@630
|
414 if (m_timingType == ExplicitTiming) {
|
Chris@630
|
415 int a = -1, b = -1;
|
Chris@630
|
416 for (int i = 0; i < m_columnCount; ++i) {
|
Chris@630
|
417 if (m_columnPurposes[i] == ColumnValue) {
|
Chris@630
|
418 if (a == -1) a = i;
|
Chris@630
|
419 else b = i;
|
Chris@630
|
420 }
|
Chris@630
|
421 }
|
Chris@630
|
422 if ((m_columnQualities[a] & ColumnIntegral) !=
|
Chris@630
|
423 (m_columnQualities[b] & ColumnIntegral)) {
|
Chris@630
|
424 int timecol = a;
|
Chris@630
|
425 if ((m_columnQualities[a] & ColumnIntegral) !=
|
Chris@630
|
426 (m_columnQualities[0] & ColumnIntegral)) {
|
Chris@630
|
427 timecol = b;
|
Chris@630
|
428 }
|
Chris@630
|
429 if (m_columnQualities[timecol] & ColumnIncreasing) {
|
Chris@630
|
430 // This shouldn't happen; should have been settled above
|
Chris@630
|
431 m_columnPurposes[timecol] = ColumnEndTime;
|
Chris@1525
|
432 haveDurationOrEndTime = true;
|
Chris@630
|
433 } else {
|
Chris@630
|
434 m_columnPurposes[timecol] = ColumnDuration;
|
Chris@1525
|
435 haveDurationOrEndTime = true;
|
Chris@630
|
436 }
|
Chris@630
|
437 --valueCount;
|
Chris@630
|
438 }
|
Chris@630
|
439 }
|
Chris@630
|
440 }
|
Chris@630
|
441
|
Chris@1525
|
442 if (timingColumnCount > 1 || haveDurationOrEndTime) {
|
Chris@631
|
443 m_modelType = TwoDimensionalModelWithDuration;
|
Chris@392
|
444 } else {
|
Chris@631
|
445 if (valueCount == 0) {
|
Chris@631
|
446 m_modelType = OneDimensionalModel;
|
Chris@631
|
447 } else if (valueCount == 1) {
|
Chris@631
|
448 m_modelType = TwoDimensionalModel;
|
Chris@631
|
449 } else {
|
Chris@631
|
450 m_modelType = ThreeDimensionalModel;
|
Chris@631
|
451 }
|
Chris@629
|
452 }
|
Chris@392
|
453
|
Chris@1362
|
454 SVDEBUG << "Estimated column purposes: ";
|
Chris@1362
|
455 for (int i = 0; i < m_columnCount; ++i) {
|
Chris@1362
|
456 SVDEBUG << int(m_columnPurposes[i]) << " ";
|
Chris@1362
|
457 }
|
Chris@1362
|
458 SVDEBUG << endl;
|
Chris@392
|
459
|
Chris@1362
|
460 SVDEBUG << "Estimated model type: " << m_modelType << endl;
|
Chris@1362
|
461 SVDEBUG << "Estimated timing type: " << m_timingType << endl;
|
Chris@1362
|
462 SVDEBUG << "Estimated units: " << m_timeUnits << endl;
|
Chris@392
|
463 }
|
Chris@392
|
464
|
Chris@1515
|
465 void
|
Chris@1515
|
466 CSVFormat::guessAudioSampleRange()
|
Chris@1515
|
467 {
|
Chris@1515
|
468 AudioSampleRange range = SampleRangeSigned1;
|
Chris@1515
|
469
|
Chris@1515
|
470 range = SampleRangeSigned1;
|
Chris@1515
|
471 bool knownSigned = false;
|
Chris@1515
|
472 bool knownNonIntegral = false;
|
Chris@1521
|
473
|
Chris@1521
|
474 SVDEBUG << "CSVFormat::guessAudioSampleRange: starting with assumption of "
|
Chris@1521
|
475 << range << endl;
|
Chris@1515
|
476
|
Chris@1515
|
477 for (int i = 0; i < m_columnCount; ++i) {
|
Chris@1521
|
478 if (m_columnPurposes[i] != ColumnValue) {
|
Chris@1521
|
479 SVDEBUG << "... column " << i
|
Chris@1521
|
480 << " is not apparently a value, ignoring" << endl;
|
Chris@1521
|
481 continue;
|
Chris@1521
|
482 }
|
Chris@1515
|
483 if (!(m_columnQualities[i] & ColumnIntegral)) {
|
Chris@1515
|
484 knownNonIntegral = true;
|
Chris@1515
|
485 if (range == SampleRangeUnsigned255 ||
|
Chris@1515
|
486 range == SampleRangeSigned32767) {
|
Chris@1515
|
487 range = SampleRangeOther;
|
Chris@1515
|
488 }
|
Chris@1521
|
489 SVDEBUG << "... column " << i
|
Chris@1521
|
490 << " is non-integral, updating range to " << range << endl;
|
Chris@1515
|
491 }
|
Chris@1515
|
492 if (m_columnQualities[i] & ColumnLarge) {
|
Chris@1515
|
493 if (range == SampleRangeSigned1 ||
|
Chris@1515
|
494 range == SampleRangeUnsigned255) {
|
Chris@1515
|
495 if (knownNonIntegral) {
|
Chris@1515
|
496 range = SampleRangeOther;
|
Chris@1515
|
497 } else {
|
Chris@1515
|
498 range = SampleRangeSigned32767;
|
Chris@1515
|
499 }
|
Chris@1515
|
500 }
|
Chris@1521
|
501 SVDEBUG << "... column " << i << " is large, updating range to "
|
Chris@1521
|
502 << range << endl;
|
Chris@1515
|
503 }
|
Chris@1515
|
504 if (m_columnQualities[i] & ColumnSigned) {
|
Chris@1515
|
505 knownSigned = true;
|
Chris@1515
|
506 if (range == SampleRangeUnsigned255) {
|
Chris@1515
|
507 range = SampleRangeSigned32767;
|
Chris@1515
|
508 }
|
Chris@1521
|
509 SVDEBUG << "... column " << i << " is signed, updating range to "
|
Chris@1521
|
510 << range << endl;
|
Chris@1515
|
511 }
|
Chris@1515
|
512 if (!(m_columnQualities[i] & ColumnSmall)) {
|
Chris@1515
|
513 if (range == SampleRangeSigned1) {
|
Chris@1515
|
514 if (knownNonIntegral) {
|
Chris@1515
|
515 range = SampleRangeOther;
|
Chris@1515
|
516 } else if (knownSigned) {
|
Chris@1515
|
517 range = SampleRangeSigned32767;
|
Chris@1515
|
518 } else {
|
Chris@1515
|
519 range = SampleRangeUnsigned255;
|
Chris@1515
|
520 }
|
Chris@1515
|
521 }
|
Chris@1521
|
522 SVDEBUG << "... column " << i << " is not small, updating range to "
|
Chris@1521
|
523 << range << endl;
|
Chris@1515
|
524 }
|
Chris@1515
|
525 }
|
Chris@1515
|
526
|
Chris@1521
|
527 SVDEBUG << "CSVFormat::guessAudioSampleRange: ended up with range "
|
Chris@1521
|
528 << range << endl;
|
Chris@1521
|
529
|
Chris@1515
|
530 m_audioSampleRange = range;
|
Chris@1515
|
531 }
|
Chris@1515
|
532
|
Chris@1870
|
533 QList<CSVFormat::ColumnPurpose>
|
Chris@1870
|
534 CSVFormat::getColumnPurposes() const
|
Chris@631
|
535 {
|
Chris@1870
|
536 QList<ColumnPurpose> purposes;
|
Chris@1870
|
537 for (int i = 0; i < m_columnCount; ++i) {
|
Chris@1870
|
538 purposes.push_back(getColumnPurpose(i));
|
Chris@631
|
539 }
|
Chris@1870
|
540 return purposes;
|
Chris@1870
|
541 }
|
Chris@1870
|
542
|
Chris@1870
|
543 void
|
Chris@1870
|
544 CSVFormat::setColumnPurposes(QList<ColumnPurpose> cl)
|
Chris@1870
|
545 {
|
Chris@1870
|
546 m_columnPurposes.clear();
|
Chris@1870
|
547 for (int i = 0; in_range_for(cl, i); ++i) {
|
Chris@1870
|
548 m_columnPurposes[i] = cl[i];
|
Chris@1870
|
549 }
|
Chris@631
|
550 }
|
Chris@629
|
551
|
Chris@631
|
552 CSVFormat::ColumnPurpose
|
Chris@631
|
553 CSVFormat::getColumnPurpose(int i) const
|
Chris@631
|
554 {
|
Chris@1870
|
555 if (m_columnPurposes.find(i) == m_columnPurposes.end()) {
|
Chris@668
|
556 return ColumnUnknown;
|
Chris@1870
|
557 } else {
|
Chris@1870
|
558 return m_columnPurposes.at(i);
|
Chris@668
|
559 }
|
Chris@631
|
560 }
|
Chris@631
|
561
|
Chris@631
|
562 void
|
Chris@631
|
563 CSVFormat::setColumnPurpose(int i, ColumnPurpose p)
|
Chris@631
|
564 {
|
Chris@631
|
565 m_columnPurposes[i] = p;
|
Chris@631
|
566 }
|
Chris@631
|
567
|
Chris@1870
|
568 QList<CSVFormat::ColumnQualities>
|
Chris@1870
|
569 CSVFormat::getColumnQualities() const
|
Chris@1870
|
570 {
|
Chris@1870
|
571 QList<ColumnQualities> qualities;
|
Chris@1870
|
572 for (int i = 0; i < m_columnCount; ++i) {
|
Chris@1870
|
573 if (m_columnQualities.find(i) == m_columnQualities.end()) {
|
Chris@1870
|
574 qualities.push_back(0);
|
Chris@1870
|
575 } else {
|
Chris@1870
|
576 qualities.push_back(m_columnQualities.at(i));
|
Chris@1870
|
577 }
|
Chris@1870
|
578 }
|
Chris@1870
|
579 return qualities;
|
Chris@1870
|
580 }
|