JS8Call-Improved master
Loading...
Searching...
No Matches
BWFFile.h
1#ifndef BWF_FILE_HPP__
2#define BWF_FILE_HPP__
3
4#include "JS8_Include/pimpl_h.h"
5
6#include <QByteArray>
7#include <QFile>
8#include <QMap>
9
10#include <array>
11
12class QObject;
13class QString;
14class QAudioFormat;
15
16//
17// BWFFile - Broadcast Wave Format File (a.k.a. WAV file)
18//
19// The BWF file format is a backward compatible variation of the
20// Microsoft WAV file format. It contains an extra chunk with id
21// 'bext' that contains metadata defined by the EBU in:
22//
23// https://tech.ebu.ch/docs/tech/tech3285.pdf
24//
25// Also relevant is the recommendation document:
26//
27// https://tech.ebu.ch/docs/r/r098.pdf
28//
29// which suggests a format to the free text coding history field.
30//
31// This class also supports the LIST-INFO chunk type which also allows
32// metadata to be added to a WAV file, the defined INFO tag ids are
33// documented here:
34//
35// http://bwfmetaedit.sourceforge.net/listinfo.html
36//
37// These ids are not enforced but they are recommended as most
38// operating systems and audio applications recognize some or more of
39// them. Notably Microsoft Windows is not one of the operating systems
40// that does :( In fact there seems to be no documented metadata
41// tagging format that Windows Explorer recognizes.
42//
43// Changes to the 'bext' fields and the LIST-INFO dictionary may be
44// made right up until the file is closed as the relevant chunks are
45// saved to the end of the file after the end of the sample data.
46//
47// This class emulates the QFile class, in fact it uses a QFile object
48// instance internally and forwards many of its operations directly to
49// it.
50//
51// BWFFile is a QIODevice subclass and the implementation provides
52// access to the audio sample data contained in the BWF file as if
53// only that data were in the file. I.e. the first sample is at file
54// offset zero and the size of the file is the size of the sample
55// data. The headers, trailers and metadata are hidden but can be
56// accessed by the operations below.
57//
58class BWFFile : public QIODevice {
59 Q_OBJECT
60 public:
61 using FileHandleFlags = QFile::FileHandleFlags;
62 using Permissions = QFile::Permissions;
63 using FileError = QFile::FileError;
64 using MemoryMapFlags = QFile::MemoryMapFlags;
65 using InfoDictionary = QMap<std::array<char, 4>, QByteArray>;
66 using UMID = std::array<quint8, 64>;
67
68 explicit BWFFile(QAudioFormat const &, QObject *parent = nullptr);
69 explicit BWFFile(QAudioFormat const &, QString const &name,
70 QObject *parent = nullptr);
71
72 // The InfoDictionary should contain valid WAV format LIST-INFO
73 // identifiers as keys, a list of them can be found here:
74 //
75 // http://bwfmetaedit.sourceforge.net/listinfo.html
76 //
77 // For files opened for ReadOnly access the dictionary is not
78 // written to the file. For files opened ReadWrite, any existing
79 // LIST-INFO tags will be merged into the dictionary when the file
80 // is opened and if the file is modified the merged dictionary will
81 // be written back to the file.
82 //
83 // Note that the sample data may no be in the native endian, it is
84 // the callers responsibility to do any required endian
85 // conversions. The internal data is always in native endian with
86 // conversions being handled automatically. Use the BWF::format()
87 // operation to access the format including the
88 // QAudioFormat::byteOrder() operation to determine the data byte
89 // ordering.
90 //
91 explicit BWFFile(QAudioFormat const &, QString const &name,
92 InfoDictionary const &, QObject *parent = nullptr);
93
94 ~BWFFile();
95 QAudioFormat const &format() const;
96 InfoDictionary &list_info();
97
98 //
99 // Broadcast Audio Extension fields
100 //
101 // If any of these modifiers are called then a "bext" chunk will be
102 // written to the file if the file is writeable and the sample data
103 // is modified.
104 //
105 enum class BextVersion : quint16 { v_0, v_1, v_2 };
106 BextVersion bext_version() const;
107 void bext_version(BextVersion = BextVersion::v_2);
108
109 QByteArray bext_description() const;
110 void bext_description(QByteArray const &); // max 256 bytes
111
112 QByteArray bext_originator() const;
113 void bext_originator(QByteArray const &); // max 32 bytes
114
115 QByteArray bext_originator_reference() const;
116 void bext_originator_reference(QByteArray const &); // max 32 bytes
117
118 QDateTime bext_origination_date_time() const;
119 void bext_origination_date_time(QDateTime const &); // 1s resolution
120
121 quint64 bext_time_reference() const;
122 void bext_time_reference(quint64); // samples since midnight at start
123
124 UMID bext_umid() const; // bext version >= 1 only
125 void bext_umid(UMID const &);
126
127 quint16 bext_loudness_value() const;
128 void bext_loudness_value(quint16); // bext version >= 2 only
129
130 quint16 bext_loudness_range() const;
131 void bext_loudness_range(quint16); // bext version >= 2 only
132
133 quint16 bext_max_true_peak_level() const;
134 void bext_max_true_peak_level(quint16); // bext version >= 2 only
135
136 quint16 bext_max_momentary_loudness() const;
137 void bext_max_momentary_loudness(quint16); // bext version >= 2 only
138
139 quint16 bext_max_short_term_loudness() const;
140 void bext_max_short_term_loudness(quint16); // bext version >= 2 only
141
142 QByteArray bext_coding_history() const;
143 void bext_coding_history(QByteArray const &); // See EBU R 98
144
145 // Emulate QFile interface
146 bool open(OpenMode) override;
147 bool open(FILE *, OpenMode, FileHandleFlags = QFile::DontCloseHandle);
148 bool open(int fd, OpenMode, FileHandleFlags = QFile::DontCloseHandle);
149 bool copy(QString const &new_name);
150 bool exists() const;
151 bool link(QString const &link_name);
152 bool remove();
153 bool rename(QString const &new_name);
154 void setFileName(QString const &name);
155 QString symLinkTarget() const;
156 QString fileName() const;
157 Permissions permissions() const;
158
159 // Resize is of the sample data portion, header and trailer chunks
160 // are excess to the given size
161 bool resize(qint64 new_size);
162
163 bool setPermissions(Permissions permissions);
164 FileError error() const;
165 bool flush();
166 int handle() const;
167
168 // The mapping offset is relative to the start of the sample data
169 uchar *map(qint64 offset, qint64 size, MemoryMapFlags = QFile::NoOptions);
170 bool unmap(uchar *address);
171
172 void unsetError();
173
174 //
175 // QIODevice implementation
176 //
177
178 // The size returned is of the sample data only, header and trailer
179 // chunks are hidden and handled internally
180 qint64 size() const override;
181
182 bool isSequential() const override;
183
184 quint16 bitsPerSample() const;
185 quint16 blockAlign() const;
186
187 // The reset operation clears the 'bext' and LIST-INFO as if they
188 // were never supplied. If the file is writable the 'bext' and
189 // LIST-INFO chunks will not be written making the resulting file a
190 // lowest common denominator WAV file.
191 bool reset() override;
192
193 // Seek offsets are relative to the start of the sample data
194 bool seek(qint64) override;
195
196 // this can fail due to updating header issues, errors are ignored
197 void close() override;
198
199 protected:
200 qint64 readData(char *data, qint64 max_size) override;
201 qint64 writeData(char const *data, qint64 max_size) override;
202
203 private:
204 class impl;
205 pimpl<impl> m_;
206};
207
208#endif
Definition BWFFile.cpp:107
Definition pimpl_h.h:16