inline void init_writeable_audio_file()

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;
  });
}