kwave  18.07.70
Kwave::WavDecoder Class Reference

#include <WavDecoder.h>

Inheritance diagram for Kwave::WavDecoder:
Inheritance graph
Collaboration diagram for Kwave::WavDecoder:
Collaboration graph

Public Member Functions

 WavDecoder ()
 
virtual ~WavDecoder () Q_DECL_OVERRIDE
 
virtual Kwave::Decoderinstance () Q_DECL_OVERRIDE
 
virtual bool open (QWidget *widget, QIODevice &source) Q_DECL_OVERRIDE
 
virtual bool decode (QWidget *widget, Kwave::MultiWriter &dst) Q_DECL_OVERRIDE
 
virtual void close () Q_DECL_OVERRIDE
 
- Public Member Functions inherited from Kwave::Decoder
 Decoder ()
 
virtual ~Decoder ()
 
virtual Kwave::MetaDataListmetaData ()
 
- Public Member Functions inherited from Kwave::CodecBase
 CodecBase ()
 
virtual ~CodecBase ()
 
virtual bool supports (const QMimeType &mimetype)
 
virtual bool supports (const QString &mimetype_name)
 
virtual QStringList extensions (const QString &mimetype_name) const
 
virtual const QList< CodecBase::MimeTypemimeTypes ()
 
virtual const QList< Kwave::Compression::TypecompressionTypes ()
 
virtual void addMimeType (const char *name, const QString &description, const char *patterns)
 
virtual void addCompression (Kwave::Compression::Type compression)
 
virtual QString mimeTypeOf (const QUrl &url)
 

Protected Member Functions

bool repair (QList< Kwave::RecoverySource *> *repair_list, Kwave::RIFFChunk *riff_chunk, Kwave::RIFFChunk *fmt_chunk, Kwave::RIFFChunk *data_chunk)
 
bool repairChunk (QList< Kwave::RecoverySource *> *repair_list, Kwave::RIFFChunk *chunk, quint32 &offset)
 

Private Member Functions

void addPropertyChunk (const Kwave::FileProperty property, const QByteArray &chunk_name)
 

Private Attributes

QIODevice * m_source
 
Kwave::VirtualAudioFilem_src_adapter
 
QStringList m_known_chunks
 
Kwave::WavPropertyMap m_property_map
 

Additional Inherited Members

- Signals inherited from Kwave::Decoder
void sourceProcessed (quint64 pos)
 
- Protected Attributes inherited from Kwave::Decoder
Kwave::MetaDataList m_meta_data
 

Detailed Description

Definition at line 43 of file WavDecoder.h.

Constructor & Destructor Documentation

◆ WavDecoder()

Kwave::WavDecoder::WavDecoder ( )

Constructor

Definition at line 61 of file WavDecoder.cpp.

References _, Kwave::WavPropertyMap::chunks(), m_known_chunks, m_property_map, name, REGISTER_COMPRESSION_TYPES, and REGISTER_MIME_TYPES.

62  :Kwave::Decoder(),
63  m_source(Q_NULLPTR),
64  m_src_adapter(Q_NULLPTR),
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 }
Kwave::WavPropertyMap m_property_map
Definition: WavDecoder.h:114
Kwave::VirtualAudioFile * m_src_adapter
Definition: WavDecoder.h:108
QStringList m_known_chunks
Definition: WavDecoder.h:111
const char name[16]
Definition: memcpy.c:510
#define REGISTER_MIME_TYPES
QIODevice * m_source
Definition: WavDecoder.h:105
#define REGISTER_COMPRESSION_TYPES
#define _(m)
Definition: memcpy.c:66
QList< QByteArray > chunks() const
Here is the call graph for this function:

◆ ~WavDecoder()

Kwave::WavDecoder::~WavDecoder ( )
virtual

Destructor

Definition at line 100 of file WavDecoder.cpp.

References close(), m_source, and m_src_adapter.

101 {
102  if (m_source) close();
103  if (m_src_adapter) delete m_src_adapter;
104 }
Kwave::VirtualAudioFile * m_src_adapter
Definition: WavDecoder.h:108
QIODevice * m_source
Definition: WavDecoder.h:105
virtual void close() Q_DECL_OVERRIDE
Definition: WavDecoder.cpp:808
Here is the call graph for this function:

Member Function Documentation

◆ addPropertyChunk()

void Kwave::WavDecoder::addPropertyChunk ( const Kwave::FileProperty  property,
const QByteArray &  chunk_name 
)
private

adds an entry to m_known_chunks and to m_property_map

◆ close()

void Kwave::WavDecoder::close ( )
virtual

Closes the source.

Implements Kwave::Decoder.

Definition at line 808 of file WavDecoder.cpp.

References m_source, and m_src_adapter.

Referenced by ~WavDecoder().

809 {
810  if (m_src_adapter) delete m_src_adapter;
811  m_src_adapter = Q_NULLPTR;
812  m_source = Q_NULLPTR;
813 }
Kwave::VirtualAudioFile * m_src_adapter
Definition: WavDecoder.h:108
QIODevice * m_source
Definition: WavDecoder.h:105
Here is the caller graph for this function:

