1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
#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;
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
|