21 #include <audiofile.h> 28 #include <QApplication> 29 #include <QProgressDialog> 31 #include <KLocalizedString> 58 #define CHECK(cond) Q_ASSERT(cond); if (!(cond)) { src.close(); return false; } 64 m_src_adapter(Q_NULLPTR),
120 bool need_repair =
false;
121 if (
m_source) qWarning(
"WavDecoder::open(), already open !");
124 if (!src.open(QIODevice::ReadOnly)) {
125 qWarning(
"failed to open source !");
129 QStringList main_chunks;
130 main_chunks.append(
_(
"RIFF"));
131 main_chunks.append(
_(
"RIFX"));
132 main_chunks.append(
_(
"FORM"));
133 main_chunks.append(
_(
"LIST"));
134 main_chunks.append(
_(
"adtl"));
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)));
152 &progress, SIGNAL(canceled()),
153 &parser, SLOT(cancel()));
158 if (progress.wasCanceled())
return false;
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)
186 if (progress.wasCanceled())
return false;
190 fmt_chunk = parser.
findChunk(
"/RIFF:WAVE/fmt ");
191 if (progress.wasCanceled())
return false;
192 if (!fmt_chunk) fmt_chunk = parser.
findChunk(
"fmt ");
198 data_chunk = parser.
findChunk(
"/RIFF:WAVE/data");
199 if (progress.wasCanceled())
return false;
200 if (!data_chunk) data_chunk = parser.
findChunk(
"data");
205 if (!fmt_chunk || !data_chunk || need_repair) {
206 qDebug(
"doing heavy repair actions...");
210 if (progress.wasCanceled())
return false;
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");
221 quint32 fmt_offset = 0;
222 if (fmt_chunk) fmt_offset = fmt_chunk->
dataStart();
226 quint32 data_size = 0;
236 qApp->processEvents();
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."));
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);
272 qApp->processEvents();
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)
293 qApp->processEvents();
302 unsigned int rate = 0;
303 unsigned int bits = 0;
305 src.seek(fmt_offset);
339 QList<Kwave::RecoverySource *> *repair_list =
340 new QList<Kwave::RecoverySource *>();
341 Q_ASSERT(repair_list);
342 if (!repair_list)
return false;
348 repair(repair_list, root, fmt_chunk, data_chunk);
364 case AF_BAD_NOT_IMPLEMENTED:
365 reason = i18n(
"Format or function is not implemented") +
366 _(
"\n(") + format_name +
_(
")");
369 reason = i18n(
"Out of memory");
372 reason = i18n(
"file header is damaged");
374 case AF_BAD_CODEC_TYPE:
375 reason = i18n(
"Invalid codec type") +
376 _(
"\n(") + format_name +
_(
")");
379 reason = i18n(
"Opening the file failed");
382 reason = i18n(
"Read access failed");
385 reason = i18n(
"Invalid sample format");
388 reason = i18n(
"internal libaudiofile error #%1: '%2'",
394 QString text= i18n(
"An error occurred while opening the file:\n'%1'",
401 AFframecount length = afGetFrameCount(fh, AF_DEFAULT_TRACK);
402 tracks = afGetVirtualChannels(fh, AF_DEFAULT_TRACK);
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)
411 case AF_SAMPFMT_TWOSCOMP:
414 case AF_SAMPFMT_UNSIGNED:
417 case AF_SAMPFMT_FLOAT:
420 case AF_SAMPFMT_DOUBLE:
428 int af_compression = afGetCompression(fh, AF_DEFAULT_TRACK);
446 if (!chunk)
continue;
453 QByteArray buffer(len + 1, 0x00);
455 src.read(buffer.data(), len);
458 value = QString::fromUtf8(buffer);
459 info.
set(prop, value);
470 src.read(reinterpret_cast<char *>(&count), 4);
471 count = qFromLittleEndian<quint32>(count);
476 for (
unsigned int i = 0; i < count; i++) {
477 quint32 data, index, position;
478 src.seek(cue_chunk->
dataStart() + 4 + ((6 * 4) * i));
491 src.read(reinterpret_cast<char *>(&data), 4);
492 index = qFromLittleEndian<quint32>(data);
495 src.read(reinterpret_cast<char *>(&data), 4);
498 src.read(reinterpret_cast<char *>(&data), 4);
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",
504 reinterpret_cast<const char *>(&data), 4).data());
507 src.read(reinterpret_cast<char *>(&data), 4);
510 qWarning(
"cue list entry %d has dwChunkStart != 0 -> skipped",
514 src.read(reinterpret_cast<char *>(&data), 4);
517 qWarning(
"cue list entry %d has dwBlockStart != 0 -> skipped",
522 src.read(reinterpret_cast<char *>(&data), 4);
523 position = qFromLittleEndian<quint32>(data);
526 QByteArray
name =
"";
528 parser.
findChunk(
"/RIFF:WAVE/LIST:adtl");
532 QListIterator<Kwave::RIFFChunk *> it(adtl_chunk->
subChunks());
533 while (it.hasNext()) {
535 labl_chunk = it.next();
544 if (labl_chunk->
name() !=
"labl")
continue;
548 src.read(reinterpret_cast<char *>(&data), 4);
549 labl_index = qFromLittleEndian<quint32>(data);
550 if (labl_index == index) {
556 Q_ASSERT(labl_chunk);
557 unsigned int len = labl_chunk->
length();
562 src.read(static_cast<char *>(name.data()), len);
563 if (name[name.count() - 1] !=
'\0')
569 if (!name.length()) {
575 QString str = QString::fromUtf8(name);
584 #if Q_BYTE_ORDER == Q_BIG_ENDIAN 585 afSetVirtualByteOrder(fh, AF_DEFAULT_TRACK, AF_BYTEORDER_BIGENDIAN);
587 afSetVirtualByteOrder(fh, AF_DEFAULT_TRACK, AF_BYTEORDER_LITTLEENDIAN);
589 afSetVirtualSampleFormat(fh, AF_DEFAULT_TRACK,
605 if (!fh)
return false;
608 const unsigned int tracks = dst.
tracks();
611 QVector<Kwave::Writer *>writers(tracks);
614 for (
unsigned int t = 0; t < tracks; t++)
619 afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, 1));
622 const unsigned int buffer_frames = (8 * 1024);
624 malloc(buffer_frames * frame_size));
626 if (!buffer)
return false;
632 unsigned int frames = buffer_frames;
634 unsigned int buffer_used = afReadFrames(fh,
635 AF_DEFAULT_TRACK, reinterpret_cast<char *>(buffer), frames);
638 if (!buffer_used)
break;
643 for (
unsigned int count = buffer_used; count; count--) {
644 for (
unsigned int track = 0; track < tracks; track++) {
654 Q_ASSERT(writer_fast[track]);
655 *(writer_fast[track]) << static_cast<sample_t>(s);
664 if (buffer) free(buffer);
670 QList<Kwave::RecoverySource *> *repair_list,
675 Q_ASSERT(repair_list);
676 if (!chunk)
return false;
678 if (!repair_list)
return false;
685 strncpy(buffer, chunk->
name().data(), 4);
688 buffer[4] = (length ) & 0xFF;
689 buffer[5] = (length >> 8) & 0xFF;
690 buffer[6] = (length >> 16) & 0xFF;
691 buffer[7] = (length >> 24) & 0xFF;
693 strncpy(&(buffer[8]), chunk->
format().data(), 4);
695 qDebug(
"[0x%08X-0x%08X] - main header '%s' (%s), len=%u",
696 offset, offset+11, chunk->
name().data(),
697 chunk->
format().data(), length);
701 qDebug(
"[0x%08X-0x%08X] - sub header '%s', len=%u",
702 offset, offset+7, chunk->
name().data(), length);
706 if (!repair)
return false;
707 repair_list->append(repair);
715 qDebug(
"[0x%08X-0x%08X] - restoring from offset 0x%08X (%u)",
719 if (!repair)
return false;
720 repair_list->append(repair);
743 Q_ASSERT(data_chunk);
744 if (!fmt_chunk || !data_chunk)
return false;
757 if (!new_fmt)
return false;
765 if (!new_data)
return false;
775 if (!chunk)
continue;
776 if (chunk->
name() ==
"fmt ")
continue;
777 if (chunk->
name() ==
"data")
continue;
778 if (chunk->
name() ==
"RIFF")
continue;
797 bool repaired =
repairChunk(repair_list, &new_root, offset);
static Kwave::Compression::Type fromAudiofile(int af_compression)
virtual unsigned int tracks() const Q_DECL_OVERRIDE
Kwave::WavPropertyMap m_property_map
virtual bool open(QWidget *widget, QIODevice &source) Q_DECL_OVERRIDE
Kwave::RIFFChunk * findMissingChunk(const QByteArray &name)
quint32 physStart() const
quint32 physLength() const
static int sorry(QWidget *widget, QString message, QString caption=QString())
virtual bool decode(QWidget *widget, Kwave::MultiWriter &dst) Q_DECL_OVERRIDE
Kwave::VirtualAudioFile * m_src_adapter
Kwave::FileProperty property(const QByteArray &chunk) const
virtual void open(Kwave::VirtualAudioFile *x, AFfilesetup setup)
QStringList m_known_chunks
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
quint32 dataStart() const
void set(FileProperty key, const QVariant &value)
virtual Kwave::Decoder * instance() Q_DECL_OVERRIDE
void setRate(double rate)
void setType(ChunkType type)
static int error(QWidget *widget, QString message, QString caption=QString())
unsigned int chunkCount(const QByteArray &path)
sample_index_t length() const
QList< Kwave::RIFFChunk * > RIFFChunkList
void setLength(sample_index_t length)
quint32 dataLength() const
void setTracks(unsigned int tracks)
Kwave::RIFFChunkList & subChunks()
virtual Kwave::MetaDataList & metaData()
#define REGISTER_MIME_TYPES
Kwave::RIFFChunk * findChunk(const QByteArray &path)
Kwave::MetaDataList toMetaDataList() const
static int warningContinueCancel(QWidget *widget, QString message, QString caption=QString(), const QString buttonContinue=QString(), const QString buttonCancel=QString(), const QString &dontAskAgainName=QString())
virtual ~WavDecoder() Q_DECL_OVERRIDE
void setBits(unsigned int bits)
#define REGISTER_COMPRESSION_TYPES
bool repair(QList< Kwave::RecoverySource *> *repair_list, Kwave::RIFFChunk *riff_chunk, Kwave::RIFFChunk *fmt_chunk, Kwave::RIFFChunk *data_chunk)
const QByteArray & name() const
bool repairChunk(QList< Kwave::RecoverySource *> *repair_list, Kwave::RIFFChunk *chunk, quint32 &offset)
#define SAMPLE_STORAGE_BITS
virtual void close() Q_DECL_OVERRIDE
const QByteArray & format() const
bool containsChunk(const QByteArray &chunk) const
QList< QByteArray > chunks() const