◆ decode()

bool Kwave::WavDecoder::decode ( QWidget *  widget,
Kwave::MultiWriter dst 
)
virtual

Decodes a stream of bytes into a MultiWriter

Parameters
widgeta widget that can be used for displaying message boxes or dialogs
dstMultiWriter that receives the audio data
Returns
true if succeeded, false on errors

Implements Kwave::Decoder.

Definition at line 596 of file WavDecoder.cpp.

References Kwave::VirtualAudioFile::handle(), Kwave::MultiWriter::isCanceled(), Kwave::FileInfo::length(), m_source, m_src_adapter, Kwave::Decoder::metaData(), SAMPLE_BITS, SAMPLE_STORAGE_BITS, Kwave::toInt(), Kwave::toUint(), and Kwave::MultiTrackSink< SINK, INITIALIZE >::tracks().

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 }
AFfilehandle & handle()
virtual unsigned int tracks() const Q_DECL_OVERRIDE
Kwave::VirtualAudioFile * m_src_adapter
Definition: WavDecoder.h:108
quint64 sample_index_t
Definition: Sample.h:28
sample_index_t length() const
Definition: FileInfo.cpp:400
int toInt(T x)
Definition: Utils.h:127
qint32 sample_storage_t
Definition: Sample.h:40
virtual Kwave::MetaDataList & metaData()
Definition: Decoder.h:78
bool isCanceled() const
Definition: MultiWriter.h:64
QIODevice * m_source
Definition: WavDecoder.h:105
#define SAMPLE_STORAGE_BITS
Definition: Sample.h:46
unsigned int toUint(T x)
Definition: Utils.h:109
#define SAMPLE_BITS
Definition: Sample.h:43
Here is the call graph for this function:

◆ instance()

Kwave::Decoder * Kwave::WavDecoder::instance ( )
virtual

Returns a new instance of the decoder

Implements Kwave::Decoder.

Definition at line 107 of file WavDecoder.cpp.

108 {
109  return new Kwave::WavDecoder();
110 }

◆ open()

bool Kwave::WavDecoder::open ( QWidget *  widget,
QIODevice &  source 
)
virtual

Opens the source and decodes the header information.

Parameters
widgeta widget that can be used for displaying message boxes or dialogs
sourcefile or other source with a stream of bytes
Returns
true if succeeded, false on errors

Implements Kwave::Decoder.

Definition at line 113 of file WavDecoder.cpp.

References _, Kwave::min_wav_header_t::bitwidth, Kwave::min_wav_header_t::blockalign, Kwave::min_wav_header_t::bytespersec, Kwave::min_wav_header_t::channels, CHECK, Kwave::RIFFParser::chunkCount(), Kwave::connect(), Kwave::WavPropertyMap::containsChunk(), Kwave::RIFFChunk::dataLength(), Kwave::RIFFChunk::dataStart(), Kwave::SampleFormat::Double, Kwave::RIFFParser::dumpStructure(), Kwave::MessageBox::error(), Kwave::RIFFParser::findChunk(), Kwave::RIFFParser::findMissingChunk(), Kwave::WavFormatMap::findName(), Kwave::SampleFormat::Float, Kwave::min_wav_header_t::format, Kwave::Compression::fromAudiofile(), Kwave::VirtualAudioFile::handle(), Kwave::INF_COMPRESSION, Kwave::INF_SAMPLE_FORMAT, Kwave::RIFFParser::isSane(), Kwave::VirtualAudioFile::lastError(), Kwave::VirtualAudioFile::lastErrorText(), Kwave::RIFFChunk::length(), m_known_chunks, m_property_map, m_source, m_src_adapter, Kwave::Decoder::metaData(), Kwave::wav_fmt_header_t::min, Kwave::RIFFChunk::name(), name, Kwave::VirtualAudioFile::open(), Kwave::RIFFParser::parse(), Kwave::RIFFChunk::physLength(), Kwave::WavPropertyMap::property(), repair(), Kwave::RIFFParser::repair(), Kwave::MetaDataList::replace(), SAMPLE_STORAGE_BITS, Kwave::min_wav_header_t::samplerate, Kwave::FileInfo::set(), Kwave::FileInfo::setBits(), Kwave::FileInfo::setLength(), Kwave::FileInfo::setRate(), Kwave::FileInfo::setTracks(), Kwave::SampleFormat::Signed, Kwave::MessageBox::sorry(), Kwave::LabelList::sort(), Kwave::RIFFChunk::subChunks(), Kwave::Compression::toInt(), Kwave::toInt(), Kwave::LabelList::toMetaDataList(), Kwave::SampleFormat::Unknown, Kwave::SampleFormat::Unsigned, and Kwave::MessageBox::warningContinueCancel().

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 }
AFfilehandle & handle()
static Kwave::Compression::Type fromAudiofile(int af_compression)
Kwave::WavPropertyMap m_property_map
Definition: WavDecoder.h:114
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
Kwave::VirtualAudioFile * m_src_adapter
Definition: WavDecoder.h:108
Kwave::FileProperty property(const QByteArray &chunk) const
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
void setRate(double rate)
Definition: FileInfo.cpp:424
const char name[16]
Definition: memcpy.c:510
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
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
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
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
void setBits(unsigned int bits)
Definition: FileInfo.cpp:439
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
#define SAMPLE_STORAGE_BITS
Definition: Sample.h:46
virtual void sort()
Definition: LabelList.cpp:64
FileProperty
Definition: FileInfo.h:45
bool isSane() const
Definition: RIFFChunk.cpp:49
bool containsChunk(const QByteArray &chunk) const
const QString & findName(unsigned int id)
Here is the call graph for this function:

