kwave  18.07.70
WavDecoder.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  WavDecoder.cpp - decoder for wav data
3  -------------------
4  begin : Sun Mar 10 2002
5  copyright : (C) 2002 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 
18 #include "config.h"
19 #include <stdlib.h>
20 
21 #include <audiofile.h>
22 
23 #include <QList>
24 #include <QVector>
25 #include <QtEndian>
26 #include <QtGlobal>
27 
28 #include <QApplication>
29 #include <QProgressDialog>
30 
31 #include <KLocalizedString>
32 
33 #include "libkwave/Compression.h"
35 #include "libkwave/Label.h"
36 #include "libkwave/LabelList.h"
37 #include "libkwave/MessageBox.h"
38 #include "libkwave/MetaData.h"
39 #include "libkwave/MetaDataList.h"
40 #include "libkwave/MultiWriter.h"
41 #include "libkwave/Sample.h"
42 #include "libkwave/SampleFormat.h"
43 #include "libkwave/String.h"
44 #include "libkwave/Utils.h"
46 #include "libkwave/Writer.h"
47 
48 #include "RIFFChunk.h"
49 #include "RIFFParser.h"
50 #include "RecoveryBuffer.h"
51 #include "RecoveryMapping.h"
52 #include "RecoverySource.h"
53 #include "RepairVirtualAudioFile.h"
54 #include "WavDecoder.h"
55 #include "WavFileFormat.h"
56 #include "WavFormatMap.h"
57 
58 #define CHECK(cond) Q_ASSERT(cond); if (!(cond)) { src.close(); return false; }
59 
60 //***************************************************************************
62  :Kwave::Decoder(),
63  m_source(Q_NULLPTR),
64  m_src_adapter(Q_NULLPTR),
65  m_known_chunks(),
66  m_property_map()
67 {
70 
71  // native WAVE chunk names
72  m_known_chunks.append(_("cue ")); /* Markers */
73  m_known_chunks.append(_("data")); /* Sound Data */
74  m_known_chunks.append(_("fact")); /* Fact (length in samples) */
75  m_known_chunks.append(_("fmt ")); /* Format */
76  m_known_chunks.append(_("inst")); /* Instrument */
77  m_known_chunks.append(_("labl")); /* label */
78  m_known_chunks.append(_("ltxt")); /* labeled text */
79  m_known_chunks.append(_("note")); /* note chunk */
80  m_known_chunks.append(_("plst")); /* Play List */
81  m_known_chunks.append(_("smpl")); /* Sampler */
82 
83  // add all sub-chunks of the LIST chunk (properties)
84  foreach (const QByteArray &name, m_property_map.chunks())
85  m_known_chunks.append(QLatin1String(name));
86 
87  // some chunks known from AIFF format
88  m_known_chunks.append(_("FVER"));
89  m_known_chunks.append(_("COMM"));
90  m_known_chunks.append(_("wave"));
91  m_known_chunks.append(_("SSND"));
92 
93  // chunks of .lbm image files, IFF format
94  m_known_chunks.append(_("BMHD"));
95  m_known_chunks.append(_("CMAP"));
96  m_known_chunks.append(_("BODY"));
97 }
98 
99 //***************************************************************************
101 {
102  if (m_source) close();
103  if (m_src_adapter) delete m_src_adapter;
104 }
105 
106 //***************************************************************************
108 {
109  return new Kwave::WavDecoder();
110 }
111 
112 //***************************************************************************
113 bool Kwave::WavDecoder::open(QWidget *widget, QIODevice &src)
114 {
115  Kwave::FileInfo info;
116  Kwave::LabelList labels;
117  metaData().clear();
118 
119  Q_ASSERT(!m_source);
120  bool need_repair = false;
121  if (m_source) qWarning("WavDecoder::open(), already open !");
122 
123  // try to open the source
124  if (!src.open(QIODevice::ReadOnly)) {
125  qWarning("failed to open source !");
126  return false;
127  }
128 
129  QStringList main_chunks;
130  main_chunks.append(_("RIFF")); /* RIFF, little-endian */
131  main_chunks.append(_("RIFX")); /* RIFF, big-endian */
132  main_chunks.append(_("FORM")); /* used in AIFF, BE or IFF/.lbm */
133  main_chunks.append(_("LIST")); /* additional information */
134  main_chunks.append(_("adtl")); /* Associated Data */
135 
136  Kwave::RIFFParser parser(src, main_chunks, m_known_chunks);
137 
138  // prepare a progress dialog
139  QProgressDialog progress(widget);
140  progress.setWindowTitle(i18n("Auto Repair"));
141  progress.setModal(true);
142  progress.setMinimumDuration(0);
143  progress.setMaximum(100);
144  progress.setAutoClose(true);
145  progress.setValue(0);
146  progress.setLabelText(i18n("Reading..."));
147  connect(&parser, SIGNAL(progress(int)),
148  &progress, SLOT(setValue(int)));
149  connect(&parser, SIGNAL(action(QString)),
150  &progress, SLOT(setLabelText(QString)));
151  Kwave::ConfirmCancelProxy confirm_cancel(widget,
152  &progress, SIGNAL(canceled()),
153  &parser, SLOT(cancel()));
154 
155  // parse, including endianness detection
156  parser.parse();
157  progress.reset();
158  if (progress.wasCanceled()) return false;
159 
160 // qDebug("--- RIFF file structure after first pass ---");
161 // parser.dumpStructure();
162 
163  // check if there is a RIFF chunk at all...
164  Kwave::RIFFChunk *riff_chunk = parser.findChunk("/RIFF:WAVE");
165  Kwave::RIFFChunk *fact_chunk = parser.findChunk("/RIFF:WAVE/fact");
166  Kwave::RIFFChunk *fmt_chunk = parser.findChunk("/RIFF:WAVE/fmt ");
167  Kwave::RIFFChunk *data_chunk = parser.findChunk("/RIFF:WAVE/data");
168 
169  if (!riff_chunk || !fmt_chunk || !data_chunk || !parser.isSane()) {
171  i18n("The file has been structurally damaged or "
172  "it is no WAV file.\n"
173  "Should Kwave try to repair it?"),
174  i18n("Kwave Auto Repair"),
175  i18n("&Repair")) != KMessageBox::Continue)
176  {
177  // user didn't let us try :-(
178  return false;
179  }
180 
181  need_repair = true;
182  }
183 
184  // collect all missing chunks
185  if (!riff_chunk) riff_chunk = parser.findMissingChunk("RIFF:WAVE");
186  if (progress.wasCanceled()) return false;
187 
188  if (!fmt_chunk) {
189  parser.findMissingChunk("fmt ");
190  fmt_chunk = parser.findChunk("/RIFF:WAVE/fmt ");
191  if (progress.wasCanceled()) return false;
192  if (!fmt_chunk) fmt_chunk = parser.findChunk("fmt ");
193  need_repair = true;
194  }
195 
196  if (!data_chunk) {
197  parser.findMissingChunk("data");
198  data_chunk = parser.findChunk("/RIFF:WAVE/data");
199  if (progress.wasCanceled()) return false;
200  if (!data_chunk) data_chunk = parser.findChunk("data");
201  need_repair = true;
202  }
203 
204  // not everything found -> need heavy repair actions !
205  if (!fmt_chunk || !data_chunk || need_repair) {
206  qDebug("doing heavy repair actions...");
207  parser.dumpStructure();
208  parser.repair();
209  parser.dumpStructure();
210  if (progress.wasCanceled()) return false;
211 
212  if (!fmt_chunk) fmt_chunk = parser.findChunk("/RIFF:WAVE/fmt ");
213  if (!fmt_chunk) fmt_chunk = parser.findChunk("/RIFF/fmt ");
214  if (!fmt_chunk) fmt_chunk = parser.findChunk("fmt ");
215  if (!data_chunk) data_chunk = parser.findChunk("/RIFF:WAVE/data");
216  if (!data_chunk) data_chunk = parser.findChunk("/RIFF/data");
217  if (!data_chunk) data_chunk = parser.findChunk("data");
218  need_repair = true;
219  }
220 
221  quint32 fmt_offset = 0;
222  if (fmt_chunk) fmt_offset = fmt_chunk->dataStart();
223 // qDebug("fmt chunk starts at 0x%08X", fmt_offset);
224 
225 // quint32 data_offset = 0;
226  quint32 data_size = 0;
227  if (data_chunk) {
228 // data_offset = data_chunk->dataStart();
229  data_size = data_chunk->physLength();
230 // qDebug("data chunk at 0x%08X (%u byte)", data_offset, data_size);
231  }
232 
233  if (!data_size) {
234  // hide progress bar, finally
235  progress.hide();
236  qApp->processEvents();
237 
238  // show final error message
240  i18n("The opened file is no WAV file or it is damaged:\n"
241  "There is not enough valid sound data.\n\n"
242  "It makes no sense to continue now."));
243  return false;
244  }
245 
246  // WORKAROUND: if there is a fact chunk, it must not contain zero
247  // for the moment the only workaround known is to open
248  // such broken files in repair mode
249  if (fact_chunk) {
250  qint64 offset = fact_chunk->dataStart();
251  union {
252  quint32 len;
253  char bytes[4];
254  } fact;
255  fact.len = 0;
256  src.seek(offset);
257  src.read(&(fact.bytes[0]), 4);
258  if ((fact.len == 0x00000000) || (fact.len == 0xFFFFFFFF)) {
259  qWarning("WARNING: invalid 'fact' chunk content: 0x%08X "
260  "-> silent repair mode", fact.len);
261  need_repair = true;
262  }
263  }
264 
265  // final check for structural integrity:
266  // we should have exactly one RIFF, fmt and data chunk !
267  if ((parser.chunkCount("fmt ") != 1) ||
268  (parser.chunkCount("data") != 1))
269  {
270  // hide progress bar, temporarily
271  progress.hide();
272  qApp->processEvents();
273 
275  i18n("The WAV file seems to be damaged:\n"
276  "Some chunks are duplicate or missing.\n\n"
277  "Kwave will only use the first ones and ignore\n"
278  "the rest. This might lead to loss of data.\n"
279  "If you want to get your file repaired completely,\n"
280  "please write an email to the Kwave mailing list\n"
281  "and we will help you."),
282  i18n("Kwave Auto Repair")
283  ) != KMessageBox::Continue)
284  {
285  // user decided to abort and repair on his own
286  // good luck!
287  return false;
288  }
289  need_repair = true;
290 
291  // show progress bar again
292  progress.show();
293  qApp->processEvents();
294  }
295 
296  // source successfully opened
297  m_source = &src;
298 
299  // read the wave header
301 
302  unsigned int rate = 0;
303  unsigned int bits = 0;
304 
305  src.seek(fmt_offset);
306 
307  // get the encoded block of data from the mime source
308  CHECK(src.size() > static_cast<qint64>(sizeof(Kwave::wav_header_t) + 8));
309 
310  // get the header
311  src.read(reinterpret_cast<char *>(&header), sizeof(Kwave::wav_fmt_header_t));
312  header.min.format = qFromLittleEndian<quint16>(header.min.format);
313  header.min.channels = qFromLittleEndian<quint16>(header.min.channels);
314  header.min.samplerate = qFromLittleEndian<quint32>(header.min.samplerate);
315  header.min.bytespersec = qFromLittleEndian<quint32>(header.min.bytespersec);
316  header.min.blockalign = qFromLittleEndian<quint16>(header.min.blockalign);
317  header.min.bitwidth = qFromLittleEndian<quint16>(header.min.bitwidth);
318 
319  unsigned int tracks = header.min.channels;
320  rate = header.min.samplerate;
321  bits = header.min.bitwidth;
322 
323  Kwave::WavFormatMap known_formats;
324  QString format_name = known_formats.findName(header.min.format);
325 
326 // qDebug("-------------------------");
327 // qDebug("wav header:");
328 // qDebug("format = 0x%04X, (%s)", header.min.format,
329 // DBG(format_name));
330 // qDebug("channels = %d", header.min.channels);
331 // qDebug("rate = %u", header.min.samplerate);
332 // qDebug("bytes/s = %u", header.min.bytespersec);
333 // qDebug("block align = %d", header.min.blockalign);
334 // qDebug("bits/sample = %d", header.min.bitwidth);
335 // qDebug("-------------------------");
336 
337  // open the file through libaudiofile :)
338  if (need_repair) {
339  QList<Kwave::RecoverySource *> *repair_list =
340  new QList<Kwave::RecoverySource *>();
341  Q_ASSERT(repair_list);
342  if (!repair_list) return false;
343 
344  Kwave::RIFFChunk *root = (riff_chunk) ? riff_chunk : parser.findChunk("");
345 // parser.dumpStructure();
346 // qDebug("riff chunk = %p, parser.findChunk('')=%p", riff_chunk,
347 // parser.findChunk(""));
348  repair(repair_list, root, fmt_chunk, data_chunk);
350  } else {
352  }
353 
354  Q_ASSERT(m_src_adapter);
355  if (!m_src_adapter) return false;
356 
357  m_src_adapter->open(m_src_adapter, Q_NULLPTR);
358 
359  AFfilehandle fh = m_src_adapter->handle();
360  if (!fh || (m_src_adapter->lastError() >= 0)) {
361  QString reason;
362 
363  switch (m_src_adapter->lastError()) {
364  case AF_BAD_NOT_IMPLEMENTED:
365  reason = i18n("Format or function is not implemented") +
366  _("\n(") + format_name + _(")");
367  break;
368  case AF_BAD_MALLOC:
369  reason = i18n("Out of memory");
370  break;
371  case AF_BAD_HEADER:
372  reason = i18n("file header is damaged");
373  break;
374  case AF_BAD_CODEC_TYPE:
375  reason = i18n("Invalid codec type") +
376  _("\n(") + format_name + _(")");
377  break;
378  case AF_BAD_OPEN:
379  reason = i18n("Opening the file failed");
380  break;
381  case AF_BAD_READ:
382  reason = i18n("Read access failed");
383  break;
384  case AF_BAD_SAMPFMT:
385  reason = i18n("Invalid sample format");
386  break;
387  default:
388  reason = i18n("internal libaudiofile error #%1: '%2'",
391  );
392  }
393 
394  QString text= i18n("An error occurred while opening the file:\n'%1'",
395  reason);
396  Kwave::MessageBox::error(widget, text);
397 
398  return false;
399  }
400 
401  AFframecount length = afGetFrameCount(fh, AF_DEFAULT_TRACK);
402  tracks = afGetVirtualChannels(fh, AF_DEFAULT_TRACK);
403 
404  int af_sample_format;
405  afGetVirtualSampleFormat(fh, AF_DEFAULT_TRACK, &af_sample_format,
406  reinterpret_cast<int *>(&bits));
407  if (static_cast<signed int>(bits) < 0) bits = 0;
409  switch (af_sample_format)
410  {
411  case AF_SAMPFMT_TWOSCOMP:
413  break;
414  case AF_SAMPFMT_UNSIGNED:
416  break;
417  case AF_SAMPFMT_FLOAT:
419  break;
420  case AF_SAMPFMT_DOUBLE:
422  break;
423  default:
425  break;
426  }
427 
428  int af_compression = afGetCompression(fh, AF_DEFAULT_TRACK);
429  const Kwave::Compression compression(
430  Kwave::Compression::fromAudiofile(af_compression)
431  );
432 
433  info.setRate(rate);
434  info.setBits(bits);
435  info.setTracks(tracks);
436  info.setLength(length);
438  info.set(Kwave::INF_COMPRESSION, compression.toInt());
439 
440  // read in all info from the LIST (INFO) chunk
441  Kwave::RIFFChunk *info_chunk = parser.findChunk("/RIFF:WAVE/LIST:INFO");
442  if (info_chunk) {
443  // found info chunk !
444  Kwave::RIFFChunkList &list = info_chunk->subChunks();
445  foreach (Kwave::RIFFChunk *chunk, list) {
446  if (!chunk) continue;
447  if (!m_property_map.containsChunk(chunk->name())) continue;
448 
449  // read the content into a QString
451  unsigned int ofs = chunk->dataStart();
452  unsigned int len = chunk->dataLength();
453  QByteArray buffer(len + 1, 0x00);
454  src.seek(ofs);
455  src.read(buffer.data(), len);
456  buffer[len] = 0;
457  QString value;
458  value = QString::fromUtf8(buffer);
459  info.set(prop, value);
460  }
461  }
462 
463  // read in the Labels (cue list)
464  Kwave::RIFFChunk *cue_chunk = parser.findChunk("/RIFF:WAVE/cue ");
465  if (cue_chunk) {
466  // found a cue list chunk !
467  quint32 count;
468 
469  src.seek(cue_chunk->dataStart());
470  src.read(reinterpret_cast<char *>(&count), 4);
471  count = qFromLittleEndian<quint32>(count);
472 // unsigned int length = cue_chunk->dataLength();
473 // qDebug("cue list found: %u entries, %u bytes (should be: %u)",
474 // count, length, count * (6 * 4) + 4);
475 
476  for (unsigned int i = 0; i < count; i++) {
477  quint32 data, index, position;
478  src.seek(cue_chunk->dataStart() + 4 + ((6 * 4) * i));
479  /*
480  * typedef struct {
481  * quint32 dwIdentifier; <- index
482  * quint32 dwPosition; <- 0
483  * quint32 fccChunk; <- 'data'
484  * quint32 dwChunkStart; <- 0
485  * quint32 dwBlockStart; <- 0
486  * quint32 dwSampleOffset; <- label.pos()
487  * } cue_list_entry_t;
488  */
489 
490  /* dwIdentifier */
491  src.read(reinterpret_cast<char *>(&data), 4);
492  index = qFromLittleEndian<quint32>(data);
493 
494  /* dwPosition (ignored) */
495  src.read(reinterpret_cast<char *>(&data), 4);
496 
497  /* fccChunk */
498  src.read(reinterpret_cast<char *>(&data), 4);
499  /* we currently support only 'data' */
500  if (qstrncmp(reinterpret_cast<const char *>(&data), "data", 4)) {
501  qWarning("cue list entry %d refers to '%s', "
502  "which is not supported -> skipped",
503  index, QByteArray(
504  reinterpret_cast<const char *>(&data), 4).data());
505  continue;
506  }
507  src.read(reinterpret_cast<char *>(&data), 4);
508  /* dwChunkStart (must be 0) */
509  if (data != 0) {
510  qWarning("cue list entry %d has dwChunkStart != 0 -> skipped",
511  index);
512  continue;
513  }
514  src.read(reinterpret_cast<char *>(&data), 4);
515  /* dwBlockStart (must be 0) */
516  if (data != 0) {
517  qWarning("cue list entry %d has dwBlockStart != 0 -> skipped",
518  index);
519  continue;
520  }
521 
522  src.read(reinterpret_cast<char *>(&data), 4); /* dwSampleOffset */
523  position = qFromLittleEndian<quint32>(data);
524 
525  // as we now have index and position, find out the name
526  QByteArray name = "";
527  Kwave::RIFFChunk *adtl_chunk =
528  parser.findChunk("/RIFF:WAVE/LIST:adtl");
529  if (adtl_chunk) {
530  Kwave::RIFFChunk *labl_chunk = Q_NULLPTR;
531  bool found = false;
532  QListIterator<Kwave::RIFFChunk *> it(adtl_chunk->subChunks());
533  while (it.hasNext()) {
534  quint32 labl_index;
535  labl_chunk = it.next();
536  /*
537  * typedef struct {
538  * quint32 dwChunkID; <- 'labl'
539  * quint32 dwChunkSize; (without padding !)
540  * quint32 dwIdentifier; <- index
541  * char dwText[]; <- label->name()
542  * } label_list_entry_t;
543  */
544  if (labl_chunk->name() != "labl") continue;
545 
546  data = 0;
547  src.seek(labl_chunk->dataStart());
548  src.read(reinterpret_cast<char *>(&data), 4); /* dwIdentifier */
549  labl_index = qFromLittleEndian<quint32>(data);
550  if (labl_index == index) {
551  found = true;
552  break; /* found it! */
553  }
554  }
555  if (found) {
556  Q_ASSERT(labl_chunk);
557  unsigned int len = labl_chunk->length();
558  if (len > 4) {
559  len -= 4;
560  name.resize(len);
561  src.seek(labl_chunk->dataStart() + 4);
562  src.read(static_cast<char *>(name.data()), len);
563  if (name[name.count() - 1] != '\0')
564  name += '\0';
565  }
566  }
567  }
568 
569  if (!name.length()) {
570 // qDebug("cue list entry %d has no name", index);
571  name = "";
572  }
573 
574  // put a new label into the list
575  QString str = QString::fromUtf8(name);
576  labels.append(Kwave::Label(position, str));
577  }
578  }
579  labels.sort();
581  metaData().replace(labels.toMetaDataList());
582 
583  // set up libaudiofile to produce Kwave's internal sample format
584 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
585  afSetVirtualByteOrder(fh, AF_DEFAULT_TRACK, AF_BYTEORDER_BIGENDIAN);
586 #else
587  afSetVirtualByteOrder(fh, AF_DEFAULT_TRACK, AF_BYTEORDER_LITTLEENDIAN);
588 #endif
589  afSetVirtualSampleFormat(fh, AF_DEFAULT_TRACK,
590  AF_SAMPFMT_TWOSCOMP, SAMPLE_STORAGE_BITS);
591 
592  return true;
593 }
594 
595 //***************************************************************************
596 bool Kwave::WavDecoder::decode(QWidget */*widget*/, Kwave::MultiWriter &dst)
597 {
598  Q_ASSERT(m_src_adapter);
599  Q_ASSERT(m_source);
600  if (!m_source) return false;
601  if (!m_src_adapter) return false;
602 
603  AFfilehandle fh = m_src_adapter->handle();
604  Q_ASSERT(fh);
605  if (!fh) return false;
606 
607 // info().dump();
608  const unsigned int tracks = dst.tracks();
609 
610  // allocate an array of Writers, for speeding up
611  QVector<Kwave::Writer *>writers(tracks);
612  Q_ASSERT(writers.count() == Kwave::toInt(dst.tracks()));
613  if (writers.count() != Kwave::toInt(dst.tracks())) return false;
614  for (unsigned int t = 0; t < tracks; t++)
615  writers[t] = dst[t];
616  Kwave::Writer **writer_fast = writers.data();
617 
618  unsigned int frame_size = Kwave::toUint(
619  afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, 1));
620 
621  // allocate a buffer for input data
622  const unsigned int buffer_frames = (8 * 1024);
623  sample_storage_t *buffer = static_cast<sample_storage_t *>(
624  malloc(buffer_frames * frame_size));
625  Q_ASSERT(buffer);
626  if (!buffer) return false;
627 
628  // read in from the audiofile source
629  Q_ASSERT(tracks == Kwave::FileInfo(metaData()).tracks());
631  while (rest) {
632  unsigned int frames = buffer_frames;
633  if (frames > rest) frames = Kwave::toUint(rest);
634  unsigned int buffer_used = afReadFrames(fh,
635  AF_DEFAULT_TRACK, reinterpret_cast<char *>(buffer), frames);
636 
637  // break if eof reached
638  if (!buffer_used) break;
639  rest -= buffer_used;
640 
641  // split into the tracks
642  sample_storage_t *p = buffer;
643  for (unsigned int count = buffer_used; count; count--) {
644  for (unsigned int track = 0; track < tracks; track++) {
645  sample_storage_t s = *p++;
646 
647  // adjust precision
649  s /= (1 << (SAMPLE_STORAGE_BITS - SAMPLE_BITS));
650  }
651 
652  // the following cast is only necessary if
653  // sample_t is not equal to a sample_storage_t
654  Q_ASSERT(writer_fast[track]);
655  *(writer_fast[track]) << static_cast<sample_t>(s);
656  }
657  }
658 
659  // abort if the user pressed cancel
660  if (dst.isCanceled()) break;
661  }
662 
663  // return with a valid Signal, even if the user pressed cancel !
664  if (buffer) free(buffer);
665  return true;
666 }
667 
668 //***************************************************************************
670  QList<Kwave::RecoverySource *> *repair_list,
671  Kwave::RIFFChunk *chunk, quint32 &offset)
672 {
673  Q_ASSERT(chunk);
674  Q_ASSERT(m_source);
675  Q_ASSERT(repair_list);
676  if (!chunk) return false;
677  if (!m_source) return false;
678  if (!repair_list) return false;
679 
680  char buffer[16];
681  quint32 length;
682  Kwave::RecoverySource *repair = Q_NULLPTR;
683 
684  // create buffer with header
685  strncpy(buffer, chunk->name().data(), 4);
686  length = (chunk->type() == Kwave::RIFFChunk::Main) ? chunk->physLength() :
687  chunk->dataLength();
688  buffer[4] = (length ) & 0xFF;
689  buffer[5] = (length >> 8) & 0xFF;
690  buffer[6] = (length >> 16) & 0xFF;
691  buffer[7] = (length >> 24) & 0xFF;
692  if (chunk->type() == Kwave::RIFFChunk::Main) {
693  strncpy(&(buffer[8]), chunk->format().data(), 4);
694  repair = new Kwave::RecoveryBuffer(offset, 12, buffer);
695  qDebug("[0x%08X-0x%08X] - main header '%s' (%s), len=%u",
696  offset, offset+11, chunk->name().data(),
697  chunk->format().data(), length);
698  offset += 12;
699  } else {
700  repair = new Kwave::RecoveryBuffer(offset, 8, buffer);
701  qDebug("[0x%08X-0x%08X] - sub header '%s', len=%u",
702  offset, offset+7, chunk->name().data(), length);
703  offset += 8;
704  }
705  Q_ASSERT(repair);
706  if (!repair) return false;
707  repair_list->append(repair);
708 
709  // map the chunk's data if not main or root
710  if ((chunk->type() != Kwave::RIFFChunk::Root) &&
711  (chunk->type() != Kwave::RIFFChunk::Main))
712  {
713  repair = new Kwave::RecoveryMapping(offset, chunk->physLength(),
714  *m_source, chunk->dataStart());
715  qDebug("[0x%08X-0x%08X] - restoring from offset 0x%08X (%u)",
716  offset, offset+chunk->physLength()-1, chunk->dataStart(),
717  chunk->physLength());
718  Q_ASSERT(repair);
719  if (!repair) return false;
720  repair_list->append(repair);
721 
722  offset += chunk->physLength();
723  }
724 
725  // recursively go over all sub-chunks
726  Kwave::RIFFChunkList &list = chunk->subChunks();
727  foreach (Kwave::RIFFChunk *c, list) {
728  if (!c) continue;
729  if (!repairChunk(repair_list, c, offset))
730  return false;
731  }
732 
733  return true;
734 }
735 
736 //***************************************************************************
737 bool Kwave::WavDecoder::repair(QList<Kwave::RecoverySource *> *repair_list,
738  Kwave::RIFFChunk *riff_chunk,
739  Kwave::RIFFChunk *fmt_chunk,
740  Kwave::RIFFChunk *data_chunk)
741 {
742  Q_ASSERT(fmt_chunk);
743  Q_ASSERT(data_chunk);
744  if (!fmt_chunk || !data_chunk) return false;
745 
746  // --- first create a chunk tree for the structure ---
747 
748  // make a new "RIFF" chunk as root
749  Kwave::RIFFChunk new_root(Q_NULLPTR, "RIFF", "WAVE", 0,0,0);
751 
752  // create a new "fmt " chunk
753  Kwave::RIFFChunk *new_fmt = new(std::nothrow)
754  Kwave::RIFFChunk(&new_root, "fmt ",Q_NULLPTR, 0,
755  fmt_chunk->physStart(), fmt_chunk->physLength());
756  Q_ASSERT(new_fmt);
757  if (!new_fmt) return false;
758  new_root.subChunks().append(new_fmt);
759 
760  // create a new "data" chunk
761  Kwave::RIFFChunk *new_data =
762  new Kwave::RIFFChunk(&new_root, "data", Q_NULLPTR, 0,
763  data_chunk->physStart(), data_chunk->physLength());
764  Q_ASSERT(new_data);
765  if (!new_data) return false;
766  new_root.subChunks().append(new_data);
767 
768  // if we have a RIFF chunk, add it's subchunks, except "fmt " and "data"
769  // we keep only pointers here, just for getting the right structure,
770  // sizes and start positions.
771  // NOTE: The sizes might be re-assigned and get invalid afterwards!!!
772  if (riff_chunk) {
773  Kwave::RIFFChunkList &list = riff_chunk->subChunks();
774  foreach (Kwave::RIFFChunk *chunk, list) {
775  if (!chunk) continue;
776  if (chunk->name() == "fmt ") continue;
777  if (chunk->name() == "data") continue;
778  if (chunk->name() == "RIFF") continue;
779  if (chunk->type() == Kwave::RIFFChunk::Empty) continue;
780  if (chunk->type() == Kwave::RIFFChunk::Garbage) continue;
781 
782  new_root.subChunks().append(chunk);
783  }
784  }
785 
786  // fix all node sizes (compress)
787  new_root.fixSize();
788 
789  // attention: some of the offsets belong to source file, some belong
790  // to reconstructed buffers! only the sizes are correct.
791 // new_root.dumpStructure();
792 
793  // --- set up the repair list ---
794 
795  // RIFF chunk length
796  quint32 offset = 0;
797  bool repaired = repairChunk(repair_list, &new_root, offset);
798 
799  // clean up...
800  new_root.subChunks().clear();
801  delete new_fmt;
802  delete new_data;
803 
804  return (repaired);
805 }
806 
807 //***************************************************************************
809 {
810  if (m_src_adapter) delete m_src_adapter;
811  m_src_adapter = Q_NULLPTR;
812  m_source = Q_NULLPTR;
813 }
814 
815 //***************************************************************************
816 //***************************************************************************
AFfilehandle & handle()
static Kwave::Compression::Type fromAudiofile(int af_compression)
virtual unsigned int tracks() const Q_DECL_OVERRIDE
Kwave::WavPropertyMap m_property_map
Definition: WavDecoder.h:114
Definition: App.h:33
virtual bool open(QWidget *widget, QIODevice &source) Q_DECL_OVERRIDE
Definition: WavDecoder.cpp:113
Kwave::RIFFChunk * findMissingChunk(const QByteArray &name)
Definition: RIFFParser.cpp:544
quint32 physStart() const
Definition: RIFFChunk.h:135
quint32 physLength() const
Definition: RIFFChunk.h:146
quint32 length() const
Definition: RIFFChunk.h:123
static int sorry(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:85
virtual bool decode(QWidget *widget, Kwave::MultiWriter &dst) Q_DECL_OVERRIDE
Definition: WavDecoder.cpp:596
Kwave::VirtualAudioFile * m_src_adapter
Definition: WavDecoder.h:108
Kwave::FileProperty property(const QByteArray &chunk) const
quint64 sample_index_t
Definition: Sample.h:28
virtual void open(Kwave::VirtualAudioFile *x, AFfilesetup setup)
QStringList m_known_chunks
Definition: WavDecoder.h:111
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
quint32 dataStart() const
Definition: RIFFChunk.cpp:130
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
virtual Kwave::Decoder * instance() Q_DECL_OVERRIDE
Definition: WavDecoder.cpp:107
void setRate(double rate)
Definition: FileInfo.cpp:424
const char name[16]
Definition: memcpy.c:510
ChunkType type() const
Definition: RIFFChunk.h:85
void setType(ChunkType type)
Definition: RIFFChunk.h:88
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
unsigned int chunkCount(const QByteArray &path)
Definition: RIFFParser.cpp:463
sample_index_t length() const
Definition: FileInfo.cpp:400
QList< Kwave::RIFFChunk * > RIFFChunkList
Definition: RIFFChunk.h:30
int toInt(T x)
Definition: Utils.h:127
void setLength(sample_index_t length)
Definition: FileInfo.cpp:409
quint32 dataLength() const
Definition: RIFFChunk.cpp:136
void setTracks(unsigned int tracks)
Definition: FileInfo.cpp:454
qint32 sample_storage_t
Definition: Sample.h:40
Kwave::RIFFChunkList & subChunks()
Definition: RIFFChunk.h:151
Kwave::min_wav_header_t min
virtual void replace(const MetaDataList &list)
virtual Kwave::MetaDataList & metaData()
Definition: Decoder.h:78
#define REGISTER_MIME_TYPES
Kwave::RIFFChunk * findChunk(const QByteArray &path)
Definition: RIFFParser.cpp:443
bool isCanceled() const
Definition: MultiWriter.h:64
Kwave::MetaDataList toMetaDataList() const
Definition: LabelList.cpp:71
QIODevice * m_source
Definition: WavDecoder.h:105
static int warningContinueCancel(QWidget *widget, QString message, QString caption=QString(), const QString buttonContinue=QString(), const QString buttonCancel=QString(), const QString &dontAskAgainName=QString())
Definition: MessageBox.cpp:115
#define CHECK(cond)
Definition: WavDecoder.cpp:58
virtual ~WavDecoder() Q_DECL_OVERRIDE
Definition: WavDecoder.cpp:100
void setBits(unsigned int bits)
Definition: FileInfo.cpp:439
#define REGISTER_COMPRESSION_TYPES
bool repair(QList< Kwave::RecoverySource *> *repair_list, Kwave::RIFFChunk *riff_chunk, Kwave::RIFFChunk *fmt_chunk, Kwave::RIFFChunk *data_chunk)
Definition: WavDecoder.cpp:737
const QByteArray & name() const
Definition: RIFFChunk.h:91
#define _(m)
Definition: memcpy.c:66
bool repairChunk(QList< Kwave::RecoverySource *> *repair_list, Kwave::RIFFChunk *chunk, quint32 &offset)
Definition: WavDecoder.cpp:669
#define SAMPLE_STORAGE_BITS
Definition: Sample.h:46
unsigned int toUint(T x)
Definition: Utils.h:109
virtual void close() Q_DECL_OVERRIDE
Definition: WavDecoder.cpp:808
const QByteArray & format() const
Definition: RIFFChunk.h:97
virtual void sort()
Definition: LabelList.cpp:64
FileProperty
Definition: FileInfo.h:45
#define SAMPLE_BITS
Definition: Sample.h:43
bool containsChunk(const QByteArray &chunk) const
QList< QByteArray > chunks() const
int toInt() const
Definition: Compression.h:122
const QString & findName(unsigned int id)