kwave  18.07.70
AsciiDecoder.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  AsciiDecoder.cpp - decoder for ASCII data
3  -------------------
4  begin : Sun Dec 03 2006
5  copyright : (C) 2006 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include "config.h"
18 
19 #include <ctype.h>
20 #include <string.h>
21 
22 #include <QDateTime>
23 #include <QLatin1Char>
24 #include <QLatin1String>
25 #include <QRegExp>
26 
27 #include <KLocalizedString>
28 
29 #include "libkwave/Label.h"
30 #include "libkwave/LabelList.h"
31 #include "libkwave/MessageBox.h"
32 #include "libkwave/MultiWriter.h"
33 #include "libkwave/Parser.h"
34 #include "libkwave/Sample.h"
35 #include "libkwave/String.h"
36 #include "libkwave/Writer.h"
37 
38 #include "AsciiCodecPlugin.h"
39 #include "AsciiDecoder.h"
40 
41 #define MAX_LINE_LEN 16384
43 //***************************************************************************
45  :Kwave::Decoder(),
46  m_source(),
47  m_dest(Q_NULLPTR),
48  m_queue_input(),
49  m_line_nr(0)
50 {
53  m_source.setCodec("UTF-8");
54 }
55 
56 //***************************************************************************
58 {
59  if (m_source.device()) close();
60 }
61 
62 //***************************************************************************
64 {
65  return new Kwave::AsciiDecoder();
66 }
67 
68 //***************************************************************************
69 bool Kwave::AsciiDecoder::open(QWidget *widget, QIODevice &src)
70 {
71  Q_UNUSED(widget);
72 
73  metaData().clear();
74  Q_ASSERT(!m_source.device());
75  if (m_source.device()) qWarning("AsciiDecoder::open(), already open !");
76 
77  // try to open the source
78  if (!src.open(QIODevice::ReadOnly)) {
79  qWarning("failed to open source !");
80  return false;
81  }
82 
83  // take over the source
84  m_source.setDevice(&src);
85 
86  Kwave::FileInfo info(metaData());
87  Kwave::LabelList labels;
88 
89  /********** Decoder setup ************/
90  qDebug("--- AsciiDecoder::open() ---");
91 
92  // read in all metadata until start of samples, EOF or user cancel
93  qDebug("AsciiDecoder::open(...)");
94 
95  m_line_nr = 0;
96  while (!m_source.atEnd()) {
97  QString line = m_source.readLine(MAX_LINE_LEN).simplified();
98  m_line_nr++;
99  if (!line.length())
100  continue; // skip empty line
101 
102  QRegExp regex(_(
103  "(^##\\s*)" // 1 start of meta data line
104  "([\\'\\\"])?" // 2 property, quote start (' or ")
105  "\\s*(\\w+[\\s\\w]*\\w)\\s*" // 3 property
106  "(\\[\\d*\\])?" // 4 index (optional)
107  "(\\2)" // 5 property, quote end
108  "(\\s*=\\s*)" // 6 assignment '='
109  "(.*)" // 7 rest, up to end of line
110  ));
111  if (regex.exactMatch(line)) {
112  // meta data entry: "## 'Name' = value"
113  QString name = Kwave::Parser::unescape(regex.cap(3) + regex.cap(4));
114  QString v = regex.cap(7);
115 
116  QString value;
117  if (v.length()) {
118  // remove quotes from the value
119  bool is_escaped = false;
120  char quote = v[0].toLatin1();
121  if ((quote != '\'') && (quote != '"'))
122  quote = -1;
123 
124  for (QString::ConstIterator it = v.begin(); it != v.end(); ++it)
125  {
126  const char c = QChar(*it).toLatin1();
127 
128  if ((c == '\\') && !is_escaped) {
129  is_escaped = true; // next char is escaped
130  continue;
131  }
132  if (is_escaped) {
133  value += *it; // escaped char
134  is_escaped = false;
135  continue;
136  }
137 
138  if (c == quote) {
139  if (!value.length())
140  continue; // starting quote
141  else
142  break; // ending quote
143  }
144 
145  if ((quote == -1) && (c == '#'))
146  break; // comment in unquoted text
147 
148  // otherwise: normal character, part of text
149  value += *it;
150  }
151 
152  // if the text was unquoted, remove leading/trailing spaces
153  if (quote == -1)
154  value = value.trimmed();
155  }
156 
157  // handle some well known aliases
158  if (name == _("rate")) name = info.name(INF_SAMPLE_RATE);
159  if (name == _("bits")) name = info.name(INF_BITS_PER_SAMPLE);
160 
161  // handle labels
162  QRegExp regex_label(_("label\\[(\\d*)\\]"));
163  if (regex_label.exactMatch(name)) {
164  bool ok = false;
165  sample_index_t pos = regex_label.cap(1).toULongLong(&ok);
166  if (!ok) {
167  qWarning("line %llu: malformed label position: '%s'",
168  m_line_nr, DBG(name));
169  continue; // skip it
170  }
171  Kwave::Label label(pos, value);
172  labels.append(label);
173  continue;
174  }
175 
176  bool found = false;
177  foreach (const Kwave::FileProperty &p, info.allKnownProperties()) {
178  if (info.name(p).toLower() == name.toLower()) {
179  found = true;
180  info.set(p, QVariant(value));
181  }
182  }
183  if (!found) {
184  qWarning("line %llu: unknown meta data entry: '%s' = '%s'",
185  m_line_nr, DBG(name), DBG(value));
186  }
187  } else if (line.startsWith(QLatin1Char('#'))) {
188  continue; // skip comment lines
189  } else {
190  // reached end of metadata:
191  // -> push back the line into the queue
192  m_queue_input.enqueue(line);
193  break;
194  }
195  }
196 
197  // if the number of channels is not known, but "tracks" is given and
198  // "track" is not present: old syntax has been used
199  if ((info.tracks() < 1) && info.contains(INF_TRACKS) &&
200  !info.contains(INF_TRACK))
201  {
202  info.set(INF_CHANNELS, info.get(INF_TRACKS));
203  info.set(INF_TRACKS, QVariant());
204  }
205 
207  metaData().add(labels.toMetaDataList());
208 
209  return (info.tracks() >= 1);
210 }
211 
212 //***************************************************************************
214 {
215  if (!m_queue_input.isEmpty())
216  return true; // there is still something in the queue
217 
218  while (!m_source.atEnd()) {
219  QString line = m_source.readLine(MAX_LINE_LEN).simplified();
220  m_line_nr++;
221  if (!line.length()) {
222  continue; // skip empty line
223  } else if (line.startsWith(QLatin1Char('#'))) {
224  continue; // skip comment lines
225  } else {
226  // -> push back the line into the queue
227  m_queue_input.enqueue(line);
228  return true;
229  }
230  }
231  return false;
232 }
233 
234 //***************************************************************************
235 bool Kwave::AsciiDecoder::decode(QWidget *widget,
236  Kwave::MultiWriter &dst)
237 {
238  Q_UNUSED(widget);
239 
240  Q_ASSERT(m_source.device());
241  if (!m_source.device()) return false;
242 
243  m_dest = &dst;
244 
245  // for the moment: use a comma as separator <= TODO
246  const char separators[] = {',', '\0' };
247 
248  Kwave::FileInfo info(metaData());
249  unsigned int channels = info.tracks();
250  QVector<sample_t> frame(channels);
251 
252  // read in all remaining data until EOF or user cancel
253  qDebug("AsciiDecoder::decode(...)");
254  while (readNextLine() && !dst.isCanceled()) {
255  QByteArray d = m_queue_input.dequeue().toLatin1();
256  char *line = d.data();
257  char *saveptr = Q_NULLPTR;
258 
259  frame.fill(0);
260  for (unsigned int channel = 0; channel < channels; channel++) {
261  sample_t s = 0;
262 
263  char *token = strtok_r(line, separators, &saveptr);
264  line = Q_NULLPTR;
265  if (token) {
266  // skip whitespace at the start
267  while (*token && isspace(*token)) ++token;
268  if (*token) {
269  char *p = token + 1;
270  while (isdigit(*p) || (*p == '+') || (*p == '-')) ++p;
271  *p = 0;
272  if (*token) s = atoi(token);
273  Kwave::Writer *w = dst[channel];
274  if (w) (*w) << s;
275  }
276  }
277  }
278  }
279 
280  m_dest = Q_NULLPTR;
281  info.setLength(dst.last() ? (dst.last() + 1) : 0);
283 
284  // return with a valid Signal, even if the user pressed cancel !
285  return true;
286 }
287 
288 //***************************************************************************
290 {
291  m_source.reset();
292  m_source.setDevice(Q_NULLPTR);
293 }
294 
295 //***************************************************************************
296 //***************************************************************************
bool contains(const FileProperty property) const
Definition: FileInfo.cpp:354
virtual bool decode(QWidget *widget, Kwave::MultiWriter &dst) Q_DECL_OVERRIDE
Definition: App.h:33
QString name(FileProperty key) const
Definition: FileInfo.h:227
QVariant get(FileProperty key) const
Definition: FileInfo.cpp:372
virtual ~AsciiDecoder() Q_DECL_OVERRIDE
#define LOAD_MIME_TYPES
quint64 sample_index_t
Definition: Sample.h:28
virtual void add(const MetaData &metadata)
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
virtual sample_index_t last() const
Definition: MultiWriter.cpp:85
const char name[16]
Definition: memcpy.c:510
virtual void close() Q_DECL_OVERRIDE
static QString unescape(const QString &text)
Definition: Parser.cpp:314
QList< FileProperty > allKnownProperties() const
Definition: FileInfo.cpp:383
void setLength(sample_index_t length)
Definition: FileInfo.cpp:409
QQueue< QString > m_queue_input
Definition: AsciiDecoder.h:90
virtual bool open(QWidget *widget, QIODevice &source) Q_DECL_OVERRIDE
virtual void replace(const MetaDataList &list)
virtual Kwave::MetaDataList & metaData()
Definition: Decoder.h:78
bool isCanceled() const
Definition: MultiWriter.h:64
Kwave::MetaDataList toMetaDataList() const
Definition: LabelList.cpp:71
QTextStream m_source
Definition: AsciiDecoder.h:84
unsigned int tracks() const
Definition: FileInfo.cpp:445
Kwave::MultiWriter * m_dest
Definition: AsciiDecoder.h:87
#define REGISTER_COMPRESSION_TYPES
#define _(m)
Definition: memcpy.c:66
#define DBG(qs)
Definition: String.h:55
#define MAX_LINE_LEN
FileProperty
Definition: FileInfo.h:45
virtual Kwave::Decoder * instance() Q_DECL_OVERRIDE
qint32 sample_t
Definition: Sample.h:37