mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-29 23:58:39 -05:00
4a33a696ba
The BWFFile class correctly indicates errors like file access issues and problems reading or writing headers using the BWFFile::error() and BWFFile::errorString() operations. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6751 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
211 lines
7.1 KiB
C++
211 lines
7.1 KiB
C++
#ifndef BWF_FILE_HPP__
|
|
#define BWF_FILE_HPP__
|
|
|
|
#include <array>
|
|
|
|
#include <QFile>
|
|
#include <QMap>
|
|
#include <QByteArray>
|
|
|
|
#include "pimpl_h.hpp"
|
|
|
|
class QObject;
|
|
class QString;
|
|
class QAudioFormat;
|
|
|
|
//
|
|
// BWFFile - Broadcast Wave Format File (a.k.a. WAV file)
|
|
//
|
|
// The BWF file format is a backward compatible variation of the
|
|
// Microsoft WAV file format. It contains an extra chunk with id
|
|
// 'bext' that contains metadata defined by the EBU in:
|
|
//
|
|
// https://tech.ebu.ch/docs/tech/tech3285.pdf
|
|
//
|
|
// Also relevant is the recommendation document:
|
|
//
|
|
// https://tech.ebu.ch/docs/r/r098.pdf
|
|
//
|
|
// which suggests a format to the free text coding history field.
|
|
//
|
|
// This class also supports the LIST-INFO chunk type which also allows
|
|
// metadata to be added to a WAV file, the defined INFO tag ids are
|
|
// documented here:
|
|
//
|
|
// http://bwfmetaedit.sourceforge.net/listinfo.html
|
|
//
|
|
// These ids are not enforced but they are recommended as most
|
|
// operating systems and audio applications recognize some or more of
|
|
// them. Notably Microsoft Windows is not one of the operating systems
|
|
// that does :( In fact there seems to be no documented metadata
|
|
// tagging format that Windows Explorer recognizes.
|
|
//
|
|
// Changes to the 'bext' fields and the LIST-INFO dictionary may be
|
|
// made right up until the file is closed as the relevant chunks are
|
|
// saved to the end of the file after the end of the sample data.
|
|
//
|
|
// This class emulates the QFile class, in fact it uses a QFile object
|
|
// instance internally and forwards many of its operations directly to
|
|
// it.
|
|
//
|
|
// BWFFile is a QIODevice subclass and the implementation provides
|
|
// access to the audio sample data contained in the BWF file as if
|
|
// only that data were in the file. I.e. the first sample is at file
|
|
// offset zero and the size of the file is the size of the sample
|
|
// data. The headers, trailers and metadata are hidden but can be
|
|
// accessed by the operations below.
|
|
//
|
|
class BWFFile
|
|
: public QIODevice
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
using FileHandleFlags = QFile::FileHandleFlags;
|
|
using Permissions = QFile::Permissions;
|
|
using FileError = QFile::FileError;
|
|
using MemoryMapFlags = QFile::MemoryMapFlags;
|
|
using InfoDictionary = QMap<std::array<char, 4>, QByteArray>;
|
|
using UMID = std::array<quint8, 64>;
|
|
|
|
explicit BWFFile (QAudioFormat const&, QObject * parent = nullptr);
|
|
explicit BWFFile (QAudioFormat const&, QString const& name,
|
|
QObject * parent = nullptr);
|
|
|
|
// The InfoDictionary should contain valid WAV format LIST-INFO
|
|
// identifiers as keys, a list of them can be found here:
|
|
//
|
|
// http://bwfmetaedit.sourceforge.net/listinfo.html
|
|
//
|
|
// For files opened for ReadOnly access the dictionary is not
|
|
// written to the file. For files opened ReadWrite, any existing
|
|
// LIST-INFO tags will be merged into the dictionary when the file
|
|
// is opened and if the file is modified the merged dictionary will
|
|
// be written back to the file.
|
|
//
|
|
// Note that the sample data may no be in the native endian, it is
|
|
// the callers responsibility to do any required endian
|
|
// conversions. The internal data is always in native endian with
|
|
// conversions being handled automatically. Use the BWF::format()
|
|
// operation to access the format including the
|
|
// QAudioFormat::byteOrder() operation to determine the data byte
|
|
// ordering.
|
|
//
|
|
explicit BWFFile (QAudioFormat const&, QString const& name,
|
|
InfoDictionary const&, QObject * parent = nullptr);
|
|
|
|
~BWFFile ();
|
|
QAudioFormat const& format () const;
|
|
InfoDictionary& list_info ();
|
|
|
|
//
|
|
// Broadcast Audio Extension fields
|
|
//
|
|
// If any of these modifiers are called then a "bext" chunk will be
|
|
// written to the file if the file is writeable and the sample data
|
|
// is modified.
|
|
//
|
|
enum class BextVersion : quint16 {v_0, v_1, v_2};
|
|
BextVersion bext_version () const;
|
|
void bext_version (BextVersion = BextVersion::v_2);
|
|
|
|
QByteArray bext_description () const;
|
|
void bext_description (QByteArray const&); // max 256 bytes
|
|
|
|
QByteArray bext_originator () const;
|
|
void bext_originator (QByteArray const&); // max 32 bytes
|
|
|
|
QByteArray bext_originator_reference () const;
|
|
void bext_originator_reference (QByteArray const&); // max 32 bytes
|
|
|
|
QDateTime bext_origination_date_time () const;
|
|
void bext_origination_date_time (QDateTime const&); // 1s resolution
|
|
|
|
quint64 bext_time_reference () const;
|
|
void bext_time_reference (quint64); // samples since midnight at start
|
|
|
|
UMID bext_umid () const; // bext version >= 1 only
|
|
void bext_umid (UMID const&);
|
|
|
|
quint16 bext_loudness_value () const;
|
|
void bext_loudness_value (quint16); // bext version >= 2 only
|
|
|
|
quint16 bext_loudness_range () const;
|
|
void bext_loudness_range (quint16); // bext version >= 2 only
|
|
|
|
quint16 bext_max_true_peak_level () const;
|
|
void bext_max_true_peak_level (quint16); // bext version >= 2 only
|
|
|
|
quint16 bext_max_momentary_loudness () const;
|
|
void bext_max_momentary_loudness (quint16); // bext version >= 2 only
|
|
|
|
quint16 bext_max_short_term_loudness () const;
|
|
void bext_max_short_term_loudness (quint16); // bext version >= 2 only
|
|
|
|
QByteArray bext_coding_history () const;
|
|
void bext_coding_history (QByteArray const&); // See EBU R 98
|
|
|
|
|
|
// Emulate QFile interface
|
|
bool open (OpenMode) override;
|
|
bool open (FILE *, OpenMode, FileHandleFlags = QFile::DontCloseHandle);
|
|
bool open (int fd, OpenMode, FileHandleFlags = QFile::DontCloseHandle);
|
|
bool copy (QString const& new_name);
|
|
bool exists () const;
|
|
bool link (QString const& link_name);
|
|
bool remove ();
|
|
bool rename (QString const& new_name);
|
|
void setFileName (QString const& name);
|
|
QString symLinkTarget () const;
|
|
QString fileName () const;
|
|
Permissions permissions () const;
|
|
|
|
// Resize is of the sample data portion, header and trailer chunks
|
|
// are excess to the given size
|
|
bool resize (qint64 new_size);
|
|
|
|
bool setPermissions (Permissions permissions);
|
|
FileError error () const;
|
|
bool flush ();
|
|
int handle () const;
|
|
|
|
// The mapping offset is relative to the start of the sample data
|
|
uchar * map (qint64 offset, qint64 size,
|
|
MemoryMapFlags = QFile::NoOptions);
|
|
bool unmap (uchar * address);
|
|
|
|
void unsetError ();
|
|
|
|
|
|
//
|
|
// QIODevice implementation
|
|
//
|
|
|
|
// The size returned is of the sample data only, header and trailer
|
|
// chunks are hidden and handled internally
|
|
qint64 size () const override;
|
|
|
|
bool isSequential () const override;
|
|
|
|
// The reset operation clears the 'bext' and LIST-INFO as if they
|
|
// were never supplied. If the file is writable the 'bext' and
|
|
// LIST-INFO chunks will not be written making the resulting file a
|
|
// lowest common denominator WAV file.
|
|
bool reset () override;
|
|
|
|
// Seek offsets are relative to the start of the sample data
|
|
bool seek (qint64) override;
|
|
|
|
// this can fail due to updating header issues, errors are ignored
|
|
void close () override;
|
|
|
|
protected:
|
|
qint64 readData (char * data, qint64 max_size) override;
|
|
qint64 writeData (char const* data, qint64 max_size) override;
|
|
|
|
private:
|
|
class impl;
|
|
pimpl<impl> m_;
|
|
};
|
|
|
|
#endif
|