in pedalboard/io/AudioFileInit.h [140:326]
inline void init_audio_file(
py::class_<AudioFile, std::shared_ptr<AudioFile>> &pyAudioFile) {
/**
* Important note: any changes made to the function signatures here should
* also be made to the constructor signatures of ReadableAudioFile and
* WriteableAudioFile to keep a consistent interface!
*/
pyAudioFile
.def(py::init<>()) // Make this class effectively abstract; we can only
// instantiate subclasses via __new__.
.def_static(
"__new__",
[](const py::object *, std::string filename, std::string mode) {
if (mode == "r") {
return std::make_shared<ReadableAudioFile>(filename);
} else if (mode == "w") {
throw py::type_error("Opening an audio file for writing requires "
"samplerate and num_channels arguments.");
} else {
throw py::type_error("AudioFile instances can only be opened in "
"read mode (\"r\") or write mode (\"w\").");
}
},
py::arg("cls"), py::arg("filename"), py::arg("mode") = "r",
"Open an audio file for reading.")
.def_static(
"__new__",
[](const py::object *, py::object filelike, std::string mode) {
if (mode == "r") {
if (!isReadableFileLike(filelike) &&
!tryConvertingToBuffer(filelike)) {
throw py::type_error(
"Expected either a filename, a file-like object (with "
"read, seek, seekable, and tell methods) or a memory view, "
"but received: " +
py::repr(filelike).cast<std::string>());
}
if (std::optional<py::buffer> buf =
tryConvertingToBuffer(filelike)) {
return std::make_shared<ReadableAudioFile>(
std::make_unique<PythonMemoryViewInputStream>(*buf,
filelike));
} else {
return std::make_shared<ReadableAudioFile>(
std::make_unique<PythonInputStream>(filelike));
}
} else if (mode == "w") {
throw py::type_error(
"Opening an audio file-like object for writing requires "
"samplerate and num_channels arguments.");
} else {
throw py::type_error("AudioFile instances can only be opened in "
"read mode (\"r\") or write mode (\"w\").");
}
},
py::arg("cls"), py::arg("file_like"), py::arg("mode") = "r",
"Open a file-like object for reading. The provided object must have "
"``read``, ``seek``, ``tell``, and ``seekable`` methods, and must "
"return binary data (i.e.: ``open(..., \"w\")`` or ``io.BytesIO``, "
"etc.).")
.def_static(
"__new__",
[](const py::object *, std::string filename, std::string mode,
std::optional<double> sampleRate, int numChannels, int bitDepth,
std::optional<std::variant<std::string, float>> quality) {
if (mode == "r") {
throw py::type_error(
"Opening an audio file for reading does not require "
"samplerate, num_channels, bit_depth, or quality arguments - "
"these parameters "
"will be read from the file.");
} else if (mode == "w") {
if (!sampleRate) {
throw py::type_error(
"Opening an audio file for writing requires a samplerate "
"argument to be provided.");
}
return std::make_shared<WriteableAudioFile>(
filename, *sampleRate, numChannels, bitDepth, quality);
} else {
throw py::type_error("AudioFile instances can only be opened in "
"read mode (\"r\") or write mode (\"w\").");
}
},
py::arg("cls"), py::arg("filename"), py::arg("mode") = "w",
py::arg("samplerate") = py::none(), py::arg("num_channels") = 1,
py::arg("bit_depth") = 16, py::arg("quality") = py::none())
.def_static(
"__new__",
[](const py::object *, py::object filelike, std::string mode,
std::optional<double> sampleRate, int numChannels, int bitDepth,
std::optional<std::variant<std::string, float>> quality,
std::optional<std::string> format) {
if (mode == "r") {
throw py::type_error(
"Opening a file-like object for reading does not require "
"samplerate, num_channels, bit_depth, or quality arguments - "
"these parameters "
"will be read from the file-like object.");
} else if (mode == "w") {
if (!sampleRate) {
throw py::type_error("Opening a file-like object for writing "
"requires a samplerate "
"argument to be provided.");
}
if (!isWriteableFileLike(filelike)) {
throw py::type_error(
"Expected either a filename or a file-like object (with "
"write, seek, seekable, and tell methods), but received: " +
filelike.attr("__repr__")().cast<std::string>());
}
auto stream = std::make_unique<PythonOutputStream>(filelike);
if (!format && !stream->getFilename()) {
throw py::type_error(
"Unable to infer audio file format for writing. Expected "
"either a \".name\" property on the provided file-like "
"object (" +
filelike.attr("__repr__")().cast<std::string>() +
") or an explicit file format passed as the \"format=\" "
"argument.");
}
return std::make_shared<WriteableAudioFile>(
format.value_or(""), std::move(stream), *sampleRate,
numChannels, bitDepth, quality);
} else {
throw py::type_error("AudioFile instances can only be opened in "
"read mode (\"r\") or write mode (\"w\").");
}
},
py::arg("cls"), py::arg("file_like"), py::arg("mode") = "w",
py::arg("samplerate") = py::none(), py::arg("num_channels") = 1,
py::arg("bit_depth") = 16, py::arg("quality") = py::none(),
py::arg("format") = py::none())
.def_static(
"encode",
[](const py::array samples, double sampleRate, std::string format,
int numChannels, int bitDepth,
std::optional<std::variant<std::string, float>> quality) {
juce::MemoryBlock outputBlock;
auto audioFile = std::make_unique<WriteableAudioFile>(
format,
std::make_unique<juce::MemoryOutputStream>(outputBlock, false),
sampleRate, numChannels, bitDepth, quality);
audioFile->write(samples);
audioFile->close();
return py::bytes((const char *)outputBlock.getData(),
outputBlock.getSize());
},
py::arg("samples"), py::arg("samplerate"), py::arg("format"),
py::arg("num_channels") = 1, py::arg("bit_depth") = 16,
py::arg("quality") = py::none(),
R"(
Encode an audio buffer to a Python :class:`bytes` object.
This function will encode an entire audio buffer at once and return a :class:`bytes`
object representing the bytes of the resulting audio file.
This function produces identical output to the following code::
buf = io.BytesIO()
with AudioFile(buf, "w", samplerate, num_channels, bit_depth, format, quality) as f:
f.write(samples)
result = buf.getvalue()
However, this function is much more efficient than the above code, as it writes
to an in-memory buffer in C++ and avoids interacting with Python at all during the
encoding process. This allows Python's Global Interpreter Lock (GIL) to be
released, which also makes this method much more performant in multi-threaded
programs.
.. warning::
This function will encode the entire audio buffer at once, and may consume a
large amount of memory if the input audio buffer is large.
To avoid running out of memory with arbitrary-length inputs, it is
recommended to stream the output into a file or file-like object by using
:class:`AudioFile` class in write (``"w"``) mode instead.
)");
}