fe891d581d
- Changing API for time values from float to double for better precision. - Fixing minor mistakes in the documentation. - Fixing minor unnecessary large memory allocation.
656 lines
16 KiB
C++
656 lines
16 KiB
C++
/*******************************************************************************
|
|
* Copyright 2009-2016 Jörg Müller
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
******************************************************************************/
|
|
|
|
#include "PySequence.h"
|
|
|
|
#include "PySound.h"
|
|
#include "PySequenceEntry.h"
|
|
|
|
#include "sequence/AnimateableProperty.h"
|
|
#include "sequence/Sequence.h"
|
|
#include "Exception.h"
|
|
|
|
#include <vector>
|
|
#include <structmember.h>
|
|
|
|
using aud::Channels;
|
|
using aud::DistanceModel;
|
|
using aud::Exception;
|
|
using aud::ISound;
|
|
using aud::AnimateableProperty;
|
|
using aud::AnimateablePropertyType;
|
|
using aud::Specs;
|
|
|
|
extern PyObject* AUDError;
|
|
|
|
// ====================================================================
|
|
|
|
static void
|
|
Sequence_dealloc(Sequence* self)
|
|
{
|
|
if(self->sequence)
|
|
delete reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence);
|
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
|
}
|
|
|
|
static PyObject *
|
|
Sequence_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
|
|
{
|
|
Sequence* self;
|
|
|
|
int channels = aud::CHANNELS_STEREO;
|
|
double rate = aud::RATE_48000;
|
|
float fps = 30.0f;
|
|
bool muted = false;
|
|
PyObject* mutedo = nullptr;
|
|
|
|
self = (Sequence*)type->tp_alloc(type, 0);
|
|
if(self != nullptr)
|
|
{
|
|
static const char* kwlist[] = {"channels", "rate", "fps", "muted", nullptr};
|
|
|
|
if(!PyArg_ParseTupleAndKeywords(args, kwds, "|idfO:Sequence", const_cast<char**>(kwlist), &channels, &rate, &fps, &mutedo))
|
|
{
|
|
Py_DECREF(self);
|
|
return nullptr;
|
|
}
|
|
|
|
if(mutedo)
|
|
{
|
|
if(!PyBool_Check(mutedo))
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, "muted is not a boolean!");
|
|
return nullptr;
|
|
}
|
|
|
|
muted = mutedo == Py_True;
|
|
}
|
|
|
|
aud::Specs specs;
|
|
specs.channels = static_cast<aud::Channels>(channels);
|
|
specs.rate = rate;
|
|
|
|
try
|
|
{
|
|
self->sequence = new std::shared_ptr<aud::Sequence>(new aud::Sequence(specs, fps, muted));
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
Py_DECREF(self);
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return (PyObject *)self;
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_add_doc,
|
|
".. classmethod:: add()\n\n"
|
|
" Adds a new entry to the sequence.\n\n"
|
|
" :arg sound: The sound this entry should play.\n"
|
|
" :type sound: :class:`Sound`\n"
|
|
" :arg begin: The start time.\n"
|
|
" :type begin: double\n"
|
|
" :arg end: The end time or a negative value if determined by the sound.\n"
|
|
" :type end: double\n"
|
|
" :arg skip: How much seconds should be skipped at the beginning.\n"
|
|
" :type skip: double\n"
|
|
" :return: The entry added.\n"
|
|
" :rtype: :class:`SequenceEntry`");
|
|
|
|
static PyObject *
|
|
Sequence_add(Sequence* self, PyObject* args, PyObject* kwds)
|
|
{
|
|
PyObject* object;
|
|
double begin;
|
|
double end = -1.0;
|
|
double skip = 0.0;
|
|
|
|
static const char* kwlist[] = {"sound", "begin", "end", "skip", nullptr};
|
|
|
|
if(!PyArg_ParseTupleAndKeywords(args, kwds, "Od|dd:add", const_cast<char**>(kwlist), &object, &begin, &end, &skip))
|
|
return nullptr;
|
|
|
|
Sound* sound = checkSound(object);
|
|
|
|
if(!sound)
|
|
return nullptr;
|
|
|
|
SequenceEntry* entry;
|
|
|
|
entry = (SequenceEntry*)SequenceEntry_empty();
|
|
if(entry != nullptr)
|
|
{
|
|
try
|
|
{
|
|
entry->entry = new std::shared_ptr<aud::SequenceEntry>((*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->add(*reinterpret_cast<std::shared_ptr<ISound>*>(sound->sound), begin, end, skip));
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
Py_DECREF(entry);
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return (PyObject *)entry;
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_remove_doc,
|
|
".. classmethod:: remove()\n\n"
|
|
" Removes an entry from the sequence.\n\n"
|
|
" :arg entry: The entry to remove.\n"
|
|
" :type entry: :class:`SequenceEntry`\n");
|
|
|
|
static PyObject *
|
|
Sequence_remove(Sequence* self, PyObject* args)
|
|
{
|
|
PyObject* object;
|
|
|
|
if(!PyArg_ParseTuple(args, "O:remove", &object))
|
|
return nullptr;
|
|
|
|
SequenceEntry* entry = checkSequenceEntry(object);
|
|
|
|
if(!entry)
|
|
return nullptr;
|
|
|
|
try
|
|
{
|
|
(*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->remove(*reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(entry->entry));
|
|
Py_RETURN_NONE;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
Py_DECREF(entry);
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_setAnimationData_doc,
|
|
".. classmethod:: setAnimationData()\n\n"
|
|
" Writes animation data to a sequence.\n\n"
|
|
" :arg type: The type of animation data.\n"
|
|
" :type type: int\n"
|
|
" :arg frame: The frame this data is for.\n"
|
|
" :type frame: int\n"
|
|
" :arg data: The data to write.\n"
|
|
" :type data: sequence of float\n"
|
|
" :arg animated: Whether the attribute is animated.\n"
|
|
" :type animated: bool");
|
|
|
|
static PyObject *
|
|
Sequence_setAnimationData(Sequence* self, PyObject* args)
|
|
{
|
|
int type, frame;
|
|
PyObject* py_data;
|
|
Py_ssize_t py_data_len;
|
|
PyObject* animatedo;
|
|
bool animated;
|
|
|
|
if(!PyArg_ParseTuple(args, "iiOO:setAnimationData", &type, &frame, &py_data, &animatedo))
|
|
return nullptr;
|
|
|
|
if(!PySequence_Check(py_data))
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, "Parameter is not a sequence!");
|
|
return nullptr;
|
|
}
|
|
|
|
py_data_len= PySequence_Size(py_data);
|
|
|
|
std::vector<float> data;
|
|
data.resize(py_data_len);
|
|
|
|
PyObject* py_value;
|
|
float value;
|
|
|
|
for(Py_ssize_t i = 0; i < py_data_len; i++)
|
|
{
|
|
py_value = PySequence_GetItem(py_data, i);
|
|
value= (float)PyFloat_AsDouble(py_value);
|
|
Py_DECREF(py_value);
|
|
|
|
if(value == -1.0f && PyErr_Occurred()) {
|
|
return nullptr;
|
|
}
|
|
|
|
data.push_back(value);
|
|
}
|
|
|
|
if(!PyBool_Check(animatedo))
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, "animated is not a boolean!");
|
|
return nullptr;
|
|
}
|
|
|
|
animated = animatedo == Py_True;
|
|
|
|
try
|
|
{
|
|
AnimateableProperty* prop = (*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->getAnimProperty(static_cast<AnimateablePropertyType>(type));
|
|
|
|
if(prop->getCount() != py_data_len)
|
|
{
|
|
PyErr_SetString(PyExc_ValueError, "the amount of floats doesn't fit the animated property");
|
|
return nullptr;
|
|
}
|
|
|
|
if(animated)
|
|
{
|
|
if(frame >= 0)
|
|
prop->write(&data[0], frame, 1);
|
|
}
|
|
else
|
|
{
|
|
prop->write(&data[0]);
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static PyMethodDef Sequence_methods[] = {
|
|
{"add", (PyCFunction)Sequence_add, METH_VARARGS | METH_KEYWORDS,
|
|
M_aud_Sequence_add_doc
|
|
},
|
|
{"remove", (PyCFunction)Sequence_remove, METH_VARARGS,
|
|
M_aud_Sequence_remove_doc
|
|
},
|
|
{"setAnimationData", (PyCFunction)Sequence_setAnimationData, METH_VARARGS,
|
|
M_aud_Sequence_setAnimationData_doc
|
|
},
|
|
{nullptr} /* Sentinel */
|
|
};
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_channels_doc,
|
|
"The channel count of the sequence.");
|
|
|
|
static PyObject *
|
|
Sequence_get_channels(Sequence* self, void* nothing)
|
|
{
|
|
try
|
|
{
|
|
Specs specs = (*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->getSpecs();
|
|
return Py_BuildValue("i", specs.channels);
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Sequence_set_channels(Sequence* self, PyObject* args, void* nothing)
|
|
{
|
|
int channels;
|
|
|
|
if(!PyArg_Parse(args, "i:channels", &channels))
|
|
return -1;
|
|
|
|
try
|
|
{
|
|
std::shared_ptr<aud::Sequence> sequence = *reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence);
|
|
Specs specs = sequence->getSpecs();
|
|
specs.channels = static_cast<Channels>(channels);
|
|
sequence->setSpecs(specs);
|
|
return 0;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_distance_model_doc,
|
|
"The distance model of the sequence.\n\n"
|
|
".. seealso:: `OpenAL Documentation <https://www.openal.org/documentation/>`__");
|
|
|
|
static PyObject *
|
|
Sequence_get_distance_model(Sequence* self, void* nothing)
|
|
{
|
|
try
|
|
{
|
|
return Py_BuildValue("i", (*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->getDistanceModel());
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Sequence_set_distance_model(Sequence* self, PyObject* args, void* nothing)
|
|
{
|
|
int distance_model;
|
|
|
|
if(!PyArg_Parse(args, "i:distance_model", &distance_model))
|
|
return -1;
|
|
|
|
try
|
|
{
|
|
(*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->setDistanceModel(static_cast<DistanceModel>(distance_model));
|
|
return 0;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_doppler_factor_doc,
|
|
"The doppler factor of the sequence.\n"
|
|
"This factor is a scaling factor for the velocity vectors in "
|
|
"doppler calculation. So a value bigger than 1 will exaggerate "
|
|
"the effect as it raises the velocity.");
|
|
|
|
static PyObject *
|
|
Sequence_get_doppler_factor(Sequence* self, void* nothing)
|
|
{
|
|
try
|
|
{
|
|
return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->getDopplerFactor());
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Sequence_set_doppler_factor(Sequence* self, PyObject* args, void* nothing)
|
|
{
|
|
float factor;
|
|
|
|
if(!PyArg_Parse(args, "f:doppler_factor", &factor))
|
|
return -1;
|
|
|
|
try
|
|
{
|
|
(*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->setDopplerFactor(factor);
|
|
return 0;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_fps_doc,
|
|
"The listeners's location in 3D space, a 3D tuple of floats.");
|
|
|
|
static PyObject *
|
|
Sequence_get_fps(Sequence* self, void* nothing)
|
|
{
|
|
try
|
|
{
|
|
return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->getFPS());
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Sequence_set_fps(Sequence* self, PyObject* args, void* nothing)
|
|
{
|
|
float fps;
|
|
|
|
if(!PyArg_Parse(args, "f:fps", &fps))
|
|
return -1;
|
|
|
|
try
|
|
{
|
|
(*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->setFPS(fps);
|
|
return 0;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_muted_doc,
|
|
"Whether the whole sequence is muted.\n");
|
|
|
|
static PyObject *
|
|
Sequence_get_muted(Sequence* self, void* nothing)
|
|
{
|
|
try
|
|
{
|
|
std::shared_ptr<aud::Sequence>* sequence = reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence);
|
|
return PyBool_FromLong((long)(*sequence)->isMuted());
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Sequence_set_muted(Sequence* self, PyObject* args, void* nothing)
|
|
{
|
|
if(!PyBool_Check(args))
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, "muted is not a boolean!");
|
|
return -1;
|
|
}
|
|
|
|
bool muted = args == Py_True;
|
|
|
|
try
|
|
{
|
|
std::shared_ptr<aud::Sequence>* sequence = reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence);
|
|
(*sequence)->mute(muted);
|
|
return 0;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_rate_doc,
|
|
"The sampling rate of the sequence in Hz.");
|
|
|
|
static PyObject *
|
|
Sequence_get_rate(Sequence* self, void* nothing)
|
|
{
|
|
try
|
|
{
|
|
Specs specs = (*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->getSpecs();
|
|
return Py_BuildValue("d", specs.rate);
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Sequence_set_rate(Sequence* self, PyObject* args, void* nothing)
|
|
{
|
|
double rate;
|
|
|
|
if(!PyArg_Parse(args, "d:rate", &rate))
|
|
return -1;
|
|
|
|
try
|
|
{
|
|
std::shared_ptr<aud::Sequence> sequence = *reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence);
|
|
Specs specs = sequence->getSpecs();
|
|
specs.rate = rate;
|
|
sequence->setSpecs(specs);
|
|
return 0;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_speed_of_sound_doc,
|
|
"The speed of sound of the sequence.\n"
|
|
"The speed of sound in air is typically 343.3 m/s.");
|
|
|
|
static PyObject *
|
|
Sequence_get_speed_of_sound(Sequence* self, void* nothing)
|
|
{
|
|
try
|
|
{
|
|
return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->getSpeedOfSound());
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Sequence_set_speed_of_sound(Sequence* self, PyObject* args, void* nothing)
|
|
{
|
|
float speed;
|
|
|
|
if(!PyArg_Parse(args, "f:speed_of_sound", &speed))
|
|
return -1;
|
|
|
|
try
|
|
{
|
|
(*reinterpret_cast<std::shared_ptr<aud::Sequence>*>(self->sequence))->setSpeedOfSound(speed);
|
|
return 0;
|
|
}
|
|
catch(Exception& e)
|
|
{
|
|
PyErr_SetString(AUDError, e.what());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static PyGetSetDef Sequence_properties[] = {
|
|
{(char*)"channels", (getter)Sequence_get_channels, (setter)Sequence_set_channels,
|
|
M_aud_Sequence_channels_doc, nullptr },
|
|
{(char*)"distance_model", (getter)Sequence_get_distance_model, (setter)Sequence_set_distance_model,
|
|
M_aud_Sequence_distance_model_doc, nullptr },
|
|
{(char*)"doppler_factor", (getter)Sequence_get_doppler_factor, (setter)Sequence_set_doppler_factor,
|
|
M_aud_Sequence_doppler_factor_doc, nullptr },
|
|
{(char*)"fps", (getter)Sequence_get_fps, (setter)Sequence_set_fps,
|
|
M_aud_Sequence_fps_doc, nullptr },
|
|
{(char*)"muted", (getter)Sequence_get_muted, (setter)Sequence_set_muted,
|
|
M_aud_Sequence_muted_doc, nullptr },
|
|
{(char*)"rate", (getter)Sequence_get_rate, (setter)Sequence_set_rate,
|
|
M_aud_Sequence_rate_doc, nullptr },
|
|
{(char*)"speed_of_sound", (getter)Sequence_get_speed_of_sound, (setter)Sequence_set_speed_of_sound,
|
|
M_aud_Sequence_speed_of_sound_doc, nullptr },
|
|
{nullptr} /* Sentinel */
|
|
};
|
|
|
|
PyDoc_STRVAR(M_aud_Sequence_doc,
|
|
"This sound represents sequenced entries to play a sound sequence.");
|
|
|
|
extern PyTypeObject SoundType;
|
|
|
|
static PyTypeObject SequenceType = {
|
|
PyVarObject_HEAD_INIT(nullptr, 0)
|
|
"aud.Sequence", /* tp_name */
|
|
sizeof(Sequence), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
(destructor)Sequence_dealloc,/* tp_dealloc */
|
|
0, /* tp_print */
|
|
0, /* tp_getattr */
|
|
0, /* tp_setattr */
|
|
0, /* tp_reserved */
|
|
0, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
0, /* tp_hash */
|
|
0, /* tp_call */
|
|
0, /* tp_str */
|
|
0, /* tp_getattro */
|
|
0, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
M_aud_Sequence_doc, /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
Sequence_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
Sequence_properties, /* tp_getset */
|
|
&SoundType, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0, /* tp_init */
|
|
0, /* tp_alloc */
|
|
Sequence_new, /* tp_new */
|
|
};
|
|
|
|
AUD_API PyObject* Sequence_empty()
|
|
{
|
|
return SequenceType.tp_alloc(&SequenceType, 0);
|
|
}
|
|
|
|
|
|
AUD_API Sequence* checkSequence(PyObject* sequence)
|
|
{
|
|
if(!PyObject_TypeCheck(sequence, &SequenceType))
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, "Object is not of type Sequence!");
|
|
return nullptr;
|
|
}
|
|
|
|
return (Sequence*)sequence;
|
|
}
|
|
|
|
|
|
bool initializeSequence()
|
|
{
|
|
return PyType_Ready(&SequenceType) >= 0;
|
|
}
|
|
|
|
|
|
void addSequenceToModule(PyObject* module)
|
|
{
|
|
Py_INCREF(&SequenceType);
|
|
PyModule_AddObject(module, "Sequence", (PyObject *)&SequenceType);
|
|
}
|