Commit f54e84e8 authored by Manuel Günther's avatar Manuel Günther
Browse files

Continued re-documenting the C++ bindings

parent c57fd3ad
......@@ -35,13 +35,8 @@ static PyModuleDef module_definition = {
};
#endif
extern PyTypeObject PyBobIoAudioWriter_Type;
static PyObject* create_module (void) {
PyBobIoAudioWriter_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&PyBobIoAudioWriter_Type) < 0) return 0;
# if PY_VERSION_HEX >= 0x03000000
PyObject* module = PyModule_Create(&module_definition);
auto module_ = make_xsafe(module);
......@@ -54,9 +49,8 @@ static PyObject* create_module (void) {
/* register the types to python */
if (!init_BobIoAudioReader(module)) return 0;
if (!init_BobIoAudioWriter(module)) return 0;
Py_INCREF(&PyBobIoAudioWriter_Type);
if (PyModule_AddObject(module, "writer", (PyObject *)&PyBobIoAudioWriter_Type) < 0) return 0;
/* imports dependencies */
if (import_bob_blitz() < 0) return 0;
......
......@@ -6,8 +6,8 @@
*/
#ifndef BOB_IP_BASE_MAIN_H
#define BOB_IP_BASE_MAIN_H
#ifndef BOB_IO_AUDIO_MAIN_H
#define BOB_IO_AUDIO_MAIN_H
#include <bob.blitz/cppapi.h>
#include <bob.blitz/cleanup.h>
......@@ -18,6 +18,7 @@
#include "cpp/utils.h"
#include "cpp/reader.h"
#include "cpp/writer.h"
#include "bobskin.h"
// Reader
typedef struct {
......@@ -29,4 +30,15 @@ extern PyTypeObject PyBobIoAudioReader_Type;
bool init_BobIoAudioReader(PyObject* module);
int PyBobIoAudioReader_Check(PyObject* o);
#endif // BOB_IP_BASE_MAIN_H
// Writer
typedef struct {
PyObject_HEAD
boost::shared_ptr<bob::io::audio::Writer> v;
} PyBobIoAudioWriterObject;
extern PyTypeObject PyBobIoAudioWriter_Type;
bool init_BobIoAudioWriter(PyObject* module);
int PyBobIoAudioWriter_Check(PyObject* o);
#endif // BOB_IO_AUDIO_MAIN_H
......@@ -5,29 +5,20 @@
* @brief Bindings to bob::io::audio::Reader
*/
#include "bobskin.h"
#include <boost/make_shared.hpp>
#include <numpy/arrayobject.h>
#include <bob.blitz/capi.h>
#include <bob.blitz/cleanup.h>
#include <bob.io.base/api.h>
#include <stdexcept>
#include "main.h"
static auto s_reader = bob::extension::ClassDoc(
"reader",
"Use this object to read samples from audio files"
"Use this object to read samples from audio files",
"Audio reader objects can read data from audio files. "
"The current implementation uses `SoX <http://sox.sourceforge.net/>`_ , which is a stable freely available audio encoding and decoding library, designed specifically for these tasks. "
"You can read an entire audio in memory by using the :py:meth:`load` method."
)
.add_constructor(
bob::extension::FunctionDoc(
"reader",
"Opens an audio file for reading",
"Audio reader objects can read data from audio files. "
"The current implementation uses `SoX <http://sox.sourceforge.net/>`_ , which is a stable freely available audio encoding and decoding library, designed specifically for these tasks. "
"You can read an entire audio in memory by using the :py:meth:`load` method.",
"Opens the audio file with the given filename for reading, i.e., using the :py:meth:`load` function",
true
)
.add_prototype("filename", "")
......@@ -38,7 +29,6 @@ static auto s_reader = bob::extension::ClassDoc(
static int PyBobIoAudioReader_Init(PyBobIoAudioReaderObject* self,
PyObject *args, PyObject* kwds) {
BOB_TRY
/* Parses input arguments in a single shot */
char** kwlist = s_reader.kwlist();
char* filename = 0;
......@@ -46,7 +36,7 @@ BOB_TRY
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &filename)) return -1;
self->v.reset(new bob::io::audio::Reader(filename));
return 0; ///< SUCCESS
return 0;
BOB_CATCH_MEMBER("constructor", -1)
}
......@@ -129,7 +119,7 @@ PyObject* PyBobIoAudioReader_CompressionFactor(PyBobIoAudioReaderObject* self) {
static auto s_encoding = bob::extension::VariableDoc(
"encoding",
"str",
"Name of the encoding in which this audio file was recorded in"
"Name of the encoding in which this audio file was recorded"
);
PyObject* PyBobIoAudioReader_EncodingName(PyBobIoAudioReaderObject* self) {
return Py_BuildValue("s", bob::io::audio::encoding2string(self->v->encoding()));
......@@ -232,7 +222,8 @@ static void Check_Interrupt() {
static auto s_load = bob::extension::FunctionDoc(
"load",
"Loads all of the audio stream in a :py:class:`numpy.ndarray`",
"The data is organized in this way: ``(channels, data)``. "
"The data is organized in this way: ``(channels, data)``. ",
true
)
.add_prototype("","data")
.add_return("data", ":py:class:`numpy.ndarray`", "The data read from this file")
......
......@@ -5,270 +5,231 @@
* @brief Bindings to bob::io::audio::Writer
*/
#include "bobskin.h"
#include <boost/make_shared.hpp>
#include <numpy/arrayobject.h>
#include <bob.blitz/cppapi.h>
#include <bob.blitz/cleanup.h>
#include <bob.io.base/api.h>
#include <stdexcept>
#include "cpp/utils.h"
#include "cpp/writer.h"
#define AUDIOWRITER_NAME "writer"
PyDoc_STRVAR(s_audiowriter_str, BOB_EXT_MODULE_PREFIX "." AUDIOWRITER_NAME);
PyDoc_STRVAR(s_audiowriter_doc,
"writer(filename, [rate=8000., [encoding='UNKNOWN', [bits_per_sample=16]]]) -> new writer\n\
\n\
Use this object to write samples to audio files.\n\
\n\
Constructor parameters:\n\
\n\
filename\n\
[str] The file path to the file you want to write data to\n\
\n\
rate\n\
[float, optional] The number of samples per second\n\
\n\
encoding\n\
[str, optional] The encoding to use\n\
\n\
bits_per_sample\n\
[int, optional] The number of bits per sample to be recorded\n\
\n\
Audio writer objects can write data to audio files. The current\n\
implementation uses `SoX <http://sox.sourceforge.net/>`_. \n\
Audio files are objects composed potentially multiple channels.\n\
The numerical representation are 2-D arrays where the first\n\
dimension corresponds to the channels of the audio stream and\n\
the second dimension represents the samples through time.\n\
");
typedef struct {
PyObject_HEAD
boost::shared_ptr<bob::io::audio::Writer> v;
} PyBobIoAudioWriterObject;
extern PyTypeObject PyBobIoAudioWriter_Type;
/* How to create a new PyBobIoAudioWriterObject */
static PyObject* PyBobIoAudioWriter_New(PyTypeObject* type, PyObject*, PyObject*) {
/* Allocates the python object itself */
PyBobIoAudioWriterObject* self = (PyBobIoAudioWriterObject*)type->tp_alloc(type, 0);
self->v.reset();
return reinterpret_cast<PyObject*>(self);
}
static void PyBobIoAudioWriter_Delete (PyBobIoAudioWriterObject* o) {
o->v.reset();
Py_TYPE(o)->tp_free((PyObject*)o);
}
#include "main.h"
static auto s_writer = bob::extension::ClassDoc(
"writer",
"Use this object to write samples to audio files",
"Audio writer objects can write data to audio files. "
"The current implementation uses `SoX <http://sox.sourceforge.net/>`_.\n\n"
"Audio files are objects composed potentially multiple channels. "
"The numerical representation are 2-D arrays where the first dimension corresponds to the channels of the audio stream and the second dimension represents the samples through time."
)
.add_constructor(
bob::extension::FunctionDoc(
"reader",
"Opens an audio file for writing",
"Opens the audio file with the given filename for writing, i.e., using the :py:meth:`append` function",
true
)
.add_prototype("filename, [rate], [encoding], [bits_per_sample]", "")
.add_parameter("filename", "str", "The file path to the file you want to write data to")
.add_parameter("rate", "float", "[Default: ``8000.``] The number of samples per second")
.add_parameter("encoding", "str", "[Default: ``'UNKNOWN'``] The encoding to use")
.add_parameter("bits_per_sample", "int", "[Default: ``16``] The number of bits per sample to be recorded")
);
/* The __init__(self) method */
static int PyBobIoAudioWriter_Init(PyBobIoAudioWriterObject* self,
PyObject *args, PyObject* kwds) {
BOB_TRY
char** kwlist = s_writer.kwlist();
/* Parses input arguments in a single shot */
static const char* const_kwlist[] = {
"filename", "rate", "encoding", "bits_per_sample",
0};
static char** kwlist = const_cast<char**>(const_kwlist);
PyObject* filename = 0;
char* filename = 0;
double rate = 8000.;
char* encoding = 0;
char* encoding = "UNKNOWN";
Py_ssize_t bits_per_sample = 16;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|dsn", kwlist,
&PyBobIo_FilenameConverter, &filename,
&rate, &encoding, &bits_per_sample)) return -1;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|dsn", kwlist,
&filename, &rate, &encoding, &bits_per_sample)) return -1;
auto filename_ = make_safe(filename);
sox_encoding_t sox_encoding = bob::io::audio::string2encoding(encoding);
std::string encoding_str = encoding?encoding:"UNKNOWN";
sox_encoding_t sox_encoding = bob::io::audio::string2encoding(encoding_str.c_str());
self->v = boost::make_shared<bob::io::audio::Writer>(filename,
rate, sox_encoding, bits_per_sample);
#if PY_VERSION_HEX >= 0x03000000
const char* c_filename = PyBytes_AS_STRING(filename);
#else
const char* c_filename = PyString_AS_STRING(filename);
#endif
return 0;
BOB_CATCH_MEMBER("constructor", -1)
}
try {
self->v = boost::make_shared<bob::io::audio::Writer>(c_filename,
rate, sox_encoding, bits_per_sample);
}
catch (std::exception& e) {
PyErr_SetString(PyExc_RuntimeError, e.what());
return -1;
}
catch (...) {
PyErr_Format(PyExc_RuntimeError, "cannot open audio file `%s' for writing: unknown exception caught", c_filename);
return -1;
}
static void PyBobIoAudioWriter_Delete (PyBobIoAudioWriterObject* o) {
o->v.reset();
Py_TYPE(o)->tp_free((PyObject*)o);
return 0; ///< SUCCESS
}
static auto s_filename = bob::extension::VariableDoc(
"filename",
"str",
"The full path to the file that will be decoded by this object"
);
PyObject* PyBobIoAudioWriter_Filename(PyBobIoAudioWriterObject* self) {
return Py_BuildValue("s", self->v->filename());
}
PyDoc_STRVAR(s_filename_str, "filename");
PyDoc_STRVAR(s_filename_doc,
"[str] The full path to the file that will be decoded by this object");
static auto s_rate = bob::extension::VariableDoc(
"rate",
"float",
"The sampling rate of the audio stream"
);
PyObject* PyBobIoAudioWriter_Rate(PyBobIoAudioWriterObject* self) {
return Py_BuildValue("d", self->v->rate());
}
PyDoc_STRVAR(s_rate_str, "rate");
PyDoc_STRVAR(s_rate_doc,
"[float] The sampling rate of the audio stream");
static auto s_number_of_channels = bob::extension::VariableDoc(
"number_of_channels",
"int",
"The number of channels on the audio stream"
);
PyObject* PyBobIoAudioWriter_NumberOfChannels(PyBobIoAudioWriterObject* self) {
return Py_BuildValue("n", self->v->numberOfChannels());
}
PyDoc_STRVAR(s_number_of_channels_str, "number_of_channels");
PyDoc_STRVAR(s_number_of_channels_doc,
"[int] The number of channels on the audio stream");
static auto s_bits_per_sample = bob::extension::VariableDoc(
"bits_per_sample",
"int",
"The number of bits per sample in this audio stream"
);
PyObject* PyBobIoAudioWriter_BitsPerSample(PyBobIoAudioWriterObject* self) {
return Py_BuildValue("n", self->v->bitsPerSample());
}
PyDoc_STRVAR(s_bits_per_sample_str, "bits_per_sample");
PyDoc_STRVAR(s_bits_per_sample_doc,
"[int] The number of bits per sample in this audio stream");
static auto s_number_of_samples = bob::extension::VariableDoc(
"number_of_samples",
"int",
"The number of samples in this audio stream"
);
PyObject* PyBobIoAudioWriter_NumberOfSamples(PyBobIoAudioWriterObject* self) {
return Py_BuildValue("n", self->v->numberOfSamples());
}
PyDoc_STRVAR(s_number_of_samples_str, "number_of_samples");
PyDoc_STRVAR(s_number_of_samples_doc,
"[int] The number of samples in this audio stream");
static auto s_duration = bob::extension::VariableDoc(
"duration",
"float",
"Total duration of this audio file in seconds"
);
PyObject* PyBobIoAudioWriter_Duration(PyBobIoAudioWriterObject* self) {
return Py_BuildValue("d", self->v->duration());
}
PyDoc_STRVAR(s_duration_str, "duration");
PyDoc_STRVAR(s_duration_doc,
"[float] Total duration of this audio file in seconds");
static auto s_compression_factor = bob::extension::VariableDoc(
"compression_factor",
"float",
"Compression factor on the audio stream"
);
PyObject* PyBobIoAudioWriter_CompressionFactor(PyBobIoAudioWriterObject* self) {
return Py_BuildValue("d", self->v->compressionFactor());
}
PyDoc_STRVAR(s_compression_factor_str, "compressionfactor");
PyDoc_STRVAR(s_compression_factor_doc,
"[float] Compression factor on the audio stream");
static auto s_encoding = bob::extension::VariableDoc(
"encoding",
"str",
"Name of the encoding in which this audio file will be written"
);
PyObject* PyBobIoAudioWriter_EncodingName(PyBobIoAudioWriterObject* self) {
return Py_BuildValue("s", bob::io::audio::encoding2string(self->v->encoding()));
}
PyDoc_STRVAR(s_encoding_name_str, "encoding");
PyDoc_STRVAR(s_encoding_name_doc,
"[str] Name of the encoding in which this audio file was recorded in");
static auto s_type = bob::extension::VariableDoc(
"type",
"tuple",
"Typing information to load all of the file at once"
);
PyObject* PyBobIoAudioWriter_TypeInfo(PyBobIoAudioWriterObject* self) {
return PyBobIo_TypeInfoAsTuple(self->v->type());
}
PyDoc_STRVAR(s_type_str, "type");
PyDoc_STRVAR(s_type_doc,
"[tuple] Typing information to load all of the file at once");
static auto s_is_opened = bob::extension::VariableDoc(
"is_opened",
"bool",
"A flag indicating if the audio is still opened for writing, or has already been closed by the user using :py:meth:`close`"
);
static PyObject* PyBobIoAudioWriter_IsOpened(PyBobIoAudioWriterObject* self) {
if (self->v->is_opened()) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyDoc_STRVAR(s_is_opened_str, "is_opened");
PyDoc_STRVAR(s_is_opened_doc,
"[bool] A flag, indicating if the audio is still opened for writing\n\
(or has already been closed by the user using ``close()``)");
static PyGetSetDef PyBobIoAudioWriter_getseters[] = {
{
s_filename_str,
s_filename.name(),
(getter)PyBobIoAudioWriter_Filename,
0,
s_filename_doc,
s_filename.doc(),
0,
},
{
s_rate_str,
s_rate.name(),
(getter)PyBobIoAudioWriter_Rate,
0,
s_rate_doc,
s_rate.doc(),
0,
},
{
s_number_of_channels_str,
s_number_of_channels.name(),
(getter)PyBobIoAudioWriter_NumberOfChannels,
0,
s_number_of_channels_doc,
s_number_of_channels.doc(),
0,
},
{
s_bits_per_sample_str,
s_bits_per_sample.name(),
(getter)PyBobIoAudioWriter_BitsPerSample,
0,
s_bits_per_sample_doc,
s_bits_per_sample.doc(),
0,
},
{
s_number_of_samples_str,
s_number_of_samples.name(),
(getter)PyBobIoAudioWriter_NumberOfSamples,
0,
s_number_of_samples_doc,
s_number_of_samples.doc(),
0,
},
{
s_duration_str,
s_duration.name(),
(getter)PyBobIoAudioWriter_Duration,
0,
s_duration_doc,
s_duration.doc(),
0,
},
{
s_encoding_name_str,
s_encoding.name(),
(getter)PyBobIoAudioWriter_EncodingName,
0,
s_encoding_name_doc,
s_encoding.doc(),
0,
},
{
s_compression_factor_str,
s_compression_factor.name(),
(getter)PyBobIoAudioWriter_CompressionFactor,
0,
s_compression_factor_doc,
s_compression_factor.doc(),
0,
},
{
s_type_str,
s_type.name(),
(getter)PyBobIoAudioWriter_TypeInfo,
0,
s_type_doc,
s_type.doc(),
0,
},
{
s_is_opened_str,
s_is_opened.name(),
(getter)PyBobIoAudioWriter_IsOpened,
0,
s_is_opened_doc,
s_is_opened.doc(),
0,
},
{0} /* Sentinel */
......@@ -290,17 +251,29 @@ static PyObject* PyBobIoAudioWriter_Repr(PyBobIoAudioWriterObject* self) {
("%s(filename='%s', rate=%g, encoding=%s, bits_per_sample=%" PY_FORMAT_SIZE_T "d)", Py_TYPE(self)->tp_name, self->v->filename(), self->v->rate(), bob::io::audio::encoding2string(self->v->encoding()), self->v->bitsPerSample());
}
static PyObject* PyBobIoAudioWriter_Append(PyBobIoAudioWriterObject* self, PyObject *args, PyObject* kwds) {
static auto s_append = bob::extension::FunctionDoc(
"append",
"Writes a new sample or set of samples to the file",
"The frame should be setup as a array with 1 dimension where each entry corresponds to one stream channel. "
"Sets of samples should be setup as a 2D array in this way: (channels, samples). "
"Arrays should contain only 64-bit float numbers.\n\n"
".. note::\n"
" At present time we only support arrays that have C-style storages (if you pass reversed arrays or arrays with Fortran-style storage, the result is undefined)",
true
)
.add_prototype("sample")
.add_parameter("sample", "array-like (1D or 2D, float)", "The sample(s) that should be appended to the file")
;
static PyObject* PyBobIoAudioWriter_Append(PyBobIoAudioWriterObject* self, PyObject *args, PyObject* kwds) {
BOB_TRY
if (!self->v->is_opened()) {
PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed",
Py_TYPE(self)->tp_name, self->v->filename());
return 0;
}
/* Parses input arguments in a single shot */
static const char* const_kwlist[] = {"sample", 0};
static char** kwlist = const_cast<char**>(const_kwlist);
char** kwlist = s_append.kwlist();
PyBlitzArrayObject* sample = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&", kwlist, &PyBlitzArray_BehavedConverter, &sample)) return 0;
......@@ -316,71 +289,44 @@ static PyObject* PyBobIoAudioWriter_Append(PyBobIoAudioWriterObject* self, PyObj
return 0;
}
try {
if (sample->ndim == 1) {
self->v->append(*PyBlitzArrayCxx_AsBlitz<double,1>(sample));
}
else {
self->v->append(*PyBlitzArrayCxx_AsBlitz<double,2>(sample));
}
if (sample->ndim == 1) {
self->v->append(*PyBlitzArrayCxx_AsBlitz<double,1>(sample));
}
catch (std::exception& e) {
if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, e.what());
return 0;
else {
self->v->append(*PyBlitzArrayCxx_AsBlitz<double,2>(sample));
}
catch (...) {
if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while writing sample #%" PY_FORMAT_SIZE_T "d to file `%s'", self->v->numberOfSamples(), self->v->filename());
return 0;
}
Py_RETURN_NONE;
BOB_CATCH_MEMBER("append", 0)
}
PyDoc_STRVAR(s_append_str, "append");
PyDoc_STRVAR(s_append_doc,
"x.append(sample) -> None\n\
\n\
Writes a new sample or set of samples to the file.\n\
\n\
The frame should be setup as a array with 1 dimension where each\n\
entry corresponds to one stream channel. Sets of samples should\n\
be setup as a 2D array in this way: (channels, samples).\n\
Arrays should contain only 64-bit float numbers.\n\
\n\
.. note::\n\
At present time we only support arrays that have C-style storages\n\
(if you pass reversed arrays or arrays with Fortran-style storage,\n\
the result is undefined).\n\
\n\
");
static auto s_close = bob::extension::FunctionDoc(
"close",
"Closes the current audio stream and forces writing the trailer",
"After this point the audio is finalized and cannot be written to anymore.",
true
)
.add_prototype("")
;
static PyObject* PyBobIoAudioWriter_Close(PyBobIoAudioWriterObject* self) {
BOB_TRY
self->v->close();
Py_RETURN_NONE;
BOB_CATCH_MEMBER("close", 0)
}
PyDoc_STRVAR(s_close_str, "close");
PyDoc_STRVAR(s_close_doc,
"x.close() -> None\n\
\n\
Closes the current audio stream and forces writing the trailer.\n\
After this point the audio is finalized and cannot be written to\n\
anymore.\n\
");
static PyMethodDef PyBobIoAudioWriter_Methods[] = {
{
s_append_str,
s_append.name(),
(PyCFunction)PyBobIoAudioWriter_Append,
METH_VARARGS|METH_KEYWORDS,
s_append_doc,
s_append.doc(),
},
{