
libbw64 – ITU-R BS.2088 Library¶
Getting Started¶
Requirements and dependencies¶
- compiler with C++11 support
- CMake build system (version 3.5 or later)
Installation¶
Just copy the content of the include
directory to your project or add the
repository as a Git submodule to your project and make sure, that the bw64
folder is in your PATH
, that the header files can be found by the compiler.
Alternatively clone the Git repository and install the library system wide using the CMake build system. See the following instructions for *nix systems.
git clone git@github.com:irt-open-source/libbw64.git
cd libbw64
mkdir build && cd build
cmake ..
make
make install
Tutorial¶
In this tutorial we will create a simple application which adjusts the level of
all channels in a BW64 file and writes the output to a new file. We assume that
the include
path of the library is added to the PATH
.
Basic structure¶
Let us start with the basic structure of our programme.
#include <iostream>
#include <bw64/bw64.hpp>
const unsigned int BLOCK_SIZE = 4096;
int main(int argc, char const* argv[]) {
if (argc != 2) {
std::cout << "usage: " << argv[0] << " [INFILE]" << std::endl;
exit(1);
}
auto inFile = bw64::readFile(argv[1]);
std::vector<float> buffer(BLOCK_SIZE * inFile->channels());
while (!inFile->eof()) {
auto readFrames = inFile->read(&buffer[0], BLOCK_SIZE);
// TODO: process samples
}
return 0;
}
We include the header and open the file we want to read using the
bw64::readFile()
function and add a while
loop in which we read
the samples in a block buffer. The bw64::Bw64Reader::read()
expects
a float array and the number of frames, the function should try to read. One
frame contains one sample for each channel. So if the
bw64::Bw64Reader::read()
function should try to read N
frames,
the buffer must be at least N * CHANNELS
big. The samples are written into
the buffer in a channel interleaved order, as illustrated in the following
table.
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Channel | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
Sample | 0 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 |
Note that we don’t need to close our file at the end of the programme. This will
be done automatically when inFile
is destroyed at the end of the programme.
Write files¶
As a next step we also prepare our output file to write the samples.
#include <iostream>
#include <bw64/bw64.hpp>
const unsigned int BLOCK_SIZE = 4096;
int main(int argc, char const* argv[]) {
if (argc != 3) {
std::cout << "usage: " << argv[0] << " [INFILE] [OUTFILE]" << std::endl;
exit(1);
}
auto inFile = bw64::readFile(argv[1]);
auto outFile =
bw64::writeFile(argv[2], inFile->channels(), inFile->sampleRate(),
inFile->bitDepth(), inFile->chnaChunk(), inFile->axmlChunk());
std::vector<float> buffer(BLOCK_SIZE * inFile->channels());
while (!inFile->eof()) {
auto readFrames = inFile->read(&buffer[0], BLOCK_SIZE);
// TODO: process samples
outFile->write(&buffer[0], readFrames);
}
return 0;
}
We use the information from the input file we opened to initialize our output
file. We also need to add the chna
and axml
chunks from the input file
to the output file during initialization. We can directly use the buffer we
passed to the bw64::Bw64Reader::read()
in the
bw64::Bw64Writer::write()
function to write the unmodified samples.
So also the bw64::Bw64Writer::write()
expects the order of the
samples to be interleaved as described above.
Add signal processing¶
To make our example complete, let us add some basic signal processing and adjust the gain of all channels.
#include <iostream>
#include <algorithm>
#include <functional>
#include <bw64/bw64.hpp>
const unsigned int BLOCK_SIZE = 4096;
int main(int argc, char const* argv[]) {
if (argc != 4) {
std::cout << "usage: " << argv[0] << " [INFILE] [OUTFILE] [GAIN]"
<< std::endl;
exit(1);
}
auto inFile = bw64::readFile(argv[1]);
auto outFile =
bw64::writeFile(argv[2], inFile->channels(), inFile->sampleRate(),
inFile->bitDepth(), inFile->chnaChunk(), inFile->axmlChunk());
std::vector<float> buffer(BLOCK_SIZE * inFile->channels());
float gain = atof(argv[3]);
while (!inFile->eof()) {
auto readFrames = inFile->read(&buffer[0], BLOCK_SIZE);
std::transform(buffer.begin(), buffer.end(), buffer.begin(),
[gain](float value) { return value * gain; });
outFile->write(&buffer[0], readFrames);
}
return 0;
}
Main functions¶
bw64::Bw64Reader
and bw64::Bw64Writer
classes usually
should not be created manually. Instead the two builder functions to either read
or write a file should be used.
-
std::unique_ptr<Bw64Reader>
bw64
::
readFile
(const std::string &filename)¶ Open a BW64 file for reading.
Convenience function to open a BW64 file for reading.
- Parameters
filename
: path of the file to read
- Return
unique_ptr
to a Bw64Reader instance that is ready to read samples.
Open a BW64 file for writing.
Convenience function to open a new BW64 file for writing, adding
axml
andchna
chunks.If passed to this function, the
axml
andchna
chunks will be added to the BW64 file before the actual data chunk, which is the recommended practice if all components are already known before writing a file.- Return
unique_ptr
to a Bw64Writer instance that is ready to write samples.- Parameters
filename
: path of the file to writechannels
: the channel count of the new filesampleRate
: the samplerate of the new filebitDepth
: target bitdepth of the new filechnaChunk
: Channel allocation chunk to include, if anyaxmlChunk
: AXML chunk to include, if any
BW64 file classes¶
-
class
Bw64Reader
¶ Representation of a BW64 file.
Normally, you will create an instance of this class using bw64::readFile().
This is a RAII class, meaning that the file will be openend and initialized (parse header, format etc.) on construction, and closed on destruction.
Public Functions
-
Bw64Reader
(const char *filename)¶ Open a new BW64 file for reading.
Opens a new BW64 file for reading, parses the whole file to read the format and identify all chunks in it.
- Note
- For convenience, you might consider using the
readFile
helper function.
-
~Bw64Reader
()¶ Bw64Reader destructor.
The destructor will automatically close the file opened in the constructor.
-
uint32_t
fileFormat
() const¶ Get file format (RIFF, BW64 or RF64)
-
uint32_t
fileSize
() const¶ Get file size.
-
uint16_t
formatTag
() const¶ Get format tag.
-
uint16_t
channels
() const¶ Get number of channels.
-
uint32_t
sampleRate
() const¶ Get sample rate.
-
uint16_t
bitDepth
() const¶ Get bit depth.
-
uint64_t
numberOfFrames
() const¶ Get number of frames.
-
uint16_t
blockAlignment
() const¶ Get block alignment.
-
std::shared_ptr<DataSize64Chunk>
ds64Chunk
() const¶ Get ‘ds64’ chunk.
- Return
std::shared_ptr
to DataSize64Chunk if present and otherwise a nullptr.
-
std::shared_ptr<FormatInfoChunk>
formatChunk
() const¶ Get ‘fmt ‘ chunk.
- Return
std::shared_ptr
to FormatInfoChunk if present and otherwise a nullptr.
-
std::shared_ptr<DataChunk>
dataChunk
() const¶ Get ‘data’ chunk.
- Warning
- This method usually should not be called, as the acces to the DataChunk is handled seperately by the Bw64Reader class .
- Return
std::shared_ptr
to DataChunk if present and otherwise a nullptr.
-
std::shared_ptr<ChnaChunk>
chnaChunk
() const¶ Get ‘chna’ chunk.
- Return
std::shared_ptr
to ChnaChunk if present and otherwise a nullptr.
-
std::shared_ptr<AxmlChunk>
axmlChunk
() const¶ Get ‘axml’ chunk.
- Return
std::shared_ptr
to AxmlChunk if present and otherwise a nullptr.
-
std::vector<ChunkHeader>
chunks
() const¶ Get list of all chunks which are present in the file.
-
bool
hasChunk
(uint32_t id) const¶ Check if a chunk with the given id is present.
-
void
seek
(int32_t offset, std::ios_base::seekdir way = std::ios::beg)¶ Seek a frame position in the DataChunk.
-
template <typename T, typename = std::enable_if<std::is_floating_point<T>::value>>
uint64_tread
(T *outBuffer, uint64_t frames)¶ Read frames from dataChunk.
- Return
- number of frames read
- Parameters
outBuffer
: Buffer to write the samples toframes
: Number of frames to read
-
uint64_t
tell
()¶ Tell the current frame position of the dataChunk.
- Return
- current frame position of the dataChunk
-
bool
eof
()¶ Check if end of data is reached.
- Return
true
if end of data is reached and otherwisefalse
-
-
class
Bw64Writer
¶ BW64 Writer class.
Normally, you will create an instance of this class using bw64::writeFile().
This is a RAII class, meaning that the file will be openend and initialized (required headers etc.) on construction, and closed and finalized (writing chunk sizes etc.) on destruction.
Public Functions
Open a new BW64 file for writing.
Opens a new BW64 file for writing, initializes everything up to the
data
chunk. Afterwards, you may write interleaved audio samples to this file.If you need any chunks to appear
before the data chunk, include them in theadditionalChunks
. They will be written directly after opening the file.- Warning
- If the file already exists it will be overwritten.
- Note
- For convenience, you might consider using the
writeFile
helper function.
-
~Bw64Writer
()¶ Finalize file.
This destructor will write all yet-to-be-written chunks to the file and will also finalize all required information, i.e. the final chunk sizes etc.
-
uint16_t
formatTag
() const¶ Get format tag.
-
uint16_t
channels
() const¶ Get number of channels.
-
uint32_t
sampleRate
() const¶ Get sample rate.
-
uint16_t
bitDepth
() const¶ Get bit depth.
-
uint64_t
framesWritten
() const¶ Get number of frames.
-
bool
isBw64File
()¶ Check if file is bigger than 4GB and therefore a BW64 file.
-
uint32_t
chunkSizeForHeader
(uint32_t id)¶ Get the chunk size for header.
-
uint64_t
riffChunkSize
()¶ Calculate riff chunk size.
-
void
writeRiffHeader
()¶ Write RIFF header.
-
void
finalizeRiffChunk
()¶ Update RIFF header.
Write chunk template.
Overwrite chunk template.
-
template <typename T, typename = std::enable_if<std::is_floating_point<T>::value>>
uint64_twrite
(T *inBuffer, uint64_t frames)¶ Write frames to dataChunk.
- Return
- number of frames written
- Parameters
inBuffer
: Buffer to write the samples toframes
: Number of frames to write
Chunks¶
-
class
Chunk
¶ RIFF chunk base class.
Subclassed by bw64::AxmlChunk, bw64::ChnaChunk, bw64::DataChunk, bw64::DataSize64Chunk, bw64::FormatInfoChunk, bw64::JunkChunk, bw64::UnknownChunk
-
class
FormatInfoChunk
: public bw64::Chunk¶ Class representation of a FormatInfoChunk.
Public Functions
Simple FormatInfoChunk constructor.
- Parameters
channels
: number of channelssampleRate
: sample rate of the audio databitDepth
: bit depth used in fileextraData
: custom ExtraData (optional, nullptr if not custom)
-
uint32_t
id
() const¶ Get FourCC id.
-
uint64_t
size
() const¶ Get the size of the chunk.
-
uint16_t
formatTag
() const¶ FormatTag getter.
-
uint16_t
channelCount
() const¶ ChannelCount getter.
-
uint32_t
sampleRate
() const¶ SampleRate getter.
-
uint32_t
bytesPerSecond
() const¶ BytesPerSecond getter.
-
uint16_t
blockAlignment
() const¶ BlockAlignment getter.
-
uint16_t
bitsPerSample
() const¶ BitsPerSample getter.
-
void
write
(std::ostream &stream) const¶ Write the chunk to a stream.
-
class
ExtraData
¶ Class representation of the ExtraData of a FormatInfoChunk.
Public Functions
-
ExtraData
(uint16_t validBitsPerSample, uint32_t dwChannelMask, uint16_t subFormat, std::string subFormatString)¶ ExtraData constructor.
-
uint16_t
validBitsPerSample
() const¶ ValidBitsPerSample getter.
-
uint32_t
dwChannelMask
() const¶ DwChannelMask getter.
-
uint16_t
subFormat
() const¶ SubFormat getter.
-
std::string
subFormatString
() const¶ SubFormatString getter.
-
-
class
DataChunk
: public bw64::Chunk¶ Class representation of a DataChunk.
Public Functions
-
uint32_t
id
() const¶ Get FourCC id.
-
uint64_t
size
() const¶ Get the size of the chunk.
-
void
write
(std::ostream&) const¶ Not to be used write chunk to stream.
- Warning
- As the data chunk is usually not written in one piece the override for this function is not used. Using this method will throw an exception.
-
uint32_t
-
class
DataSize64Chunk
: public bw64::Chunk¶ Class representation of a DataSize64 chunk.
Public Functions
-
DataSize64Chunk
(uint64_t bw64Size = 0, uint64_t dataSize = 0, std::map<uint32_t, uint64_t> table = std::map<uint32_t, uint64_t>())¶ DataSize64Chunk constructor.
-
uint32_t
id
() const¶ Get FourCC id.
-
uint64_t
size
() const¶ Get the size of the chunk.
-
uint64_t
bw64Size
() const¶ Bw64Size getter.
-
uint64_t
dataSize
() const¶ DataSize getter.
-
uint64_t
dummySize
() const¶ DummySize getter.
-
uint32_t
tableLength
() const¶ TableLength getter.
-
void
bw64Size
(uint64_t size)¶ Bw64Size setter.
-
void
dataSize
(uint64_t size)¶ DataSize setter.
-
void
dummySize
(uint64_t size)¶ DummySize setter.
-
const std::map<uint32_t, uint64_t> &
table
() const¶ Get table.
-
bool
hasChunkSize
(uint32_t id) const¶ Has chunkSize for id.
-
uint64_t
getChunkSize
(uint32_t id) const¶ Get chunkSize for id.
-
void
setChunkSize
(uint32_t id, uint64_t size)¶ Set or add a ChunkSize.
-
void
removeChunkSize
(uint32_t id)¶ Remove a ChunkSize from table.
-
void
clearChunkSizeTable
()¶ Clear ChunkSize table.
-
void
write
(std::ostream &stream) const¶ Write the chunk to a stream.
-
The libbw64 library is a
lightweight C++ header only library to read and write BW64 files. BW64 is
standardised as Recommendation ITU-R BS.2088 and the successor of RF64. So it
already contains necessary extensions to support files which are bigger than 4
GB. Apart from that an BW64 file is able to contain the ADM metadata and link it
with the audio tracks in the file. To do that a BW64 specifies two new RIFF
chunks – the axml
chunk and the chna
chunk. To parse, create, modify and
write the ADM metadata in the axml
chunk you may use the libadm library.
Features¶
- no dependencies
- support file sizes bigger than 4 GB (
ds64
chunk) - read and write
axml
andchna
chunks - 16, 24, and 32 bit integer file formats
Acknowledgement¶
This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreement No 687645.