◆ repair()

bool Kwave::WavDecoder::repair ( QList< Kwave::RecoverySource *> *  repair_list,
Kwave::RIFFChunk riff_chunk,
Kwave::RIFFChunk fmt_chunk,
Kwave::RIFFChunk data_chunk 
)
protected

Fix all inconsistencies and create a repar list.

Definition at line 737 of file WavDecoder.cpp.

References Kwave::RIFFChunk::Empty, Kwave::RIFFChunk::fixSize(), Kwave::RIFFChunk::Garbage, Kwave::RIFFChunk::Main, Kwave::RIFFChunk::name(), Kwave::RIFFChunk::physLength(), Kwave::RIFFChunk::physStart(), repairChunk(), Kwave::RIFFChunk::setType(), Kwave::RIFFChunk::subChunks(), and Kwave::RIFFChunk::type().

Referenced by open(), and repairChunk().

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);
750  new_root.setType(Kwave::RIFFChunk::Main);
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 }
quint32 physStart() const
Definition: RIFFChunk.h:135
quint32 physLength() const
Definition: RIFFChunk.h:146
ChunkType type() const
Definition: RIFFChunk.h:85
QList< Kwave::RIFFChunk * > RIFFChunkList
Definition: RIFFChunk.h:30
Kwave::RIFFChunkList & subChunks()
Definition: RIFFChunk.h:151
const QByteArray & name() const
Definition: RIFFChunk.h:91
bool repairChunk(QList< Kwave::RecoverySource *> *repair_list, Kwave::RIFFChunk *chunk, quint32 &offset)
Definition: WavDecoder.cpp:669
Here is the call graph for this function:
Here is the caller graph for this function:

◆ repairChunk()

bool Kwave::WavDecoder::repairChunk ( QList< Kwave::RecoverySource *> *  repair_list,
Kwave::RIFFChunk chunk,
quint32 &  offset 
)
protected

Adds a chunk to a repair list

Definition at line 669 of file WavDecoder.cpp.

References Kwave::RIFFChunk::dataLength(), Kwave::RIFFChunk::dataStart(), Kwave::RIFFChunk::format(), m_source, Kwave::RIFFChunk::Main, Kwave::RIFFChunk::name(), Kwave::RIFFChunk::physLength(), repair(), Kwave::RIFFChunk::Root, Kwave::RIFFChunk::subChunks(), and Kwave::RIFFChunk::type().

Referenced by repair().

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 }
quint32 physLength() const
Definition: RIFFChunk.h:146
quint32 dataStart() const
Definition: RIFFChunk.cpp:130
ChunkType type() const
Definition: RIFFChunk.h:85
QList< Kwave::RIFFChunk * > RIFFChunkList
Definition: RIFFChunk.h:30
quint32 dataLength() const
Definition: RIFFChunk.cpp:136
Kwave::RIFFChunkList & subChunks()
Definition: RIFFChunk.h:151
QIODevice * m_source
Definition: WavDecoder.h:105
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
bool repairChunk(QList< Kwave::RecoverySource *> *repair_list, Kwave::RIFFChunk *chunk, quint32 &offset)
Definition: WavDecoder.cpp:669
const QByteArray & format() const
Definition: RIFFChunk.h:97
Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_known_chunks

QStringList Kwave::WavDecoder::m_known_chunks
private

list of all known chunk names

Definition at line 111 of file WavDecoder.h.

Referenced by open(), and WavDecoder().

◆ m_property_map

Kwave::WavPropertyMap Kwave::WavDecoder::m_property_map
private

map for translating chunk names to FileInfo properties

Definition at line 114 of file WavDecoder.h.

Referenced by open(), and WavDecoder().

◆ m_source

QIODevice* Kwave::WavDecoder::m_source
private

source of the audio data

Definition at line 105 of file WavDecoder.h.

Referenced by close(), decode(), open(), repairChunk(), and ~WavDecoder().

◆ m_src_adapter

Kwave::VirtualAudioFile* Kwave::WavDecoder::m_src_adapter
private

adapter for libaudiofile

Definition at line 108 of file WavDecoder.h.

Referenced by close(), decode(), open(), and ~WavDecoder().


The documentation for this class was generated from the following files: