in pedalboard/io/WriteableAudioFile.h [935:1148]
inline void init_writeable_audio_file(
py::module &m,
py::class_<WriteableAudioFile, AudioFile,
std::shared_ptr<WriteableAudioFile>> &pyWriteableAudioFile) {
pyWriteableAudioFile
.def(py::init([](std::string filename, double sampleRate, int numChannels,
int bitDepth,
std::optional<std::variant<std::string, float>> quality)
-> WriteableAudioFile * {
// This definition is only here to provide nice docstrings.
throw std::runtime_error(
"Internal error: __init__ should never be called, as this "
"class implements __new__.");
}),
py::arg("filename"), py::arg("samplerate"),
py::arg("num_channels") = 1, py::arg("bit_depth") = 16,
py::arg("quality") = py::none())
.def(py::init(
[](py::object filelike, double sampleRate, int numChannels,
int bitDepth,
std::optional<std::variant<std::string, float>> quality,
std::optional<std::string> format) -> WriteableAudioFile * {
// This definition is only here to provide nice docstrings.
throw std::runtime_error(
"Internal error: __init__ should never be called, as this "
"class implements __new__.");
}),
py::arg("file_like"), py::arg("samplerate"),
py::arg("num_channels") = 1, py::arg("bit_depth") = 16,
py::arg("quality") = py::none(), py::arg("format") = py::none())
.def_static(
"__new__",
[](const py::object *, std::string filename,
std::optional<double> sampleRate, int numChannels, int bitDepth,
std::optional<std::variant<std::string, float>> quality) {
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);
},
py::arg("cls"), py::arg("filename"),
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::optional<double> sampleRate, int numChannels, int bitDepth,
std::optional<std::variant<std::string, float>> quality,
std::optional<std::string> format) {
if (!sampleRate) {
throw py::type_error(
"Opening an audio file 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: " +
py::repr(filelike).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 (" +
py::repr(filelike).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);
},
py::arg("cls"), py::arg("file_like"),
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(
"write",
[](WriteableAudioFile &file, py::array samples) {
file.write(samples);
},
py::arg("samples").noconvert(),
"Encode an array of audio data and write "
"it to this file. The number of channels in the array must match the "
"number of channels used to open the file. The array may contain "
"audio in any shape. If the file's bit depth or format does not "
"match the provided data type, the audio will be automatically "
"converted.\n\n"
"Arrays of type int8, int16, int32, float32, and float64 are "
"supported. If an array of an unsupported ``dtype`` is provided, a "
"``TypeError`` will be raised.\n\n"
".. warning::\n If an array of shape ``(num_channels, "
"num_channels)`` is passed to this method before any other audio "
"data is provided, an exception will be thrown, as the method will "
"not be able to infer which dimension of the input corresponds to "
"the number of channels and which dimension corresponds to the "
"number of samples.\n\n To avoid this, first call this method "
"with an array where the number of samples does not match the "
"number of channels.\n\n The channel layout from the most "
"recently "
"provided input will be cached on the :py:class:`WritableAudioFile` "
"object and will be used if necessary to disambiguate the array "
"layout:\n\n"
" .. code-block:: python\n\n"
" with AudioFile(\"my_file.mp3\", \"w\", 44100, "
"num_channels=2) as f:\n"
" # This will throw an exception:\n"
" f.write(np.zeros((2, 2))) \n"
" # But this will work:\n"
" f.write(np.zeros((2, 1)))\n"
" # And now `f` expects an input shape of (num_channels, "
"num_samples), so this works:\n"
" f.write(np.zeros((2, 2))) \n"
"\n"
" # Also an option: pass (0, num_channels) or (num_channels, "
"0) first\n"
" # to hint that the input will be in that shape "
"without writing anything:\n"
" with AudioFile(\"my_file.mp3\", \"w\", 44100, "
"num_channels=2) as f:\n"
" # Pass a hint, but write nothing:\n"
" f.write(np.zeros((2, 0))) \n"
" # And now `f` expects an input shape of (num_channels, "
"num_samples), so this works:\n"
" f.write(np.zeros((2, 2))) \n"
"\n")
.def("flush", &WriteableAudioFile::flush,
"Attempt to flush this audio file's contents to disk. Not all "
"formats support flushing, so this may throw a RuntimeError. (If "
"this happens, closing the file will reliably force a flush to "
"occur.)")
.def("close", &WriteableAudioFile::close,
"Close this file, flushing its contents to disk and rendering this "
"object unusable for further writing.")
.def("__enter__", &WriteableAudioFile::enter)
.def("__exit__", &WriteableAudioFile::exit)
.def("__repr__",
[](const WriteableAudioFile &file) {
std::ostringstream ss;
ss << "<pedalboard.io.WriteableAudioFile";
if (!file.getFilename().empty()) {
ss << " filename=\"" << file.getFilename() << "\"";
} else if (PythonOutputStream *stream =
file.getPythonOutputStream()) {
ss << " file_like=" << stream->getRepresentation();
}
if (file.isClosed()) {
ss << " closed";
} else {
ss << " samplerate=" << file.getSampleRateAsDouble();
ss << " num_channels=" << file.getNumChannels();
if (file.getQuality()) {
ss << " quality=\"" << *file.getQuality() << "\"";
}
ss << " file_dtype=" << file.getFileDatatype();
}
ss << " at " << &file;
ss << ">";
return ss.str();
})
.def_property_readonly(
"closed", &WriteableAudioFile::isClosed,
"If this file has been closed, this property will be True.")
.def_property_readonly(
"samplerate", &WriteableAudioFile::getSampleRate,
"The sample rate of this file in samples (per channel) per second "
"(Hz). Sample rates are represented as floating-point numbers by "
"default, but this property will be an integer if the file's sample "
"rate has no fractional part.")
.def_property_readonly("num_channels",
&WriteableAudioFile::getNumChannels,
"The number of channels in this file.")
.def_property_readonly("frames", &WriteableAudioFile::getFramesWritten,
"The total number of frames (samples per "
"channel) written to this file so far.")
.def("tell", &WriteableAudioFile::getFramesWritten,
"Return the current position of the write pointer in this audio "
"file, in frames at the target sample rate. This value will "
"increase as :meth:`write` is called, and will never decrease.")
.def_property_readonly(
"file_dtype", &WriteableAudioFile::getFileDatatype,
"The data type stored natively by this file. Note that write(...) "
"will accept multiple datatypes, regardless of the value of this "
"property.")
.def_property_readonly(
"quality", &WriteableAudioFile::getQuality,
"The quality setting used to write this file. For many "
"formats, this may be ``None``.\n\nQuality options differ based on "
"the audio codec used in the file. Most codecs specify a number of "
"bits per second in 16- or 32-bit-per-second increments (128 kbps, "
"160 kbps, etc). Some codecs provide string-like options for "
"variable bit-rate encoding (i.e. \"V0\" through \"V9\" for MP3). "
"The strings ``\"best\"``, ``\"worst\"``, ``\"fastest\"``, and "
"``\"slowest\"`` will also work for any codec.");
m.def("get_supported_write_formats", []() {
// JUCE doesn't support writing other formats out-of-the-box on all
// platforms, and there's no easy way to tell which formats are
// supported without attempting to create an AudioFileWriter object -
// so this list is hardcoded for now.
const std::vector<std::string> formats = {".aiff", ".flac", ".ogg", ".wav",
".mp3"};
return formats;
});
}