diff options
author | Dalai Felinto <dfelinto@gmail.com> | 2018-02-01 14:11:04 +0300 |
---|---|---|
committer | Dalai Felinto <dfelinto@gmail.com> | 2018-02-01 14:22:57 +0300 |
commit | 781dd5edb5b74ecb417a16c28eab31483fc3d11a (patch) | |
tree | e5c142f2eef1cbcb1ed6dc243ec9707fe64f8d5f /extern/audaspace/bindings/python | |
parent | cd317fab65e1b6c1652ba7a58786a8cc5bd11843 (diff) |
Fix audaspace mess in 2.8
All these files were removed since accidental commit, revert and merge in 2.8.
ea31f0ac3b877e + 0a4e170c28cec + 11f9a23a286c17f + 7b27b10fa6a6
Diffstat (limited to 'extern/audaspace/bindings/python')
37 files changed, 7946 insertions, 0 deletions
diff --git a/extern/audaspace/bindings/python/PyAPI.cpp b/extern/audaspace/bindings/python/PyAPI.cpp new file mode 100644 index 00000000000..cceadbc0992 --- /dev/null +++ b/extern/audaspace/bindings/python/PyAPI.cpp @@ -0,0 +1,231 @@ +/******************************************************************************* + * 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 "PyAPI.h" +#include "PySound.h" +#include "PyHandle.h" +#include "PyDevice.h" +#include "PySequenceEntry.h" +#include "PySequence.h" +#include "PyPlaybackManager.h" +#include "PyDynamicMusic.h" +#include "PyThreadPool.h" +#include "PySource.h" + +#ifdef WITH_CONVOLUTION +#include "PyImpulseResponse.h" +#include "PyHRTF.h" +#endif + +#include "respec/Specification.h" +#include "devices/IHandle.h" +#include "devices/I3DDevice.h" +#include "file/IWriter.h" +#include "plugin/PluginManager.h" +#include "sequence/AnimateableProperty.h" +#include "ISound.h" + +#include <memory> + +#include <structmember.h> + +using namespace aud; + +// ==================================================================== + +#define PY_MODULE_ADD_CONSTANT(module, name) PyModule_AddIntConstant(module, #name, name) + +// ==================================================================== + +extern PyObject* AUDError; +PyObject* AUDError = nullptr; + +// ==================================================================== + +PyDoc_STRVAR(M_aud_doc, + "Audaspace (pronounced \"outer space\") is a high level audio library."); + +static struct PyModuleDef audmodule = { + PyModuleDef_HEAD_INIT, + "aud", /* name of module */ + M_aud_doc, /* module documentation */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + nullptr, nullptr, nullptr, nullptr, nullptr +}; + +PyMODINIT_FUNC +PyInit_aud() +{ + PyObject* module; + + PluginManager::loadPlugins(); + + if(!initializeSound()) + return nullptr; + + if(!initializeDevice()) + return nullptr; + + if(!initializeHandle()) + return nullptr; + + if(!initializeSequenceEntry()) + return nullptr; + + if(!initializeSequence()) + return nullptr; + + if(!initializeDynamicMusic()) + return nullptr; + + if(!initializePlaybackManager()) + return nullptr; + + if(!initializeThreadPool()) + return nullptr; + + if(!initializeSource()) + return nullptr; + +#ifdef WITH_CONVOLUTION + if(!initializeImpulseResponse()) + return nullptr; + + if(!initializeHRTF()) + return nullptr; +#endif + + module = PyModule_Create(&audmodule); + if(module == nullptr) + return nullptr; + + addSoundToModule(module); + addHandleToModule(module); + addDeviceToModule(module); + addSequenceEntryToModule(module); + addSequenceToModule(module); + addDynamicMusicToModule(module); + addPlaybackManagerToModule(module); + addThreadPoolToModule(module); + addSourceToModule(module); + +#ifdef WITH_CONVOLUTION + addImpulseResponseToModule(module); + addHRTFToModule(module); +#endif + + AUDError = PyErr_NewException("aud.error", nullptr, nullptr); + Py_INCREF(AUDError); + PyModule_AddObject(module, "error", AUDError); + + // animatable property type constants + PY_MODULE_ADD_CONSTANT(module, AP_VOLUME); + PY_MODULE_ADD_CONSTANT(module, AP_PANNING); + PY_MODULE_ADD_CONSTANT(module, AP_PITCH); + PY_MODULE_ADD_CONSTANT(module, AP_LOCATION); + PY_MODULE_ADD_CONSTANT(module, AP_ORIENTATION); + // channels constants + PY_MODULE_ADD_CONSTANT(module, CHANNELS_INVALID); + PY_MODULE_ADD_CONSTANT(module, CHANNELS_MONO); + PY_MODULE_ADD_CONSTANT(module, CHANNELS_STEREO); + PY_MODULE_ADD_CONSTANT(module, CHANNELS_STEREO_LFE); + PY_MODULE_ADD_CONSTANT(module, CHANNELS_SURROUND4); + PY_MODULE_ADD_CONSTANT(module, CHANNELS_SURROUND5); + PY_MODULE_ADD_CONSTANT(module, CHANNELS_SURROUND51); + PY_MODULE_ADD_CONSTANT(module, CHANNELS_SURROUND61); + PY_MODULE_ADD_CONSTANT(module, CHANNELS_SURROUND71); + // codec constants + PY_MODULE_ADD_CONSTANT(module, CODEC_INVALID); + PY_MODULE_ADD_CONSTANT(module, CODEC_AAC); + PY_MODULE_ADD_CONSTANT(module, CODEC_AC3); + PY_MODULE_ADD_CONSTANT(module, CODEC_FLAC); + PY_MODULE_ADD_CONSTANT(module, CODEC_MP2); + PY_MODULE_ADD_CONSTANT(module, CODEC_MP3); + PY_MODULE_ADD_CONSTANT(module, CODEC_PCM); + PY_MODULE_ADD_CONSTANT(module, CODEC_VORBIS); + PY_MODULE_ADD_CONSTANT(module, CODEC_OPUS); + // container constants + PY_MODULE_ADD_CONSTANT(module, CONTAINER_INVALID); + PY_MODULE_ADD_CONSTANT(module, CONTAINER_AC3); + PY_MODULE_ADD_CONSTANT(module, CONTAINER_FLAC); + PY_MODULE_ADD_CONSTANT(module, CONTAINER_MATROSKA); + PY_MODULE_ADD_CONSTANT(module, CONTAINER_MP2); + PY_MODULE_ADD_CONSTANT(module, CONTAINER_MP3); + PY_MODULE_ADD_CONSTANT(module, CONTAINER_OGG); + PY_MODULE_ADD_CONSTANT(module, CONTAINER_WAV); + // distance model constants + PY_MODULE_ADD_CONSTANT(module, DISTANCE_MODEL_EXPONENT); + PY_MODULE_ADD_CONSTANT(module, DISTANCE_MODEL_EXPONENT_CLAMPED); + PY_MODULE_ADD_CONSTANT(module, DISTANCE_MODEL_INVERSE); + PY_MODULE_ADD_CONSTANT(module, DISTANCE_MODEL_INVERSE_CLAMPED); + PY_MODULE_ADD_CONSTANT(module, DISTANCE_MODEL_LINEAR); + PY_MODULE_ADD_CONSTANT(module, DISTANCE_MODEL_LINEAR_CLAMPED); + PY_MODULE_ADD_CONSTANT(module, DISTANCE_MODEL_INVALID); + // format constants + PY_MODULE_ADD_CONSTANT(module, FORMAT_INVALID); + PY_MODULE_ADD_CONSTANT(module, FORMAT_FLOAT32); + PY_MODULE_ADD_CONSTANT(module, FORMAT_FLOAT64); + PY_MODULE_ADD_CONSTANT(module, FORMAT_INVALID); + PY_MODULE_ADD_CONSTANT(module, FORMAT_S16); + PY_MODULE_ADD_CONSTANT(module, FORMAT_S24); + PY_MODULE_ADD_CONSTANT(module, FORMAT_S32); + PY_MODULE_ADD_CONSTANT(module, FORMAT_U8); + // rate constants + PY_MODULE_ADD_CONSTANT(module, RATE_INVALID); + PY_MODULE_ADD_CONSTANT(module, RATE_8000); + PY_MODULE_ADD_CONSTANT(module, RATE_16000); + PY_MODULE_ADD_CONSTANT(module, RATE_11025); + PY_MODULE_ADD_CONSTANT(module, RATE_22050); + PY_MODULE_ADD_CONSTANT(module, RATE_32000); + PY_MODULE_ADD_CONSTANT(module, RATE_44100); + PY_MODULE_ADD_CONSTANT(module, RATE_48000); + PY_MODULE_ADD_CONSTANT(module, RATE_88200); + PY_MODULE_ADD_CONSTANT(module, RATE_96000); + PY_MODULE_ADD_CONSTANT(module, RATE_192000); + // status constants + PY_MODULE_ADD_CONSTANT(module, STATUS_INVALID); + PY_MODULE_ADD_CONSTANT(module, STATUS_PAUSED); + PY_MODULE_ADD_CONSTANT(module, STATUS_PLAYING); + PY_MODULE_ADD_CONSTANT(module, STATUS_STOPPED); + + return module; +} + +AUD_API PyObject* AUD_getPythonSound(void* sound) +{ + if(sound) + { + Sound* object = (Sound*) Sound_empty(); + if(object) + { + object->sound = new std::shared_ptr<ISound>(*reinterpret_cast<std::shared_ptr<ISound>*>(sound)); + return (PyObject *) object; + } + } + + return nullptr; +} + +AUD_API void* AUD_getSoundFromPython(PyObject* object) +{ + Sound* sound = checkSound(object); + + if(!sound) + return nullptr; + + return new std::shared_ptr<ISound>(*reinterpret_cast<std::shared_ptr<ISound>*>(sound->sound)); +} diff --git a/extern/audaspace/bindings/python/PyAPI.h b/extern/audaspace/bindings/python/PyAPI.h new file mode 100644 index 00000000000..a413b4813d6 --- /dev/null +++ b/extern/audaspace/bindings/python/PyAPI.h @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +#ifdef __cplusplus +extern "C" { +#endif + +PyMODINIT_FUNC +PyInit_aud(); + +/** + * Retrieves the python factory of a sound. + * \param sound The sound factory. + * \return The python factory. + */ +extern AUD_API PyObject* AUD_getPythonSound(void* sound); + +/** + * Retrieves the sound factory of a python factory. + * \param sound The python factory. + * \return The sound factory. + */ +extern AUD_API void* AUD_getSoundFromPython(PyObject* object); + +#ifdef __cplusplus +} +#endif diff --git a/extern/audaspace/bindings/python/PyDevice.cpp b/extern/audaspace/bindings/python/PyDevice.cpp new file mode 100644 index 00000000000..a6beef57d83 --- /dev/null +++ b/extern/audaspace/bindings/python/PyDevice.cpp @@ -0,0 +1,785 @@ +/******************************************************************************* + * 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 "PyDevice.h" + +#include "PySound.h" +#include "PyHandle.h" + +#include "Exception.h" +#include "devices/IDevice.h" +#include "devices/I3DDevice.h" +#include "devices/DeviceManager.h" +#include "devices/IDeviceFactory.h" + +#include <structmember.h> + +using namespace aud; + +extern PyObject* AUDError; +static const char* device_not_3d_error = "Device is not a 3D device!"; + +// ==================================================================== + +static void +Device_dealloc(Device* self) +{ + if(self->device) + delete reinterpret_cast<std::shared_ptr<IDevice>*>(self->device); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject * +Device_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + Device* self; + + static const char* kwlist[] = {"type", "rate", "channels", "format", "buffer_size", "name", nullptr}; + const char* device = nullptr; + double rate = RATE_48000; + int channels = CHANNELS_STEREO; + int format = FORMAT_FLOAT32; + int buffersize = AUD_DEFAULT_BUFFER_SIZE; + const char* name = ""; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "|sdiiis:Device", const_cast<char**>(kwlist), + &device, &rate, &channels, &format, &buffersize, &name)) + return nullptr; + + if(buffersize < 128) + { + PyErr_SetString(PyExc_ValueError, "buffer_size must be at least 128!"); + return nullptr; + } + + self = (Device*)type->tp_alloc(type, 0); + + if(self != nullptr) + { + DeviceSpecs specs; + specs.channels = (Channels)channels; + specs.format = (SampleFormat)format; + specs.rate = (SampleRate)rate; + + self->device = nullptr; + + try + { + if(!device) + { + auto dev = DeviceManager::getDevice(); + if(!dev) + { + DeviceManager::openDefaultDevice(); + dev = DeviceManager::getDevice(); + } + self->device = new std::shared_ptr<IDevice>(dev); + } + else + { + std::shared_ptr<IDeviceFactory> factory; + if(!*device) + factory = DeviceManager::getDefaultDeviceFactory(); + else + factory = DeviceManager::getDeviceFactory(device); + + if(factory) + { + factory->setName(name); + factory->setSpecs(specs); + factory->setBufferSize(buffersize); + self->device = new std::shared_ptr<IDevice>(factory->openDevice()); + } + } + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + + if(!self->device) + { + Py_DECREF(self); + PyErr_SetString(AUDError, "Unsupported device type!"); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Device_lock_doc, + "lock()\n\n" + "Locks the device so that it's guaranteed, that no samples are " + "read from the streams until :meth:`unlock` is called.\n" + "This is useful if you want to do start/stop/pause/resume some " + "sounds at the same time.\n\n" + ".. note:: The device has to be unlocked as often as locked to be " + "able to continue playback.\n\n" + ".. warning:: Make sure the time between locking and unlocking is " + "as short as possible to avoid clicks."); + +static PyObject * +Device_lock(Device* self) +{ + try + { + (*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->lock(); + Py_RETURN_NONE; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Device_play_doc, + "play(sound, keep=False)\n\n" + "Plays a sound.\n\n" + ":arg sound: The sound to play.\n" + ":type sound: :class:`Sound`\n" + ":arg keep: See :attr:`Handle.keep`.\n" + ":type keep: bool\n" + ":return: The playback handle with which playback can be " + "controlled with.\n" + ":rtype: :class:`Handle`"); + +static PyObject * +Device_play(Device* self, PyObject* args, PyObject* kwds) +{ + PyObject* object; + PyObject* keepo = nullptr; + + bool keep = false; + + static const char* kwlist[] = {"sound", "keep", nullptr}; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:play", const_cast<char**>(kwlist), &object, &keepo)) + return nullptr; + + Sound* sound = checkSound(object); + + if(!sound) + return nullptr; + + if(keepo != nullptr) + { + if(!PyBool_Check(keepo)) + { + PyErr_SetString(PyExc_TypeError, "keep is not a boolean!"); + return nullptr; + } + + keep = keepo == Py_True; + } + + Handle* handle; + + handle = (Handle*)Handle_empty(); + if(handle != nullptr) + { + try + { + handle->handle = new std::shared_ptr<IHandle>((*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->play(*reinterpret_cast<std::shared_ptr<ISound>*>(sound->sound), keep)); + } + catch(Exception& e) + { + Py_DECREF(handle); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)handle; +} + +PyDoc_STRVAR(M_aud_Device_stopAll_doc, + "stopAll()\n\n" + "Stops all playing and paused sounds."); + +static PyObject * +Device_stopAll(Device* self) +{ + try + { + (*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->stopAll(); + Py_RETURN_NONE; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Device_unlock_doc, + "unlock()\n\n" + "Unlocks the device after a lock call, see :meth:`lock` for " + "details."); + +static PyObject * +Device_unlock(Device* self) +{ + try + { + (*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->unlock(); + Py_RETURN_NONE; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static PyMethodDef Device_methods[] = { + {"lock", (PyCFunction)Device_lock, METH_NOARGS, + M_aud_Device_lock_doc + }, + {"play", (PyCFunction)Device_play, METH_VARARGS | METH_KEYWORDS, + M_aud_Device_play_doc + }, + {"stopAll", (PyCFunction)Device_stopAll, METH_NOARGS, + M_aud_Device_stopAll_doc + }, + {"unlock", (PyCFunction)Device_unlock, METH_NOARGS, + M_aud_Device_unlock_doc + }, + {nullptr} /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_Device_channels_doc, + "The channel count of the device."); + +static PyObject * +Device_get_channels(Device* self, void* nothing) +{ + try + { + DeviceSpecs specs = (*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->getSpecs(); + return Py_BuildValue("i", specs.channels); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Device_distance_model_doc, + "The distance model of the device.\n\n" + ".. seealso:: http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specification.htm#_Toc199835864"); + +static PyObject * +Device_get_distance_model(Device* self, void* nothing) +{ + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + return Py_BuildValue("i", int(device->getDistanceModel())); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Device_set_distance_model(Device* self, PyObject* args, void* nothing) +{ + int model; + + if(!PyArg_Parse(args, "i:distance_model", &model)) + return -1; + + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + device->setDistanceModel(DistanceModel(model)); + return 0; + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Device_doppler_factor_doc, + "The doppler factor of the device.\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 * +Device_get_doppler_factor(Device* self, void* nothing) +{ + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + return Py_BuildValue("f", device->getDopplerFactor()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Device_set_doppler_factor(Device* self, PyObject* args, void* nothing) +{ + float factor; + + if(!PyArg_Parse(args, "f:doppler_factor", &factor)) + return -1; + + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + device->setDopplerFactor(factor); + return 0; + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Device_format_doc, + "The native sample format of the device."); + +static PyObject * +Device_get_format(Device* self, void* nothing) +{ + try + { + DeviceSpecs specs = (*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->getSpecs(); + return Py_BuildValue("i", specs.format); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Device_listener_location_doc, + "The listeners's location in 3D space, a 3D tuple of floats."); + +static PyObject * +Device_get_listener_location(Device* self, void* nothing) +{ + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + Vector3 v = device->getListenerLocation(); + return Py_BuildValue("(fff)", v.x(), v.y(), v.z()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +Device_set_listener_location(Device* self, PyObject* args, void* nothing) +{ + float x, y, z; + + if(!PyArg_Parse(args, "(fff):listener_location", &x, &y, &z)) + return -1; + + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + Vector3 location(x, y, z); + device->setListenerLocation(location); + return 0; + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Device_listener_orientation_doc, + "The listener's orientation in 3D space as quaternion, a 4 float tuple."); + +static PyObject * +Device_get_listener_orientation(Device* self, void* nothing) +{ + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + Quaternion o = device->getListenerOrientation(); + return Py_BuildValue("(ffff)", o.w(), o.x(), o.y(), o.z()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +Device_set_listener_orientation(Device* self, PyObject* args, void* nothing) +{ + float w, x, y, z; + + if(!PyArg_Parse(args, "(ffff):listener_orientation", &w, &x, &y, &z)) + return -1; + + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + Quaternion orientation(w, x, y, z); + device->setListenerOrientation(orientation); + return 0; + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Device_listener_velocity_doc, + "The listener's velocity in 3D space, a 3D tuple of floats."); + +static PyObject * +Device_get_listener_velocity(Device* self, void* nothing) +{ + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + Vector3 v = device->getListenerVelocity(); + return Py_BuildValue("(fff)", v.x(), v.y(), v.z()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +Device_set_listener_velocity(Device* self, PyObject* args, void* nothing) +{ + float x, y, z; + + if(!PyArg_Parse(args, "(fff):listener_velocity", &x, &y, &z)) + return -1; + + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + Vector3 velocity(x, y, z); + device->setListenerVelocity(velocity); + return 0; + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Device_rate_doc, + "The sampling rate of the device in Hz."); + +static PyObject * +Device_get_rate(Device* self, void* nothing) +{ + try + { + DeviceSpecs specs = (*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->getSpecs(); + return Py_BuildValue("d", specs.rate); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Device_speed_of_sound_doc, + "The speed of sound of the device.\n" + "The speed of sound in air is typically 343.3 m/s."); + +static PyObject * +Device_get_speed_of_sound(Device* self, void* nothing) +{ + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + return Py_BuildValue("f", device->getSpeedOfSound()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Device_set_speed_of_sound(Device* self, PyObject* args, void* nothing) +{ + float speed; + + if(!PyArg_Parse(args, "f:speed_of_sound", &speed)) + return -1; + + try + { + I3DDevice* device = dynamic_cast<I3DDevice*>(reinterpret_cast<std::shared_ptr<IDevice>*>(self->device)->get()); + if(device) + { + device->setSpeedOfSound(speed); + return 0; + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Device_volume_doc, + "The overall volume of the device."); + +static PyObject * +Device_get_volume(Device* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->getVolume()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Device_set_volume(Device* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:volume", &volume)) + return -1; + + try + { + (*reinterpret_cast<std::shared_ptr<IDevice>*>(self->device))->setVolume(volume); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return -1; + } +} + +static PyGetSetDef Device_properties[] = { + {(char*)"channels", (getter)Device_get_channels, nullptr, + M_aud_Device_channels_doc, nullptr }, + {(char*)"distance_model", (getter)Device_get_distance_model, (setter)Device_set_distance_model, + M_aud_Device_distance_model_doc, nullptr }, + {(char*)"doppler_factor", (getter)Device_get_doppler_factor, (setter)Device_set_doppler_factor, + M_aud_Device_doppler_factor_doc, nullptr }, + {(char*)"format", (getter)Device_get_format, nullptr, + M_aud_Device_format_doc, nullptr }, + {(char*)"listener_location", (getter)Device_get_listener_location, (setter)Device_set_listener_location, + M_aud_Device_listener_location_doc, nullptr }, + {(char*)"listener_orientation", (getter)Device_get_listener_orientation, (setter)Device_set_listener_orientation, + M_aud_Device_listener_orientation_doc, nullptr }, + {(char*)"listener_velocity", (getter)Device_get_listener_velocity, (setter)Device_set_listener_velocity, + M_aud_Device_listener_velocity_doc, nullptr }, + {(char*)"rate", (getter)Device_get_rate, nullptr, + M_aud_Device_rate_doc, nullptr }, + {(char*)"speed_of_sound", (getter)Device_get_speed_of_sound, (setter)Device_set_speed_of_sound, + M_aud_Device_speed_of_sound_doc, nullptr }, + {(char*)"volume", (getter)Device_get_volume, (setter)Device_set_volume, + M_aud_Device_volume_doc, nullptr }, + {nullptr} /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_Device_doc, + "Device objects represent an audio output backend like OpenAL or " + "SDL, but might also represent a file output or RAM buffer " + "output."); + +static PyTypeObject DeviceType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.Device", /* tp_name */ + sizeof(Device), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Device_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_Device_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Device_methods, /* tp_methods */ + 0, /* tp_members */ + Device_properties, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Device_new, /* tp_new */ +}; + +AUD_API PyObject* Device_empty() +{ + return DeviceType.tp_alloc(&DeviceType, 0); +} + + +AUD_API Device* checkDevice(PyObject* device) +{ + if(!PyObject_TypeCheck(device, &DeviceType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type Device!"); + return nullptr; + } + + return (Device*)device; +} + + +bool initializeDevice() +{ + return PyType_Ready(&DeviceType) >= 0; +} + + +void addDeviceToModule(PyObject* module) +{ + Py_INCREF(&DeviceType); + PyModule_AddObject(module, "Device", (PyObject *)&DeviceType); +} diff --git a/extern/audaspace/bindings/python/PyDevice.h b/extern/audaspace/bindings/python/PyDevice.h new file mode 100644 index 00000000000..610b5b4cd23 --- /dev/null +++ b/extern/audaspace/bindings/python/PyDevice.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_IDevice; + +typedef struct { + PyObject_HEAD + Reference_IDevice* device; +} Device; + +extern AUD_API PyObject* Device_empty(); +extern AUD_API Device* checkDevice(PyObject* device); + +bool initializeDevice(); +void addDeviceToModule(PyObject* module); diff --git a/extern/audaspace/bindings/python/PyDynamicMusic.cpp b/extern/audaspace/bindings/python/PyDynamicMusic.cpp new file mode 100644 index 00000000000..d49f73737c2 --- /dev/null +++ b/extern/audaspace/bindings/python/PyDynamicMusic.cpp @@ -0,0 +1,467 @@ +/******************************************************************************* +* Copyright 2015-2016 Juan Francisco Crespo Galán +* +* 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 "PyDynamicMusic.h" +#include "PySound.h" +#include "PyHandle.h" +#include "PyDevice.h" + +#include "Exception.h" +#include "fx/DynamicMusic.h" + +extern PyObject* AUDError; + +static PyObject * +DynamicMusic_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + DynamicMusicP* self = (DynamicMusicP*)type->tp_alloc(type, 0); + + if(self != nullptr) + { + PyObject* object; + if(!PyArg_ParseTuple(args, "O:device", &object)) + return nullptr; + Device* device = checkDevice(object); + + try + { + self->dynamicMusic = new std::shared_ptr<aud::DynamicMusic>(new aud::DynamicMusic(*reinterpret_cast<std::shared_ptr<aud::IDevice>*>(device->device))); + } + catch(aud::Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +static void +DynamicMusic_dealloc(DynamicMusicP* self) +{ + if(self->dynamicMusic) + delete reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyDoc_STRVAR(M_aud_DynamicMusic_addScene_doc, + "addScene(scene)\n\n" + "Adds a new scene.\n\n" + ":arg scene: The scene sound.\n" + ":type scene: :class:`Sound`\n" + ":return: The new scene id.\n" + ":rtype: int"); + +static PyObject * +DynamicMusic_addScene(DynamicMusicP* self, PyObject* args) +{ + PyObject* object; + if(!PyArg_Parse(args, "O:sound", &object)) + return nullptr; + + Sound* sound = checkSound(object); + if(!sound) + return nullptr; + + try + { + return Py_BuildValue("i", (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->addScene(*reinterpret_cast<std::shared_ptr<aud::ISound>*>(sound->sound))); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_DynamicMusic_addTransition_doc, + "addTransition(ini, end, transition)\n\n" + "Adds a new scene.\n\n" + ":arg ini: the initial scene foor the transition.\n" + ":type ini: int\n" + ":arg end: The final scene for the transition.\n" + ":type end: int\n" + ":arg transition: The transition sound.\n" + ":type transition: :class:`Sound`\n" + ":return: false if the ini or end scenes don't exist, true othrwise.\n" + ":rtype: bool"); + +static PyObject * +DynamicMusic_addTransition(DynamicMusicP* self, PyObject* args) +{ + PyObject* object; + int ini, end; + if(!PyArg_ParseTuple(args, "iiO:sound", &ini, &end, &object)) + return nullptr; + Sound* sound = checkSound(object); + if(!sound) + return nullptr; + + try + { + (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->addTransition(ini, end, *reinterpret_cast<std::shared_ptr<aud::ISound>*>(sound->sound)); + Py_RETURN_NONE; + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_DynamicMusic_resume_doc, + "resume()\n\n" + "Resumes playback of the scene.\n\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool"); + +static PyObject * +DynamicMusic_resume(DynamicMusicP* self) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->resume()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_DynamicMusic_pause_doc, + "pause()\n\n" + "Pauses playback of the scene.\n\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool"); + +static PyObject * +DynamicMusic_pause(DynamicMusicP* self) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->pause()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_DynamicMusic_stop_doc, + "stop()\n\n" + "Stops playback of the scene.\n\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool\n\n"); + +static PyObject * +DynamicMusic_stop(DynamicMusicP* self) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->stop()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static PyMethodDef DynamicMusic_methods[] = { + { "addScene", (PyCFunction)DynamicMusic_addScene, METH_O, + M_aud_DynamicMusic_addScene_doc + }, + { "addTransition", (PyCFunction)DynamicMusic_addTransition, METH_VARARGS, + M_aud_DynamicMusic_addTransition_doc + }, + { "resume", (PyCFunction)DynamicMusic_resume, METH_NOARGS, + M_aud_DynamicMusic_resume_doc + }, + { "pause", (PyCFunction)DynamicMusic_pause, METH_NOARGS, + M_aud_DynamicMusic_pause_doc + }, + { "stop", (PyCFunction)DynamicMusic_stop, METH_NOARGS, + M_aud_DynamicMusic_stop_doc + }, + { nullptr } /* Sentinel */ +}; + +///////////////////////////////////////////////////// + +PyDoc_STRVAR(M_aud_DynamicMusic_status_doc, + "Whether the scene is playing, paused or stopped (=invalid)."); + +static PyObject * +DynamicMusic_get_status(DynamicMusicP* self, void* nothing) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->getStatus()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_DynamicMusic_position_doc, + "The playback position of the scene in seconds."); + +static int +DynamicMusic_set_position(DynamicMusicP* self, PyObject* args, void* nothing) +{ + float position; + + if(!PyArg_Parse(args, "f:position", &position)) + return -1; + + try + { + if((*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->seek(position)) + return 0; + PyErr_SetString(AUDError, "Couldn't seek the sound!"); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyObject * +DynamicMusic_get_position(DynamicMusicP* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->getPosition()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_DynamicMusic_fadeTime_doc, + "The length in seconds of the crossfade transition"); + +static int +DynamicMusic_set_fadeTime(DynamicMusicP* self, PyObject* args, void* nothing) +{ + float fadeTime; + + if(!PyArg_Parse(args, "f:fadeTime", &fadeTime)) + return -1; + + try + { + (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->setFadeTime(fadeTime); + return 0; + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyObject * +DynamicMusic_get_fadeTime(DynamicMusicP* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->getFadeTime()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_DynamicMusic_scene_doc, + "The current scene"); + +static int +DynamicMusic_set_scene(DynamicMusicP* self, PyObject* args, void* nothing) +{ + int scene; + + if(!PyArg_Parse(args, "i:scene", &scene)) + return -1; + + try + { + if((*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->changeScene(scene)) + return 0; + PyErr_SetString(AUDError, "Couldn't change the scene!"); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyObject * +DynamicMusic_get_scene(DynamicMusicP* self, void* nothing) +{ + try + { + return Py_BuildValue("i", (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->getScene()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_DynamicMusic_volume_doc, + "The volume of the scene."); + +static int +DynamicMusic_set_volume(DynamicMusicP* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:volume", &volume)) + return -1; + + try + { + if((*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->setVolume(volume)) + return 0; + PyErr_SetString(AUDError, "Couldn't change the volume!"); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyObject * +DynamicMusic_get_volume(DynamicMusicP* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->getVolume()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static PyGetSetDef DynamicMusic_properties[] = { + { (char*)"status", (getter)DynamicMusic_get_status, nullptr, + M_aud_DynamicMusic_status_doc, nullptr }, + { (char*)"position", (getter)DynamicMusic_get_position, (setter)DynamicMusic_set_position, + M_aud_DynamicMusic_position_doc, nullptr }, + { (char*)"fadeTime", (getter)DynamicMusic_get_fadeTime, (setter)DynamicMusic_set_fadeTime, + M_aud_DynamicMusic_fadeTime_doc, nullptr }, + { (char*)"scene", (getter)DynamicMusic_get_scene, (setter)DynamicMusic_set_scene, + M_aud_DynamicMusic_scene_doc, nullptr }, + { (char*)"volume", (getter)DynamicMusic_get_volume, (setter)DynamicMusic_set_volume, + M_aud_DynamicMusic_volume_doc, nullptr }, + { nullptr } /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_DynamicMusic_doc, + "The DynamicMusic object allows to play music depending on a current scene, scene changes are managed by the class, with the possibility of custom transitions.\n" + "The default transition is a crossfade effect, and the default scene is silent and has id 0"); + +PyTypeObject DynamicMusicType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.DynamicMusic", /* tp_name */ + sizeof(DynamicMusicP), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)DynamicMusic_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_DynamicMusic_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + DynamicMusic_methods, /* tp_methods */ + 0, /* tp_members */ + DynamicMusic_properties, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + DynamicMusic_new, /* tp_new */ +}; + +AUD_API PyObject* DynamicMusic_empty() +{ + return DynamicMusicType.tp_alloc(&DynamicMusicType, 0); +} + + +AUD_API DynamicMusicP* checkDynamicMusic(PyObject* dynamicMusic) +{ + if(!PyObject_TypeCheck(dynamicMusic, &DynamicMusicType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type DynamicMusic!"); + return nullptr; + } + + return (DynamicMusicP*)dynamicMusic; +} + + +bool initializeDynamicMusic() +{ + return PyType_Ready(&DynamicMusicType) >= 0; +} + + +void addDynamicMusicToModule(PyObject* module) +{ + Py_INCREF(&DynamicMusicType); + PyModule_AddObject(module, "DynamicMusic", (PyObject *)&DynamicMusicType); +}
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/PyDynamicMusic.h b/extern/audaspace/bindings/python/PyDynamicMusic.h new file mode 100644 index 00000000000..f19de2d8c75 --- /dev/null +++ b/extern/audaspace/bindings/python/PyDynamicMusic.h @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright 2015-2016 Juan Francisco Crespo Galán +* +* 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. +******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_DynamicMusic; + +typedef struct { + PyObject_HEAD + Reference_DynamicMusic* dynamicMusic; +} DynamicMusicP; + +extern AUD_API PyObject* DynamicMusic_empty(); +extern AUD_API DynamicMusicP* checkDynamicMusic(PyObject* dynamicMusic); + +bool initializeDynamicMusic(); +void addDynamicMusicToModule(PyObject* module);
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/PyHRTF.cpp b/extern/audaspace/bindings/python/PyHRTF.cpp new file mode 100644 index 00000000000..2a5b6be624f --- /dev/null +++ b/extern/audaspace/bindings/python/PyHRTF.cpp @@ -0,0 +1,247 @@ +/******************************************************************************* +* Copyright 2009-2015 Juan Francisco Crespo Galán +* +* 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 "PyHRTF.h" +#include "PySound.h" + +#include "Exception.h" +#include "fx/HRTF.h" +#include "fx/HRTFLoader.h" + +extern PyObject* AUDError; + +static PyObject * +HRTF_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + HRTFP* self = (HRTFP*)type->tp_alloc(type, 0); + + if(self != nullptr) + { + try + { + self->hrtf = new std::shared_ptr<aud::HRTF>(new aud::HRTF()); + } + catch(aud::Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +static void +HRTF_dealloc(HRTFP* self) +{ + if(self->hrtf) + delete reinterpret_cast<std::shared_ptr<aud::HRTF>*>(self->hrtf); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyDoc_STRVAR(M_aud_HRTF_addImpulseResponse_doc, + "addImpulseResponseFromSound(sound, azimuth, elevation)\n\n" + "Adds a new hrtf to the HRTF object\n\n" + ":arg sound: The sound that contains the hrtf.\n" + ":type sound: :class:`Sound`\n" + ":arg azimuth: The azimuth angle of the hrtf.\n" + ":type azimuth: float\n" + ":arg elevation: The elevation angle of the hrtf.\n" + ":type elevation: float\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool"); + +static PyObject * +HRTF_addImpulseResponseFromSound(HRTFP* self, PyObject* args) +{ + PyObject* object; + float azimuth, elevation; + + if(!PyArg_ParseTuple(args, "Off:hrtf", &object, &azimuth, &elevation)) + return nullptr; + + Sound* ir = checkSound(object); + if(!ir) + return nullptr; + + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::HRTF>*>(self->hrtf))->addImpulseResponse(std::make_shared<aud::StreamBuffer>(*reinterpret_cast<std::shared_ptr<aud::ISound>*>(ir->sound)), azimuth, elevation)); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_HRTF_loadLeftHrtfSet_doc, + "loadLeftHrtfSet(extension, directory)\n\n" + "Loads all HRTFs from a directory.\n\n" + ":arg extension: The file extension of the hrtfs.\n" + ":type extension: string\n" + ":arg directory: The path to where the HRTF files are located.\n" + ":type extension: string\n" + ":return: The loaded :class:`HRTF` object.\n" + ":rtype: :class:`HRTF`\n\n"); + +static PyObject * +HRTF_loadLeftHrtfSet(PyTypeObject* type, PyObject* args) +{ + const char* dir = nullptr; + const char* ext = nullptr; + + if(!PyArg_ParseTuple(args, "ss:hrtf", &ext, &dir)) + return nullptr; + + HRTFP* self; + self = (HRTFP*)type->tp_alloc(type, 0); + + try + { + self->hrtf = new std::shared_ptr<aud::HRTF>(aud::HRTFLoader::loadLeftHRTFs(ext, dir)); + } + catch(aud::Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_HRTF_loadRightHrtfSet_doc, + "loadLeftHrtfSet(extension, directory)\n\n" + "Loads all HRTFs from a directory.\n\n" + ":arg extension: The file extension of the hrtfs.\n" + ":type extension: string\n" + ":arg directory: The path to where the HRTF files are located.\n" + ":type extension: string\n" + ":return: The loaded :class:`HRTF` object.\n" + ":rtype: :class:`HRTF`\n\n"); + +static PyObject * +HRTF_loadRightHrtfSet(PyTypeObject* type, PyObject* args) +{ + const char* dir = nullptr; + const char* ext = nullptr; + + if(!PyArg_ParseTuple(args, "ss:hrtf", &ext, &dir)) + return nullptr; + + HRTFP* self; + self = (HRTFP*)type->tp_alloc(type, 0); + + try + { + self->hrtf = new std::shared_ptr<aud::HRTF>(aud::HRTFLoader::loadRightHRTFs(ext, dir)); + } + catch(aud::Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + return (PyObject *)self; +} + +static PyMethodDef HRTF_methods[] = { + { "addImpulseResponseFromSound", (PyCFunction)HRTF_addImpulseResponseFromSound, METH_VARARGS | METH_KEYWORDS, + M_aud_HRTF_addImpulseResponse_doc + }, + { "loadLeftHrtfSet", (PyCFunction)HRTF_loadLeftHrtfSet, METH_VARARGS | METH_CLASS, + M_aud_HRTF_loadLeftHrtfSet_doc + }, + { "loadRightHrtfSet", (PyCFunction)HRTF_loadRightHrtfSet, METH_VARARGS | METH_CLASS, + M_aud_HRTF_loadRightHrtfSet_doc + }, + { nullptr } /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_HRTF_doc, + "An HRTF object represents a set of head related transfer functions as impulse responses. It's used for binaural sound"); + +PyTypeObject HRTFType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.HRTF", /* tp_name */ + sizeof(HRTFP), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)HRTF_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_HRTF_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + HRTF_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + HRTF_new, /* tp_new */ +}; + +AUD_API PyObject* HRTF_empty() +{ + return HRTFType.tp_alloc(&HRTFType, 0); +} + + +AUD_API HRTFP* checkHRTF(PyObject* hrtf) +{ + if(!PyObject_TypeCheck(hrtf, &HRTFType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type HRTF!"); + return nullptr; + } + + return (HRTFP*)hrtf; +} + + +bool initializeHRTF() +{ + return PyType_Ready(&HRTFType) >= 0; +} + + +void addHRTFToModule(PyObject* module) +{ + Py_INCREF(&HRTFType); + PyModule_AddObject(module, "HRTF", (PyObject *)&HRTFType); +} diff --git a/extern/audaspace/bindings/python/PyHRTF.h b/extern/audaspace/bindings/python/PyHRTF.h new file mode 100644 index 00000000000..0445069929f --- /dev/null +++ b/extern/audaspace/bindings/python/PyHRTF.h @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright 2009-2015 Juan Francisco Crespo Galán +* +* 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. +******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_HRTF; + +typedef struct { + PyObject_HEAD + Reference_HRTF* hrtf; +} HRTFP; + +extern AUD_API PyObject* HRTF_empty(); +extern AUD_API HRTFP* checkHRTF(PyObject* hrtf); + +bool initializeHRTF(); +void addHRTFToModule(PyObject* module);
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/PyHandle.cpp b/extern/audaspace/bindings/python/PyHandle.cpp new file mode 100644 index 00000000000..7f7a7660049 --- /dev/null +++ b/extern/audaspace/bindings/python/PyHandle.cpp @@ -0,0 +1,1126 @@ +/******************************************************************************* + * 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 "PyHandle.h" + +#include "devices/IHandle.h" +#include "devices/I3DHandle.h" +#include "Exception.h" + +#include <memory> + +#include <structmember.h> + +using namespace aud; + +extern PyObject* AUDError; +static const char* device_not_3d_error = "Device is not a 3D device!"; + +static void +Handle_dealloc(Handle* self) +{ + if(self->handle) + delete reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyDoc_STRVAR(M_aud_Handle_pause_doc, + "pause()\n\n" + "Pauses playback.\n\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool"); + +static PyObject * +Handle_pause(Handle* self) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->pause()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Handle_resume_doc, + "resume()\n\n" + "Resumes playback.\n\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool"); + +static PyObject * +Handle_resume(Handle* self) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->resume()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Handle_stop_doc, + "stop()\n\n" + "Stops playback.\n\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool\n\n" + ".. note:: This makes the handle invalid."); + +static PyObject * +Handle_stop(Handle* self) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->stop()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static PyMethodDef Handle_methods[] = { + {"pause", (PyCFunction)Handle_pause, METH_NOARGS, + M_aud_Handle_pause_doc + }, + {"resume", (PyCFunction)Handle_resume, METH_NOARGS, + M_aud_Handle_resume_doc + }, + {"stop", (PyCFunction)Handle_stop, METH_NOARGS, + M_aud_Handle_stop_doc + }, + {nullptr} /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_Handle_attenuation_doc, + "This factor is used for distance based attenuation of the " + "source.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +Handle_get_attenuation(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return Py_BuildValue("f", handle->getAttenuation()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_attenuation(Handle* self, PyObject* args, void* nothing) +{ + float factor; + + if(!PyArg_Parse(args, "f:attenuation", &factor)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setAttenuation(factor)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the attenuation!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_cone_angle_inner_doc, + "The opening angle of the inner cone of the source. If the cone " + "values of a source are set there are two (audible) cones with " + "the apex at the :attr:`location` of the source and with infinite " + "height, heading in the direction of the source's " + ":attr:`orientation`.\n" + "In the inner cone the volume is normal. Outside the outer cone " + "the volume will be :attr:`cone_volume_outer` and in the area " + "between the volume will be interpolated linearly."); + +static PyObject * +Handle_get_cone_angle_inner(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return Py_BuildValue("f", handle->getConeAngleInner()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_cone_angle_inner(Handle* self, PyObject* args, void* nothing) +{ + float angle; + + if(!PyArg_Parse(args, "f:cone_angle_inner", &angle)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setConeAngleInner(angle)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the cone inner angle!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_cone_angle_outer_doc, + "The opening angle of the outer cone of the source.\n\n" + ".. seealso:: :attr:`cone_angle_inner`"); + +static PyObject * +Handle_get_cone_angle_outer(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return Py_BuildValue("f", handle->getConeAngleOuter()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_cone_angle_outer(Handle* self, PyObject* args, void* nothing) +{ + float angle; + + if(!PyArg_Parse(args, "f:cone_angle_outer", &angle)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setConeAngleOuter(angle)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the cone outer angle!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_cone_volume_outer_doc, + "The volume outside the outer cone of the source.\n\n" + ".. seealso:: :attr:`cone_angle_inner`"); + +static PyObject * +Handle_get_cone_volume_outer(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return Py_BuildValue("f", handle->getConeVolumeOuter()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_cone_volume_outer(Handle* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:cone_volume_outer", &volume)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setConeVolumeOuter(volume)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the cone outer volume!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_distance_maximum_doc, + "The maximum distance of the source.\n" + "If the listener is further away the source volume will be 0.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +Handle_get_distance_maximum(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return Py_BuildValue("f", handle->getDistanceMaximum()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_distance_maximum(Handle* self, PyObject* args, void* nothing) +{ + float distance; + + if(!PyArg_Parse(args, "f:distance_maximum", &distance)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setDistanceMaximum(distance)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the maximum distance!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_distance_reference_doc, + "The reference distance of the source.\n" + "At this distance the volume will be exactly :attr:`volume`.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +Handle_get_distance_reference(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return Py_BuildValue("f", handle->getDistanceReference()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_distance_reference(Handle* self, PyObject* args, void* nothing) +{ + float distance; + + if(!PyArg_Parse(args, "f:distance_reference", &distance)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setDistanceReference(distance)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the reference distance!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_keep_doc, + "Whether the sound should be kept paused in the device when its " + "end is reached.\n" + "This can be used to seek the sound to some position and start " + "playback again.\n\n" + ".. warning:: If this is set to true and you forget stopping this " + "equals a memory leak as the handle exists until the device is " + "destroyed."); + +static PyObject * +Handle_get_keep(Handle* self, void* nothing) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getKeep()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_keep(Handle* self, PyObject* args, void* nothing) +{ + if(!PyBool_Check(args)) + { + PyErr_SetString(PyExc_TypeError, "keep is not a boolean!"); + return -1; + } + + bool keep = args == Py_True; + + try + { + if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->setKeep(keep)) + return 0; + PyErr_SetString(AUDError, "Couldn't set keep of the sound!"); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_location_doc, + "The source's location in 3D space, a 3D tuple of floats."); + +static PyObject * +Handle_get_location(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + Vector3 v = handle->getLocation(); + return Py_BuildValue("(fff)", v.x(), v.y(), v.z()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +Handle_set_location(Handle* self, PyObject* args, void* nothing) +{ + float x, y, z; + + if(!PyArg_Parse(args, "(fff):location", &x, &y, &z)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + Vector3 location(x, y, z); + if(handle->setLocation(location)) + return 0; + PyErr_SetString(AUDError, "Location couldn't be set!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_loop_count_doc, + "The (remaining) loop count of the sound. A negative value indicates infinity."); + +static PyObject * +Handle_get_loop_count(Handle* self, void* nothing) +{ + try + { + return Py_BuildValue("i", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getLoopCount()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_loop_count(Handle* self, PyObject* args, void* nothing) +{ + int loops; + + if(!PyArg_Parse(args, "i:loop_count", &loops)) + return -1; + + try + { + if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->setLoopCount(loops)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the loop count!"); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_orientation_doc, + "The source's orientation in 3D space as quaternion, a 4 float tuple."); + +static PyObject * +Handle_get_orientation(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + Quaternion o = handle->getOrientation(); + return Py_BuildValue("(ffff)", o.w(), o.x(), o.y(), o.z()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +Handle_set_orientation(Handle* self, PyObject* args, void* nothing) +{ + float w, x, y, z; + + if(!PyArg_Parse(args, "(ffff):orientation", &w, &x, &y, &z)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + Quaternion orientation(w, x, y, z); + if(handle->setOrientation(orientation)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the orientation!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_pitch_doc, + "The pitch of the sound."); + +static PyObject * +Handle_get_pitch(Handle* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getPitch()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_pitch(Handle* self, PyObject* args, void* nothing) +{ + float pitch; + + if(!PyArg_Parse(args, "f:pitch", &pitch)) + return -1; + + try + { + if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->setPitch(pitch)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the sound pitch!"); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_position_doc, + "The playback position of the sound in seconds."); + +static PyObject * +Handle_get_position(Handle* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getPosition()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_position(Handle* self, PyObject* args, void* nothing) +{ + float position; + + if(!PyArg_Parse(args, "f:position", &position)) + return -1; + + try + { + if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->seek(position)) + return 0; + PyErr_SetString(AUDError, "Couldn't seek the sound!"); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_relative_doc, + "Whether the source's location, velocity and orientation is relative or absolute to the listener."); + +static PyObject * +Handle_get_relative(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return PyBool_FromLong((long)handle->isRelative()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +Handle_set_relative(Handle* self, PyObject* args, void* nothing) +{ + if(!PyBool_Check(args)) + { + PyErr_SetString(PyExc_TypeError, "Value is not a boolean!"); + return -1; + } + + bool relative = (args == Py_True); + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setRelative(relative)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the relativeness!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_status_doc, + "Whether the sound is playing, paused or stopped (=invalid)."); + +static PyObject * +Handle_get_status(Handle* self, void* nothing) +{ + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getStatus()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Handle_velocity_doc, + "The source's velocity in 3D space, a 3D tuple of floats."); + +static PyObject * +Handle_get_velocity(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + Vector3 v = handle->getVelocity(); + return Py_BuildValue("(fff)", v.x(), v.y(), v.z()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +Handle_set_velocity(Handle* self, PyObject* args, void* nothing) +{ + float x, y, z; + + if(!PyArg_Parse(args, "(fff):velocity", &x, &y, &z)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + Vector3 velocity(x, y, z); + if(handle->setVelocity(velocity)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the velocity!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_volume_doc, + "The volume of the sound."); + +static PyObject * +Handle_get_volume(Handle* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getVolume()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_volume(Handle* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:volume", &volume)) + return -1; + + try + { + if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->setVolume(volume)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the sound volume!"); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_volume_maximum_doc, + "The maximum volume of the source.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +Handle_get_volume_maximum(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return Py_BuildValue("f", handle->getVolumeMaximum()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_volume_maximum(Handle* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:volume_maximum", &volume)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setVolumeMaximum(volume)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the maximum volume!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_Handle_volume_minimum_doc, + "The minimum volume of the source.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +Handle_get_volume_minimum(Handle* self, void* nothing) +{ + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + return Py_BuildValue("f", handle->getVolumeMinimum()); + } + else + { + PyErr_SetString(AUDError, device_not_3d_error); + return nullptr; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +Handle_set_volume_minimum(Handle* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:volume_minimum", &volume)) + return -1; + + try + { + I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get()); + if(handle) + { + if(handle->setVolumeMinimum(volume)) + return 0; + PyErr_SetString(AUDError, "Couldn't set the minimum volume!"); + } + else + PyErr_SetString(AUDError, device_not_3d_error); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyGetSetDef Handle_properties[] = { + {(char*)"attenuation", (getter)Handle_get_attenuation, (setter)Handle_set_attenuation, + M_aud_Handle_attenuation_doc, nullptr }, + {(char*)"cone_angle_inner", (getter)Handle_get_cone_angle_inner, (setter)Handle_set_cone_angle_inner, + M_aud_Handle_cone_angle_inner_doc, nullptr }, + {(char*)"cone_angle_outer", (getter)Handle_get_cone_angle_outer, (setter)Handle_set_cone_angle_outer, + M_aud_Handle_cone_angle_outer_doc, nullptr }, + {(char*)"cone_volume_outer", (getter)Handle_get_cone_volume_outer, (setter)Handle_set_cone_volume_outer, + M_aud_Handle_cone_volume_outer_doc, nullptr }, + {(char*)"distance_maximum", (getter)Handle_get_distance_maximum, (setter)Handle_set_distance_maximum, + M_aud_Handle_distance_maximum_doc, nullptr }, + {(char*)"distance_reference", (getter)Handle_get_distance_reference, (setter)Handle_set_distance_reference, + M_aud_Handle_distance_reference_doc, nullptr }, + {(char*)"keep", (getter)Handle_get_keep, (setter)Handle_set_keep, + M_aud_Handle_keep_doc, nullptr }, + {(char*)"location", (getter)Handle_get_location, (setter)Handle_set_location, + M_aud_Handle_location_doc, nullptr }, + {(char*)"loop_count", (getter)Handle_get_loop_count, (setter)Handle_set_loop_count, + M_aud_Handle_loop_count_doc, nullptr }, + {(char*)"orientation", (getter)Handle_get_orientation, (setter)Handle_set_orientation, + M_aud_Handle_orientation_doc, nullptr }, + {(char*)"pitch", (getter)Handle_get_pitch, (setter)Handle_set_pitch, + M_aud_Handle_pitch_doc, nullptr }, + {(char*)"position", (getter)Handle_get_position, (setter)Handle_set_position, + M_aud_Handle_position_doc, nullptr }, + {(char*)"relative", (getter)Handle_get_relative, (setter)Handle_set_relative, + M_aud_Handle_relative_doc, nullptr }, + {(char*)"status", (getter)Handle_get_status, nullptr, + M_aud_Handle_status_doc, nullptr }, + {(char*)"velocity", (getter)Handle_get_velocity, (setter)Handle_set_velocity, + M_aud_Handle_velocity_doc, nullptr }, + {(char*)"volume", (getter)Handle_get_volume, (setter)Handle_set_volume, + M_aud_Handle_volume_doc, nullptr }, + {(char*)"volume_maximum", (getter)Handle_get_volume_maximum, (setter)Handle_set_volume_maximum, + M_aud_Handle_volume_maximum_doc, nullptr }, + {(char*)"volume_minimum", (getter)Handle_get_volume_minimum, (setter)Handle_set_volume_minimum, + M_aud_Handle_volume_minimum_doc, nullptr }, + {nullptr} /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_Handle_doc, + "Handle objects are playback handles that can be used to control " + "playback of a sound. If a sound is played back multiple times " + "then there are as many handles."); + +static PyTypeObject HandleType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.Handle", /* tp_name */ + sizeof(Handle), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Handle_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_Handle_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Handle_methods, /* tp_methods */ + 0, /* tp_members */ + Handle_properties, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + + +AUD_API PyObject* Handle_empty() +{ + return HandleType.tp_alloc(&HandleType, 0); +} + + +AUD_API Handle*checkHandle(PyObject* handle) +{ + if(!PyObject_TypeCheck(handle, &HandleType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type Handle!"); + return nullptr; + } + + return (Handle*)handle; +} + + +bool initializeHandle() +{ + return PyType_Ready(&HandleType) >= 0; +} + + +void addHandleToModule(PyObject* module) +{ + Py_INCREF(&HandleType); + PyModule_AddObject(module, "Handle", (PyObject *)&HandleType); +} + + diff --git a/extern/audaspace/bindings/python/PyHandle.h b/extern/audaspace/bindings/python/PyHandle.h new file mode 100644 index 00000000000..95006c88da7 --- /dev/null +++ b/extern/audaspace/bindings/python/PyHandle.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_IHandle; + +typedef struct { + PyObject_HEAD + Reference_IHandle* handle; +} Handle; + +extern AUD_API PyObject* Handle_empty(); +extern AUD_API Handle* checkHandle(PyObject* handle); + +bool initializeHandle(); +void addHandleToModule(PyObject* module); diff --git a/extern/audaspace/bindings/python/PyImpulseResponse.cpp b/extern/audaspace/bindings/python/PyImpulseResponse.cpp new file mode 100644 index 00000000000..5200c938511 --- /dev/null +++ b/extern/audaspace/bindings/python/PyImpulseResponse.cpp @@ -0,0 +1,137 @@ +/******************************************************************************* +* Copyright 2009-2015 Juan Francisco Crespo Galán +* +* 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 "PyImpulseResponse.h" +#include "PySound.h" + +#include "Exception.h" +#include "fx/ImpulseResponse.h" +#include "util/StreamBuffer.h" + +extern PyObject* AUDError; + +static PyObject * +ImpulseResponse_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + ImpulseResponseP* self = (ImpulseResponseP*)type->tp_alloc(type, 0); + + if(self != nullptr) + { + PyObject* object; + if(!PyArg_ParseTuple(args, "O:sound", &object)) + return nullptr; + Sound* sound = checkSound(object); + + try + { + self->impulseResponse = new std::shared_ptr<aud::ImpulseResponse>(new aud::ImpulseResponse(std::make_shared<aud::StreamBuffer>(*reinterpret_cast<std::shared_ptr<aud::ISound>*>(sound->sound)))); + } + catch(aud::Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +static void +ImpulseResponse_dealloc(ImpulseResponseP* self) +{ + if(self->impulseResponse) + delete reinterpret_cast<std::shared_ptr<aud::ImpulseResponse>*>(self->impulseResponse); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyMethodDef ImpulseResponse_methods[] = { + { nullptr } /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_ImpulseResponse_doc, + "An ImpulseResponse object represents a filter with which to convolve a sound."); + +PyTypeObject ImpulseResponseType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.ImpulseResponse", /* tp_name */ + sizeof(ImpulseResponseP), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ImpulseResponse_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_ImpulseResponse_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ImpulseResponse_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + ImpulseResponse_new, /* tp_new */ +}; + +AUD_API PyObject* ImpulseResponse_empty() +{ + return ImpulseResponseType.tp_alloc(&ImpulseResponseType, 0); +} + + +AUD_API ImpulseResponseP* checkImpulseResponse(PyObject* impulseResponse) +{ + if(!PyObject_TypeCheck(impulseResponse, &ImpulseResponseType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type ImpulseResponse!"); + return nullptr; + } + + return (ImpulseResponseP*)impulseResponse; +} + + +bool initializeImpulseResponse() +{ + return PyType_Ready(&ImpulseResponseType) >= 0; +} + + +void addImpulseResponseToModule(PyObject* module) +{ + Py_INCREF(&ImpulseResponseType); + PyModule_AddObject(module, "ImpulseResponse", (PyObject *)&ImpulseResponseType); +} diff --git a/extern/audaspace/bindings/python/PyImpulseResponse.h b/extern/audaspace/bindings/python/PyImpulseResponse.h new file mode 100644 index 00000000000..3e974c0701c --- /dev/null +++ b/extern/audaspace/bindings/python/PyImpulseResponse.h @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright 2009-2015 Juan Francisco Crespo Galán +* +* 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. +******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_ImpulseResponse; + +typedef struct { + PyObject_HEAD + Reference_ImpulseResponse* impulseResponse; +} ImpulseResponseP; + +extern AUD_API PyObject* ImpulseResponse_empty(); +extern AUD_API ImpulseResponseP* checkImpulseResponse(PyObject* impulseResponse); + +bool initializeImpulseResponse(); +void addImpulseResponseToModule(PyObject* module);
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/PyPlaybackManager.cpp b/extern/audaspace/bindings/python/PyPlaybackManager.cpp new file mode 100644 index 00000000000..9b6614cae9a --- /dev/null +++ b/extern/audaspace/bindings/python/PyPlaybackManager.cpp @@ -0,0 +1,389 @@ +/******************************************************************************* +* Copyright 2015-2016 Juan Francisco Crespo Galán +* +* 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 "PyPlaybackManager.h" +#include "PySound.h" +#include "PyHandle.h" +#include "PyDevice.h" + +#include "Exception.h" +#include "fx/PlaybackManager.h" + +extern PyObject* AUDError; + +static PyObject * +PlaybackManager_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + PlaybackManagerP* self = (PlaybackManagerP*)type->tp_alloc(type, 0); + + if(self != nullptr) + { + PyObject* object; + if(!PyArg_ParseTuple(args, "O:catKey", &object)) + return nullptr; + Device* device = checkDevice(object); + + try + { + self->playbackManager = new std::shared_ptr<aud::PlaybackManager>(new aud::PlaybackManager(*reinterpret_cast<std::shared_ptr<aud::IDevice>*>(device->device))); + } + catch(aud::Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +static void +PlaybackManager_dealloc(PlaybackManagerP* self) +{ + if(self->playbackManager) + delete reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyDoc_STRVAR(M_aud_PlaybackManager_play_doc, + "setVolume(sound, catKey)\n\n" + "Plays a sound through the playback manager and assigns it to a category.\n\n" + ":arg sound: The sound to play.\n" + ":type sound: :class:`Sound`\n" + ":arg catKey: the key of the category in which the sound will be added, if it doesn't exist, a new one will be created.\n" + ":type catKey: int\n" + ":return: The playback handle with which playback can be controlled with.\n" + ":rtype: :class:`Handle`"); + +static PyObject * +PlaybackManager_play(PlaybackManagerP* self, PyObject* args) +{ + PyObject* object; + unsigned int cat; + + if(!PyArg_ParseTuple(args, "OI:catKey", &object, &cat)) + return nullptr; + + Sound* sound = checkSound(object); + if(!sound) + return nullptr; + + Handle* handle; + + handle = (Handle*)Handle_empty(); + if(handle != nullptr) + { + try + { + handle->handle = new std::shared_ptr<aud::IHandle>((*reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager))->play(*reinterpret_cast<std::shared_ptr<aud::ISound>*>(sound->sound), cat)); + } + catch(aud::Exception& e) + { + Py_DECREF(handle); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)handle; +} + +PyDoc_STRVAR(M_aud_PlaybackManager_resume_doc, + "resume(catKey)\n\n" + "Resumes playback of the catgory.\n\n" + ":arg catKey: the key of the category.\n" + ":type catKey: int\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool"); + +static PyObject * +PlaybackManager_resume(PlaybackManagerP* self, PyObject* args) +{ + unsigned int cat; + + if(!PyArg_ParseTuple(args, "I:catKey", &cat)) + return nullptr; + + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager))->resume(cat)); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_PlaybackManager_pause_doc, + "pause(catKey)\n\n" + "Pauses playback of the category.\n\n" + ":arg catKey: the key of the category.\n" + ":type catKey: int\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool"); + +static PyObject * +PlaybackManager_pause(PlaybackManagerP* self, PyObject* args) +{ + unsigned int cat; + + if(!PyArg_ParseTuple(args, "I:catKey", &cat)) + return nullptr; + + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager))->pause(cat)); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_PlaybackManager_add_category_doc, + "addCategory(volume)\n\n" + "Adds a category with a custom volume.\n\n" + ":arg volume: The volume for ther new category.\n" + ":type volume: float\n" + ":return: The key of the new category.\n" + ":rtype: int\n\n"); + +static PyObject * +PlaybackManager_add_category(PlaybackManagerP* self, PyObject* args) +{ + float vol; + + if(!PyArg_ParseTuple(args, "f:volume", &vol)) + return nullptr; + + try + { + return Py_BuildValue("I", (*reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager))->addCategory(vol)); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_PlaybackManager_get_volume_doc, + "getVolume(catKey)\n\n" + "Retrieves the volume of a category.\n\n" + ":arg catKey: the key of the category.\n" + ":type catKey: int\n" + ":return: The volume of the cateogry.\n" + ":rtype: float\n\n"); + +static PyObject * +PlaybackManager_get_volume(PlaybackManagerP* self, PyObject* args) +{ + unsigned int cat; + + if(!PyArg_ParseTuple(args, "I:catKey", &cat)) + return nullptr; + + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager))->getVolume(cat)); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_PlaybackManager_set_volume_doc, + "setVolume(volume, catKey)\n\n" + "Changes the volume of a category.\n\n" + ":arg volume: the new volume value.\n" + ":type volume: float\n" + ":arg catKey: the key of the category.\n" + ":type catKey: int\n" + ":return: Whether the action succeeded.\n" + ":rtype: int\n\n"); + +static PyObject * +PlaybackManager_set_volume(PlaybackManagerP* self, PyObject* args) +{ + float volume; + unsigned int cat; + + if(!PyArg_ParseTuple(args, "fI:volume", &volume, &cat)) + return nullptr; + + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager))->setVolume(volume, cat)); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_PlaybackManager_stop_doc, + "stop(catKey)\n\n" + "Stops playback of the category.\n\n" + ":arg catKey: the key of the category.\n" + ":type catKey: int\n" + ":return: Whether the action succeeded.\n" + ":rtype: bool\n\n"); + +static PyObject * +PlaybackManager_stop(PlaybackManagerP* self, PyObject* args) +{ + unsigned int cat; + + if(!PyArg_ParseTuple(args, "I:catKey", &cat)) + return nullptr; + + try + { + return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager))->stop(cat)); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_PlaybackManager_clean_doc, + "clean()\n\n" + "Cleans all the invalid and finished sound from the playback manager.\n\n"); + +static PyObject * +PlaybackManager_clean(PlaybackManagerP* self) +{ + try + { + (*reinterpret_cast<std::shared_ptr<aud::PlaybackManager>*>(self->playbackManager))->clean(); + Py_RETURN_NONE; + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static PyMethodDef PlaybackManager_methods[] = { + { "play", (PyCFunction)PlaybackManager_play, METH_VARARGS | METH_KEYWORDS, + M_aud_PlaybackManager_play_doc + }, + { "resume", (PyCFunction)PlaybackManager_resume, METH_VARARGS, + M_aud_PlaybackManager_resume_doc + }, + { "pause", (PyCFunction)PlaybackManager_pause, METH_VARARGS, + M_aud_PlaybackManager_pause_doc + }, + { "stop", (PyCFunction)PlaybackManager_stop, METH_VARARGS, + M_aud_PlaybackManager_stop_doc + }, + { "addCategory", (PyCFunction)PlaybackManager_add_category, METH_VARARGS, + M_aud_PlaybackManager_add_category_doc + }, + { "getVolume", (PyCFunction)PlaybackManager_get_volume, METH_VARARGS, + M_aud_PlaybackManager_get_volume_doc + }, + { "setVolume", (PyCFunction)PlaybackManager_set_volume, METH_VARARGS, + M_aud_PlaybackManager_set_volume_doc + }, + { "clean", (PyCFunction)PlaybackManager_clean, METH_NOARGS, + M_aud_PlaybackManager_clean_doc + }, + { nullptr } /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_PlaybackManager_doc, + "A PlabackManager object allows to easily control groups os sounds organized in categories."); + +PyTypeObject PlaybackManagerType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.PlaybackManager", /* tp_name */ + sizeof(PlaybackManagerP), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PlaybackManager_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_PlaybackManager_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PlaybackManager_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PlaybackManager_new, /* tp_new */ +}; + +AUD_API PyObject* PlaybackManager_empty() +{ + return PlaybackManagerType.tp_alloc(&PlaybackManagerType, 0); +} + + +AUD_API PlaybackManagerP* checkPlaybackManager(PyObject* playbackManager) +{ + if(!PyObject_TypeCheck(playbackManager, &PlaybackManagerType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type PlaybackManager!"); + return nullptr; + } + + return (PlaybackManagerP*)playbackManager; +} + + +bool initializePlaybackManager() +{ + return PyType_Ready(&PlaybackManagerType) >= 0; +} + + +void addPlaybackManagerToModule(PyObject* module) +{ + Py_INCREF(&PlaybackManagerType); + PyModule_AddObject(module, "PlaybackManager", (PyObject *)&PlaybackManagerType); +} diff --git a/extern/audaspace/bindings/python/PyPlaybackManager.h b/extern/audaspace/bindings/python/PyPlaybackManager.h new file mode 100644 index 00000000000..f26df1b32d0 --- /dev/null +++ b/extern/audaspace/bindings/python/PyPlaybackManager.h @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright 2015-2016 Juan Francisco Crespo Galán +* +* 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. +******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_PlaybackManager; + +typedef struct { + PyObject_HEAD + Reference_PlaybackManager* playbackManager; +} PlaybackManagerP; + +extern AUD_API PyObject* PlaybackManager_empty(); +extern AUD_API PlaybackManagerP* checkPlaybackManager(PyObject* playbackManager); + +bool initializePlaybackManager(); +void addPlaybackManagerToModule(PyObject* module);
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/PySequence.cpp b/extern/audaspace/bindings/python/PySequence.cpp new file mode 100644 index 00000000000..d4773c743ee --- /dev/null +++ b/extern/audaspace/bindings/python/PySequence.cpp @@ -0,0 +1,655 @@ +/******************************************************************************* + * 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, + "add()\n\n" + "Adds a new entry to the scene.\n" + ":arg sound: The sound this entry should play.\n" + ":type sound: :class:`Sound`\n" + ":arg begin: The start time.\n" + ":type begin: float\n" + ":arg end: The end time or a negative value if determined by the sound.\n" + ":type end: float\n" + ":arg skip: How much seconds should be skipped at the beginning.\n" + ":type skip: float\n" + ":return: The entry added.\n" + ":rtype: :class:`SequenceEntry`"); + +static PyObject * +Sequence_add(Sequence* self, PyObject* args, PyObject* kwds) +{ + PyObject* object; + float begin; + float end = -1.0f; + float skip = 0.0f; + + static const char* kwlist[] = {"sound", "begin", "end", "skip", nullptr}; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "Of|ff: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, + "reomve()\n\n" + "Adds a new entry to the scene.\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, + "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:: http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specification.htm#_Toc199835864"); + +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 scene."); + +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); +} diff --git a/extern/audaspace/bindings/python/PySequence.h b/extern/audaspace/bindings/python/PySequence.h new file mode 100644 index 00000000000..17855121dda --- /dev/null +++ b/extern/audaspace/bindings/python/PySequence.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_Sequence; + +typedef struct { + PyObject_HEAD + Reference_Sequence* sequence; +} Sequence; + +extern AUD_API PyObject* Sequence_empty(); +extern AUD_API Sequence* checkSequence(PyObject* sequence); + +bool initializeSequence(); +void addSequenceToModule(PyObject* module); diff --git a/extern/audaspace/bindings/python/PySequenceEntry.cpp b/extern/audaspace/bindings/python/PySequenceEntry.cpp new file mode 100644 index 00000000000..e6a034ed880 --- /dev/null +++ b/extern/audaspace/bindings/python/PySequenceEntry.cpp @@ -0,0 +1,740 @@ +/******************************************************************************* + * 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 "PySequenceEntry.h" + +#include "PySound.h" + +#include "Exception.h" +#include "sequence/AnimateableProperty.h" +#include "sequence/SequenceEntry.h" + +#include <structmember.h> +#include <vector> + +using aud::Exception; +using aud::AnimateableProperty; +using aud::AnimateablePropertyType; +using aud::ISound; + +extern PyObject* AUDError; + +// ==================================================================== + +static void +SequenceEntry_dealloc(SequenceEntry* self) +{ + if(self->entry) + delete reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyDoc_STRVAR(M_aud_SequenceEntry_move_doc, + "move()\n\n" + "Moves the entry.\n\n" + ":arg begin: The new start time.\n" + ":type begin: float\n" + ":arg end: The new end time or a negative value if unknown.\n" + ":type end: float\n" + ":arg skip: How many seconds to skip at the beginning.\n" + ":type skip: float\n"); + +static PyObject * +SequenceEntry_move(SequenceEntry* self, PyObject* args) +{ + float begin, end, skip; + + if(!PyArg_ParseTuple(args, "fff:move", &begin, &end, &skip)) + return nullptr; + + try + { + (*reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry))->move(begin, end, skip); + Py_RETURN_NONE; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_SequenceEntry_setAnimationData_doc, + "setAnimationData()\n\n" + "Writes animation data to a sequenced entry.\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 * +SequenceEntry_setAnimationData(SequenceEntry* 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::SequenceEntry>*>(self->entry))->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 SequenceEntry_methods[] = { + {"move", (PyCFunction)SequenceEntry_move, METH_VARARGS, + M_aud_SequenceEntry_move_doc + }, + {"setAnimationData", (PyCFunction)SequenceEntry_setAnimationData, METH_VARARGS, + M_aud_SequenceEntry_setAnimationData_doc + }, + {nullptr} /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_SequenceEntry_attenuation_doc, + "This factor is used for distance based attenuation of the " + "source.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +SequenceEntry_get_attenuation(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return Py_BuildValue("f", (*entry)->getAttenuation()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_attenuation(SequenceEntry* self, PyObject* args, void* nothing) +{ + float factor; + + if(!PyArg_Parse(args, "f:attenuation", &factor)) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setAttenuation(factor); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_cone_angle_inner_doc, + "The opening angle of the inner cone of the source. If the cone " + "values of a source are set there are two (audible) cones with " + "the apex at the :attr:`location` of the source and with infinite " + "height, heading in the direction of the source's " + ":attr:`orientation`.\n" + "In the inner cone the volume is normal. Outside the outer cone " + "the volume will be :attr:`cone_volume_outer` and in the area " + "between the volume will be interpolated linearly."); + +static PyObject * +SequenceEntry_get_cone_angle_inner(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return Py_BuildValue("f", (*entry)->getConeAngleInner()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_cone_angle_inner(SequenceEntry* self, PyObject* args, void* nothing) +{ + float angle; + + if(!PyArg_Parse(args, "f:cone_angle_inner", &angle)) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setConeAngleInner(angle); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_cone_angle_outer_doc, + "The opening angle of the outer cone of the source.\n\n" + ".. seealso:: :attr:`cone_angle_inner`"); + +static PyObject * +SequenceEntry_get_cone_angle_outer(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return Py_BuildValue("f", (*entry)->getConeAngleOuter()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_cone_angle_outer(SequenceEntry* self, PyObject* args, void* nothing) +{ + float angle; + + if(!PyArg_Parse(args, "f:cone_angle_outer", &angle)) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setConeAngleOuter(angle); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_cone_volume_outer_doc, + "The volume outside the outer cone of the source.\n\n" + ".. seealso:: :attr:`cone_angle_inner`"); + +static PyObject * +SequenceEntry_get_cone_volume_outer(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return Py_BuildValue("f", (*entry)->getConeVolumeOuter()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_cone_volume_outer(SequenceEntry* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:cone_volume_outer", &volume)) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setConeVolumeOuter(volume); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_distance_maximum_doc, + "The maximum distance of the source.\n" + "If the listener is further away the source volume will be 0.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +SequenceEntry_get_distance_maximum(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return Py_BuildValue("f", (*entry)->getDistanceMaximum()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_distance_maximum(SequenceEntry* self, PyObject* args, void* nothing) +{ + float distance; + + if(!PyArg_Parse(args, "f:distance_maximum", &distance)) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setDistanceMaximum(distance); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_distance_reference_doc, + "The reference distance of the source.\n" + "At this distance the volume will be exactly :attr:`volume`.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +SequenceEntry_get_distance_reference(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return Py_BuildValue("f", (*entry)->getDistanceReference()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_distance_reference(SequenceEntry* self, PyObject* args, void* nothing) +{ + float distance; + + if(!PyArg_Parse(args, "f:distance_reference", &distance)) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setDistanceReference(distance); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_muted_doc, + "Whether the entry is muted.\n"); + +static PyObject * +SequenceEntry_get_muted(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return PyBool_FromLong((long)(*entry)->isMuted()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_muted(SequenceEntry* 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::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->mute(muted); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_relative_doc, + "Whether the source's location, velocity and orientation is relative or absolute to the listener."); + +static PyObject * +SequenceEntry_get_relative(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return PyBool_FromLong((long)(*entry)->isRelative()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +SequenceEntry_set_relative(SequenceEntry* self, PyObject* args, void* nothing) +{ + if(!PyBool_Check(args)) + { + PyErr_SetString(PyExc_TypeError, "Value is not a boolean!"); + return -1; + } + + bool relative = (args == Py_True); + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setRelative(relative); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_sound_doc, + "The sound the entry is representing and will be played in the sequence."); + +static PyObject * +SequenceEntry_get_sound(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + Sound* object = (Sound*) Sound_empty(); + if(object) + { + object->sound = new std::shared_ptr<ISound>((*entry)->getSound()); + return (PyObject *) object; + } + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return nullptr; +} + +static int +SequenceEntry_set_sound(SequenceEntry* self, PyObject* args, void* nothing) +{ + Sound* sound = checkSound(args); + + if(!sound) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setSound(*reinterpret_cast<std::shared_ptr<ISound>*>(sound->sound)); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_volume_maximum_doc, + "The maximum volume of the source.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +SequenceEntry_get_volume_maximum(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return Py_BuildValue("f", (*entry)->getVolumeMaximum()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_volume_maximum(SequenceEntry* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:volume_maximum", &volume)) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setVolumeMaximum(volume); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +PyDoc_STRVAR(M_aud_SequenceEntry_volume_minimum_doc, + "The minimum volume of the source.\n\n" + ".. seealso:: :attr:`Device.distance_model`"); + +static PyObject * +SequenceEntry_get_volume_minimum(SequenceEntry* self, void* nothing) +{ + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + return Py_BuildValue("f", (*entry)->getVolumeMinimum()); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static int +SequenceEntry_set_volume_minimum(SequenceEntry* self, PyObject* args, void* nothing) +{ + float volume; + + if(!PyArg_Parse(args, "f:volume_minimum", &volume)) + return -1; + + try + { + std::shared_ptr<aud::SequenceEntry>* entry = reinterpret_cast<std::shared_ptr<aud::SequenceEntry>*>(self->entry); + (*entry)->setVolumeMinimum(volume); + return 0; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyGetSetDef SequenceEntry_properties[] = { + {(char*)"attenuation", (getter)SequenceEntry_get_attenuation, (setter)SequenceEntry_set_attenuation, + M_aud_SequenceEntry_attenuation_doc, nullptr }, + {(char*)"cone_angle_inner", (getter)SequenceEntry_get_cone_angle_inner, (setter)SequenceEntry_set_cone_angle_inner, + M_aud_SequenceEntry_cone_angle_inner_doc, nullptr }, + {(char*)"cone_angle_outer", (getter)SequenceEntry_get_cone_angle_outer, (setter)SequenceEntry_set_cone_angle_outer, + M_aud_SequenceEntry_cone_angle_outer_doc, nullptr }, + {(char*)"cone_volume_outer", (getter)SequenceEntry_get_cone_volume_outer, (setter)SequenceEntry_set_cone_volume_outer, + M_aud_SequenceEntry_cone_volume_outer_doc, nullptr }, + {(char*)"distance_maximum", (getter)SequenceEntry_get_distance_maximum, (setter)SequenceEntry_set_distance_maximum, + M_aud_SequenceEntry_distance_maximum_doc, nullptr }, + {(char*)"distance_reference", (getter)SequenceEntry_get_distance_reference, (setter)SequenceEntry_set_distance_reference, + M_aud_SequenceEntry_distance_reference_doc, nullptr }, + {(char*)"muted", (getter)SequenceEntry_get_muted, (setter)SequenceEntry_set_muted, + M_aud_SequenceEntry_muted_doc, nullptr }, + {(char*)"relative", (getter)SequenceEntry_get_relative, (setter)SequenceEntry_set_relative, + M_aud_SequenceEntry_relative_doc, nullptr }, + {(char*)"sound", (getter)SequenceEntry_get_sound, (setter)SequenceEntry_set_sound, + M_aud_SequenceEntry_sound_doc, nullptr }, + {(char*)"volume_maximum", (getter)SequenceEntry_get_volume_maximum, (setter)SequenceEntry_set_volume_maximum, + M_aud_SequenceEntry_volume_maximum_doc, nullptr }, + {(char*)"volume_minimum", (getter)SequenceEntry_get_volume_minimum, (setter)SequenceEntry_set_volume_minimum, + M_aud_SequenceEntry_volume_minimum_doc, nullptr }, + {nullptr} /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_SequenceEntry_doc, + "SequenceEntry objects represent an entry of a sequenced sound."); + +static PyTypeObject SequenceEntryType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.SequenceEntry", /* tp_name */ + sizeof(SequenceEntry), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)SequenceEntry_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_SequenceEntry_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + SequenceEntry_methods, /* tp_methods */ + 0, /* tp_members */ + SequenceEntry_properties, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +AUD_API PyObject* SequenceEntry_empty() +{ + return SequenceEntryType.tp_alloc(&SequenceEntryType, 0); +} + + +AUD_API SequenceEntry* checkSequenceEntry(PyObject* entry) +{ + if(!PyObject_TypeCheck(entry, &SequenceEntryType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type SequenceEntry!"); + return nullptr; + } + + return (SequenceEntry*)entry; +} + + +bool initializeSequenceEntry() +{ + return PyType_Ready(&SequenceEntryType) >= 0; +} + + +void addSequenceEntryToModule(PyObject* module) +{ + Py_INCREF(&SequenceEntryType); + PyModule_AddObject(module, "SequenceEntry", (PyObject *)&SequenceEntryType); +} diff --git a/extern/audaspace/bindings/python/PySequenceEntry.h b/extern/audaspace/bindings/python/PySequenceEntry.h new file mode 100644 index 00000000000..7bb4ae4a281 --- /dev/null +++ b/extern/audaspace/bindings/python/PySequenceEntry.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_SequenceEntry; + +typedef struct { + PyObject_HEAD + Reference_SequenceEntry* entry; +} SequenceEntry; + +extern AUD_API PyObject* SequenceEntry_empty(); +extern AUD_API SequenceEntry* checkSequenceEntry(PyObject* entry); + +bool initializeSequenceEntry(); +void addSequenceEntryToModule(PyObject* module); diff --git a/extern/audaspace/bindings/python/PySound.cpp b/extern/audaspace/bindings/python/PySound.cpp new file mode 100644 index 00000000000..2ab1974be49 --- /dev/null +++ b/extern/audaspace/bindings/python/PySound.cpp @@ -0,0 +1,1966 @@ +/******************************************************************************* + * 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 "PySound.h" +#include "PySource.h" +#include "PyThreadPool.h" + +#ifdef WITH_CONVOLUTION +#include "PyHRTF.h" +#include "PyImpulseResponse.h" +#endif + +#include "Exception.h" +#include "file/File.h" +#include "file/FileWriter.h" +#include "util/StreamBuffer.h" +#include "generator/Sawtooth.h" +#include "generator/Silence.h" +#include "generator/Sine.h" +#include "generator/Square.h" +#include "generator/Triangle.h" +#include "fx/Accumulator.h" +#include "fx/ADSR.h" +#include "fx/Delay.h" +#include "fx/Envelope.h" +#include "fx/Fader.h" +#include "fx/Highpass.h" +#include "fx/IIRFilter.h" +#include "fx/Limiter.h" +#include "fx/Loop.h" +#include "fx/Lowpass.h" +#include "fx/MutableSound.h" +#include "fx/Pitch.h" +#include "fx/Reverse.h" +#include "fx/SoundList.h" +#include "fx/Sum.h" +#include "fx/Threshold.h" +#include "fx/Volume.h" +#include "respec/ChannelMapper.h" +#include "respec/ChannelMapperReader.h" +#include "respec/LinearResample.h" +#include "respec/JOSResample.h" +#include "respec/JOSResampleReader.h" +#include "sequence/Double.h" +#include "sequence/PingPong.h" +#include "sequence/Superpose.h" + +#ifdef WITH_CONVOLUTION +#include "fx/BinauralSound.h" +#include "fx/ConvolverSound.h" +#endif + +#include <cstring> +#include <structmember.h> +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include <numpy/ndarrayobject.h> + +using namespace aud; + +extern PyObject* AUDError; + +static void +Sound_dealloc(Sound* self) +{ + if(self->sound) + delete reinterpret_cast<std::shared_ptr<ISound>*>(self->sound); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject * +Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + static const char* kwlist[] = {"filename", nullptr}; + const char* filename = nullptr; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "s:Sound", const_cast<char**>(kwlist), &filename)) + { + Py_DECREF(self); + return nullptr; + } + + try + { + self->sound = new std::shared_ptr<ISound>(new File(filename)); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_data_doc, + "data()\n\n" + "Retrieves the data of the sound as numpy array.\n\n" + ":return: A two dimensional numpy float array.\n" + ":rtype: :class:`numpy.ndarray`\n\n" + ".. note:: Best efficiency with cached sounds."); + +static PyObject * +Sound_data(Sound* self) +{ + std::shared_ptr<ISound> sound = *reinterpret_cast<std::shared_ptr<ISound>*>(self->sound); + + auto stream_buffer = std::dynamic_pointer_cast<StreamBuffer>(sound); + if(!stream_buffer) + stream_buffer = std::make_shared<StreamBuffer>(sound); + Specs specs = stream_buffer->getSpecs(); + auto buffer = stream_buffer->getBuffer(); + + npy_intp dimensions[2]; + dimensions[0] = buffer->getSize() / AUD_SAMPLE_SIZE(specs); + dimensions[1] = specs.channels; + + PyArrayObject* array = reinterpret_cast<PyArrayObject*>(PyArray_SimpleNew(2, dimensions, NPY_FLOAT)); + + sample_t* data = reinterpret_cast<sample_t*>(PyArray_DATA(array)); + + std::memcpy(data, buffer->getBuffer(), buffer->getSize()); + + Py_INCREF(array); + + return reinterpret_cast<PyObject*>(array); +} + +PyDoc_STRVAR(M_aud_Sound_write_doc, + "write(filename, rate, channels, format, container, codec, bitrate, buffersize)\n\n" + "Writes the sound to a file.\n\n" + ":arg filename: The path to write to.\n" + ":type filename: string\n" + ":arg rate: The sample rate to write with.\n" + ":type rate: int\n" + ":arg channels: The number of channels to write with.\n" + ":type channels: int\n" + ":arg format: The sample format to write with.\n" + ":type format: int\n" + ":arg container: The container format for the file.\n" + ":type container: int\n" + ":arg codec: The codec to use in the file.\n" + ":type codec: int\n" + ":arg bitrate: The bitrate to write with.\n" + ":type bitrate: int\n" + ":arg buffersize: The size of the writing buffer.\n" + ":type buffersize: int\n"); + +static PyObject * +Sound_write(Sound* self, PyObject* args, PyObject* kwds) +{ + const char* filename = nullptr; + int rate = RATE_INVALID; + Channels channels = CHANNELS_INVALID; + SampleFormat format = FORMAT_INVALID; + Container container = CONTAINER_INVALID; + Codec codec = CODEC_INVALID; + int bitrate = 0; + int buffersize = 0; + + static const char* kwlist[] = {"filename", "rate", "channels", "format", "container", "codec", "bitrate", "buffersize", nullptr}; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|iiiiiii:write", const_cast<char**>(kwlist), &filename, &rate, &channels, &format, &container, &codec, &bitrate, &buffersize)) + return nullptr; + + try + { + std::shared_ptr<IReader> reader = (*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound))->createReader(); + + DeviceSpecs specs; + specs.specs = reader->getSpecs(); + + if((rate != RATE_INVALID) && (specs.rate != rate)) + { + specs.rate = rate; + reader = std::make_shared<JOSResampleReader>(reader, rate); + } + + if((channels != CHANNELS_INVALID) && (specs.channels != channels)) + { + specs.channels = channels; + reader = std::make_shared<ChannelMapperReader>(reader, channels); + } + + if(format == FORMAT_INVALID) + format = FORMAT_S16; + specs.format = format; + + const char* invalid_container_error = "Container could not be determined from filename."; + + if(container == CONTAINER_INVALID) + { + std::string path = filename; + + if(path.length() < 4) + { + PyErr_SetString(AUDError, invalid_container_error); + return nullptr; + } + + std::string extension = path.substr(path.length() - 4); + + if(extension == ".ac3") + container = CONTAINER_AC3; + else if(extension == "flac") + container = CONTAINER_FLAC; + else if(extension == ".mkv") + container = CONTAINER_MATROSKA; + else if(extension == ".mp2") + container = CONTAINER_MP2; + else if(extension == ".mp3") + container = CONTAINER_MP3; + else if(extension == ".ogg") + container = CONTAINER_OGG; + else if(extension == ".wav") + container = CONTAINER_WAV; + else + { + PyErr_SetString(AUDError, invalid_container_error); + return nullptr; + } + } + + if(codec == CODEC_INVALID) + { + switch(container) + { + case CONTAINER_AC3: + codec = CODEC_AC3; + break; + case CONTAINER_FLAC: + codec = CODEC_FLAC; + break; + case CONTAINER_MATROSKA: + codec = CODEC_OPUS; + break; + case CONTAINER_MP2: + codec = CODEC_MP2; + break; + case CONTAINER_MP3: + codec = CODEC_MP3; + break; + case CONTAINER_OGG: + codec = CODEC_VORBIS; + break; + case CONTAINER_WAV: + codec = CODEC_PCM; + break; + default: + PyErr_SetString(AUDError, "Unknown container, cannot select default codec."); + return nullptr; + } + } + + if(buffersize <= 0) + buffersize = AUD_DEFAULT_BUFFER_SIZE; + + std::shared_ptr<IWriter> writer = FileWriter::createWriter(filename, specs, container, codec, bitrate); + FileWriter::writeReader(reader, writer, 0, buffersize); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(M_aud_Sound_buffer_doc, + "buffer(data, rate)\n\n" + "Creates a sound from a data buffer.\n\n" + ":arg data: The data as two dimensional numpy array.\n" + ":type data: numpy.ndarray\n" + ":arg rate: The sample rate.\n" + ":type rate: double\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_buffer(PyTypeObject* type, PyObject* args) +{ + PyArrayObject* array = nullptr; + double rate = RATE_INVALID; + + if(!PyArg_ParseTuple(args, "Od:buffer", &array, &rate)) + return nullptr; + + if((!PyObject_TypeCheck(reinterpret_cast<PyObject*>(array), &PyArray_Type)) || (PyArray_TYPE(array) != NPY_FLOAT)) + { + PyErr_SetString(PyExc_TypeError, "The data needs to be supplied as float32 numpy array!"); + return nullptr; + } + + if(PyArray_NDIM(array) > 2) + { + PyErr_SetString(PyExc_TypeError, "The array needs to have one or two dimensions!"); + return nullptr; + } + + if(rate <= 0) + { + PyErr_SetString(PyExc_TypeError, "The sample rate has to be positive!"); + return nullptr; + } + + Specs specs; + specs.rate = rate; + specs.channels = CHANNELS_MONO; + + if(PyArray_NDIM(array) == 2) + specs.channels = static_cast<Channels>(PyArray_DIM(array, 1)); + + int size = PyArray_DIM(array, 0) * AUD_SAMPLE_SIZE(specs); + + std::shared_ptr<Buffer> buffer = std::make_shared<Buffer>(size); + + std::memcpy(buffer->getBuffer(), PyArray_DATA(array), size); + + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + try + { + self->sound = new std::shared_ptr<StreamBuffer>(new StreamBuffer(buffer, specs)); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_cache_doc, + "cache()\n\n" + "Caches a sound into RAM.\n" + "This saves CPU usage needed for decoding and file access if the " + "underlying sound reads from a file on the harddisk, but it " + "consumes a lot of memory.\n\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: Only known-length factories can be buffered.\n\n" + ".. warning:: Raw PCM data needs a lot of space, only buffer " + "short factories."); + +static PyObject * +Sound_cache(Sound* self) +{ + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new StreamBuffer(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_file_doc, + "file(filename)\n\n" + "Creates a sound object of a sound file.\n\n" + ":arg filename: Path of the file.\n" + ":type filename: string\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. warning:: If the file doesn't exist or can't be read you will " + "not get an exception immediately, but when you try to start " + "playback of that sound."); + +static PyObject * +Sound_file(PyTypeObject* type, PyObject* args) +{ + const char* filename = nullptr; + + if(!PyArg_ParseTuple(args, "s:file", &filename)) + return nullptr; + + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + try + { + self->sound = new std::shared_ptr<ISound>(new File(filename)); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_sawtooth_doc, + "sawtooth(frequency, rate=48000)\n\n" + "Creates a sawtooth sound which plays a sawtooth wave.\n\n" + ":arg frequency: The frequency of the sawtooth wave in Hz.\n" + ":type frequency: float\n" + ":arg rate: The sampling rate in Hz. It's recommended to set this " + "value to the playback device's samling rate to avoid resamping.\n" + ":type rate: int\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_sawtooth(PyTypeObject* type, PyObject* args) +{ + float frequency; + double rate = 48000; + + if(!PyArg_ParseTuple(args, "f|d:sawtooth", &frequency, &rate)) + return nullptr; + + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + try + { + self->sound = new std::shared_ptr<ISound>(new Sawtooth(frequency, (SampleRate)rate)); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_silence_doc, + "silence()\n\n" + "Creates a silence sound which plays simple silence.\n\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_silence(PyTypeObject* type) +{ + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + try + { + self->sound = new std::shared_ptr<ISound>(new Silence()); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_sine_doc, + "sine(frequency, rate=48000)\n\n" + "Creates a sine sound which plays a sine wave.\n\n" + ":arg frequency: The frequency of the sine wave in Hz.\n" + ":type frequency: float\n" + ":arg rate: The sampling rate in Hz. It's recommended to set this " + "value to the playback device's samling rate to avoid resamping.\n" + ":type rate: int\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_sine(PyTypeObject* type, PyObject* args) +{ + float frequency; + double rate = 48000; + + if(!PyArg_ParseTuple(args, "f|d:sine", &frequency, &rate)) + return nullptr; + + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + try + { + self->sound = new std::shared_ptr<ISound>(new Sine(frequency, (SampleRate)rate)); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_square_doc, + "square(frequency, rate=48000)\n\n" + "Creates a square sound which plays a square wave.\n\n" + ":arg frequency: The frequency of the square wave in Hz.\n" + ":type frequency: float\n" + ":arg rate: The sampling rate in Hz. It's recommended to set this " + "value to the playback device's samling rate to avoid resamping.\n" + ":type rate: int\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_square(PyTypeObject* type, PyObject* args) +{ + float frequency; + double rate = 48000; + + if(!PyArg_ParseTuple(args, "f|d:square", &frequency, &rate)) + return nullptr; + + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + try + { + self->sound = new std::shared_ptr<ISound>(new Square(frequency, (SampleRate)rate)); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_triangle_doc, + "triangle(frequency, rate=48000)\n\n" + "Creates a triangle sound which plays a triangle wave.\n\n" + ":arg frequency: The frequency of the triangle wave in Hz.\n" + ":type frequency: float\n" + ":arg rate: The sampling rate in Hz. It's recommended to set this " + "value to the playback device's samling rate to avoid resamping.\n" + ":type rate: int\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_triangle(PyTypeObject* type, PyObject* args) +{ + float frequency; + double rate = 48000; + + if(!PyArg_ParseTuple(args, "f|d:triangle", &frequency, &rate)) + return nullptr; + + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + try + { + self->sound = new std::shared_ptr<ISound>(new Triangle(frequency, (SampleRate)rate)); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_accumulate_doc, + "accumulate(additive=False)\n\n" + "Accumulates a sound by summing over positive input differences thus generating a monotonic sigal. " + "If additivity is set to true negative input differences get added too, but positive ones with a factor of two. " + "Note that with additivity the signal is not monotonic anymore.\n\n" + ":arg additive: Whether the accumulation should be additive or not.\n" + ":type time: bool\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_accumulate(Sound* self, PyObject* args) +{ + bool additive = false; + PyObject* additiveo; + + if(!PyArg_ParseTuple(args, "|O:accumulate", &additiveo)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + if(additiveo != nullptr) + { + if(!PyBool_Check(additiveo)) + { + PyErr_SetString(PyExc_TypeError, "additive is not a boolean!"); + return nullptr; + } + + additive = additiveo == Py_True; + } + + try + { + parent->sound = new std::shared_ptr<ISound>(new Accumulator(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), additive)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_ADSR_doc, + "ADSR(attack,decay,sustain,release)\n\n" + "Attack-Decay-Sustain-Release envelopes the volume of a sound. " + "Note: there is currently no way to trigger the release with this API.\n\n" + ":arg attack: The attack time in seconds.\n" + ":type attack: float\n" + ":arg decay: The decay time in seconds.\n" + ":type decay: float\n" + ":arg sustain: The sustain level.\n" + ":type sustain: float\n" + ":arg release: The release level.\n" + ":type release: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_ADSR(Sound* self, PyObject* args) +{ + float attack, decay, sustain, release; + + if(!PyArg_ParseTuple(args, "ffff:ADSR", &attack, &decay, &sustain, &release)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new ADSR(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), attack, decay, sustain, release)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_delay_doc, + "delay(time)\n\n" + "Delays by playing adding silence in front of the other sound's " + "data.\n\n" + ":arg time: How many seconds of silence should be added before " + "the sound.\n" + ":type time: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_delay(Sound* self, PyObject* args) +{ + float delay; + + if(!PyArg_ParseTuple(args, "f:delay", &delay)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Delay(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), delay)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_envelope_doc, + "envelope(attack, release, threshold, arthreshold)\n\n" + "Delays by playing adding silence in front of the other sound's " + "data.\n\n" + ":arg attack: The attack factor.\n" + ":type attack: float\n" + ":arg release: The release factor.\n" + ":type release: float\n" + ":arg threshold: The general threshold value.\n" + ":type threshold: float\n" + ":arg arthreshold: The attack/release threshold value.\n" + ":type arthreshold: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_envelope(Sound* self, PyObject* args) +{ + float attack, release, threshold, arthreshold; + + if(!PyArg_ParseTuple(args, "ffff:envelope", &attack, &release, &threshold, &arthreshold)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Envelope(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), attack, release, threshold, arthreshold)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_fadein_doc, + "fadein(start, length)\n\n" + "Fades a sound in by raising the volume linearly in the given " + "time interval.\n\n" + ":arg start: Time in seconds when the fading should start.\n" + ":type start: float\n" + ":arg length: Time in seconds how long the fading should last.\n" + ":type length: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: Before the fade starts it plays silence."); + +static PyObject * +Sound_fadein(Sound* self, PyObject* args) +{ + float start, length; + + if(!PyArg_ParseTuple(args, "ff:fadein", &start, &length)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Fader(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), FADE_IN, start, length)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_fadeout_doc, + "fadeout(start, length)\n\n" + "Fades a sound in by lowering the volume linearly in the given " + "time interval.\n\n" + ":arg start: Time in seconds when the fading should start.\n" + ":type start: float\n" + ":arg length: Time in seconds how long the fading should last.\n" + ":type length: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: After the fade this sound plays silence, so that " + "the length of the sound is not altered."); + +static PyObject * +Sound_fadeout(Sound* self, PyObject* args) +{ + float start, length; + + if(!PyArg_ParseTuple(args, "ff:fadeout", &start, &length)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Fader(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), FADE_OUT, start, length)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_filter_doc, + "filter(b, a = (1))\n\n" + "Filters a sound with the supplied IIR filter coefficients.\n" + "Without the second parameter you'll get a FIR filter.\n" + "If the first value of the a sequence is 0 it will be set to 1 " + "automatically.\n" + "If the first value of the a sequence is neither 0 nor 1, all " + "filter coefficients will be scaled by this value so that it is 1 " + "in the end, you don't have to scale yourself.\n\n" + ":arg b: The nominator filter coefficients.\n" + ":type b: sequence of float\n" + ":arg a: The denominator filter coefficients.\n" + ":type a: sequence of float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_filter(Sound* self, PyObject* args) +{ + PyObject* py_b; + PyObject* py_a = nullptr; + Py_ssize_t py_a_len; + Py_ssize_t py_b_len; + + if(!PyArg_ParseTuple(args, "O|O:filter", &py_b, &py_a)) + return nullptr; + + if(!PySequence_Check(py_b) || (py_a != nullptr && !PySequence_Check(py_a))) + { + PyErr_SetString(PyExc_TypeError, "Parameter is not a sequence!"); + return nullptr; + } + + py_a_len= py_a ? PySequence_Size(py_a) : 0; + py_b_len= PySequence_Size(py_b); + + if(!py_b_len || ((py_a != nullptr) && !py_a_len)) + { + PyErr_SetString(PyExc_ValueError, "The sequence has to contain at least one value!"); + return nullptr; + } + + std::vector<float> a, b; + PyObject* py_value; + float value; + + for(Py_ssize_t i = 0; i < py_b_len; i++) + { + py_value = PySequence_GetItem(py_b, i); + value= (float)PyFloat_AsDouble(py_value); + Py_DECREF(py_value); + + if(value == -1.0f && PyErr_Occurred()) { + return nullptr; + } + + b.push_back(value); + } + + if(py_a) + { + for(Py_ssize_t i = 0; i < py_a_len; i++) + { + py_value = PySequence_GetItem(py_a, i); + value= (float)PyFloat_AsDouble(py_value); + Py_DECREF(py_value); + + if(value == -1.0f && PyErr_Occurred()) { + return nullptr; + } + + a.push_back(value); + } + + if(a[0] == 0) + a[0] = 1; + } + else + a.push_back(1); + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new IIRFilter(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), b, a)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_highpass_doc, + "highpass(frequency, Q=0.5)\n\n" + "Creates a second order highpass filter based on the transfer " + "function H(s) = s^2 / (s^2 + s/Q + 1)\n\n" + ":arg frequency: The cut off trequency of the highpass.\n" + ":type frequency: float\n" + ":arg Q: Q factor of the lowpass.\n" + ":type Q: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_highpass(Sound* self, PyObject* args) +{ + float frequency; + float Q = 0.5; + + if(!PyArg_ParseTuple(args, "f|f:highpass", &frequency, &Q)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Highpass(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), frequency, Q)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_limit_doc, + "limit(start, end)\n\n" + "Limits a sound within a specific start and end time.\n\n" + ":arg start: Start time in seconds.\n" + ":type start: float\n" + ":arg end: End time in seconds.\n" + ":type end: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_limit(Sound* self, PyObject* args) +{ + float start, end; + + if(!PyArg_ParseTuple(args, "ff:limit", &start, &end)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Limiter(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), start, end)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_loop_doc, + "loop(count)\n\n" + "Loops a sound.\n\n" + ":arg count: How often the sound should be looped. " + "Negative values mean endlessly.\n" + ":type count: integer\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: This is a filter function, you might consider using " + ":attr:`Handle.loop_count` instead."); + +static PyObject * +Sound_loop(Sound* self, PyObject* args) +{ + int loop; + + if(!PyArg_ParseTuple(args, "i:loop", &loop)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Loop(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), loop)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_lowpass_doc, + "lowpass(frequency, Q=0.5)\n\n" + "Creates a second order lowpass filter based on the transfer " + "function H(s) = 1 / (s^2 + s/Q + 1)\n\n" + ":arg frequency: The cut off trequency of the lowpass.\n" + ":type frequency: float\n" + ":arg Q: Q factor of the lowpass.\n" + ":type Q: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_lowpass(Sound* self, PyObject* args) +{ + float frequency; + float Q = 0.5; + + if(!PyArg_ParseTuple(args, "f|f:lowpass", &frequency, &Q)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Lowpass(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), frequency, Q)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_pitch_doc, + "pitch(factor)\n\n" + "Changes the pitch of a sound with a specific factor.\n\n" + ":arg factor: The factor to change the pitch with.\n" + ":type factor: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: This is done by changing the sample rate of the " + "underlying sound, which has to be an integer, so the factor " + "value rounded and the factor may not be 100 % accurate.\n\n" + ".. note:: This is a filter function, you might consider using " + ":attr:`Handle.pitch` instead."); + +static PyObject * +Sound_pitch(Sound* self, PyObject* args) +{ + float factor; + + if(!PyArg_ParseTuple(args, "f:pitch", &factor)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Pitch(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), factor)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_rechannel_doc, + "rechannel(channels)\n\n" + "Rechannels the sound.\n\n" + ":arg channels: The new channel configuration.\n" + ":type channels: int\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_rechannel(Sound* self, PyObject* args) +{ + int channels; + + if(!PyArg_ParseTuple(args, "i:rechannel", &channels)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + DeviceSpecs specs; + specs.channels = static_cast<Channels>(channels); + specs.rate = RATE_INVALID; + specs.format = FORMAT_INVALID; + parent->sound = new std::shared_ptr<ISound>(new ChannelMapper(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), specs)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_resample_doc, + "resample(rate, high_quality)\n\n" + "Resamples the sound.\n\n" + ":arg rate: The new sample rate.\n" + ":type rate: double\n" + ":arg high_quality: When true use a higher quality but slower resampler.\n" + ":type high_quality: bool\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_resample(Sound* self, PyObject* args) +{ + double rate; + PyObject* high_qualityo; + bool high_quality = false; + + if(!PyArg_ParseTuple(args, "d|O:resample", &rate, &high_qualityo)) + return nullptr; + + if(!PyBool_Check(high_qualityo)) + { + PyErr_SetString(PyExc_TypeError, "high_quality is not a boolean!"); + return nullptr; + } + + high_quality = high_qualityo == Py_True; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + DeviceSpecs specs; + specs.channels = CHANNELS_INVALID; + specs.rate = rate; + specs.format = FORMAT_INVALID; + if(high_quality) + parent->sound = new std::shared_ptr<ISound>(new JOSResample(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), specs)); + else + parent->sound = new std::shared_ptr<ISound>(new LinearResample(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), specs)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_reverse_doc, + "reverse()\n\n" + "Plays a sound reversed.\n\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: The sound has to have a finite length and has to be " + "seekable. It's recommended to use this only with factories with " + "fast and accurate seeking, which is not true for encoded audio " + "files, such ones should be buffered using :meth:`cache` before " + "being played reversed.\n\n" + ".. warning:: If seeking is not accurate in the underlying sound " + "you'll likely hear skips/jumps/cracks."); + +static PyObject * +Sound_reverse(Sound* self) +{ + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Reverse(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_sum_doc, + "sum()\n\n" + "Sums the samples of a sound.\n\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_sum(Sound* self) +{ + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Sum(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_threshold_doc, + "threshold(threshold = 0)\n\n" + "Makes a threshold wave out of an audio wave by setting all samples " + "with a amplitude >= threshold to 1, all <= -threshold to -1 and " + "all between to 0.\n\n" + ":arg threshold: Threshold value over which an amplitude counts " + "non-zero.\n" + ":type threshold: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_threshold(Sound* self, PyObject* args) +{ + float threshold = 0; + + if(!PyArg_ParseTuple(args, "|f:threshold", &threshold)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Threshold(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), threshold)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_volume_doc, + "volume(volume)\n\n" + "Changes the volume of a sound.\n\n" + ":arg volume: The new volume..\n" + ":type volume: float\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: Should be in the range [0, 1] to avoid clipping.\n\n" + ".. note:: This is a filter function, you might consider using " + ":attr:`Handle.volume` instead."); + +static PyObject * +Sound_volume(Sound* self, PyObject* args) +{ + float volume; + + if(!PyArg_ParseTuple(args, "f:volume", &volume)) + return nullptr; + + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Volume(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), volume)); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_join_doc, + "join(sound)\n\n" + "Plays two factories in sequence.\n\n" + ":arg sound: The sound to play second.\n" + ":type sound: :class:`Sound`\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: The two factories have to have the same specifications " + "(channels and samplerate)."); + +static PyObject * +Sound_join(Sound* self, PyObject* object) +{ + PyTypeObject* type = Py_TYPE(self); + + if(!PyObject_TypeCheck(object, type)) + { + PyErr_SetString(PyExc_TypeError, "Object has to be of type Sound!"); + return nullptr; + } + + Sound* parent; + Sound* child = (Sound*)object; + + parent = (Sound*)type->tp_alloc(type, 0); + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Double(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), *reinterpret_cast<std::shared_ptr<ISound>*>(child->sound))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_mix_doc, + "mix(sound)\n\n" + "Mixes two factories.\n\n" + ":arg sound: The sound to mix over the other.\n" + ":type sound: :class:`Sound`\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`\n\n" + ".. note:: The two factories have to have the same specifications " + "(channels and samplerate)."); + +static PyObject * +Sound_mix(Sound* self, PyObject* object) +{ + PyTypeObject* type = Py_TYPE(self); + + if(!PyObject_TypeCheck(object, type)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type Sound!"); + return nullptr; + } + + Sound* parent = (Sound*)type->tp_alloc(type, 0); + Sound* child = (Sound*)object; + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new Superpose(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), *reinterpret_cast<std::shared_ptr<ISound>*>(child->sound))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_pingpong_doc, + "pingpong()\n\n" + "Plays a sound forward and then backward.\n" + "This is like joining a sound with its reverse.\n\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_pingpong(Sound* self) +{ + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new PingPong(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_list_doc, + "list()\n\n" + "Creates an empty sound list that can contain several sounds.\n\n" + ":arg random: wether the playback will be random or not.\n" + ":type random: int\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_list(PyTypeObject* type, PyObject* args) +{ + int random; + + if(!PyArg_ParseTuple(args, "i:random", &random)) + return nullptr; + + Sound* self; + + self = (Sound*)type->tp_alloc(type, 0); + if(self != nullptr) + { + try + { + self->sound = new std::shared_ptr<ISound>(new SoundList(random)); + } + catch(Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +PyDoc_STRVAR(M_aud_Sound_mutable_doc, + "mutable()\n\n" + "Creates a sound that will be restarted when sought backwards.\n" + "If the original sound is a sound list, the playing sound can change.\n\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_mutable(Sound* self) +{ + PyTypeObject* type = Py_TYPE(self); + Sound* parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new MutableSound(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_list_addSound_doc, + "addSound(sound)\n\n" + "Adds a new sound to a sound list.\n\n" + ":arg sound: The sound that will be added to the list.\n" + ":type sound: :class:`Sound`\n\n" + ".. note:: You can only add a sound to a sound list."); + +static PyObject * +Sound_list_addSound(Sound* self, PyObject* object) +{ + PyTypeObject* type = Py_TYPE(self); + + if(!PyObject_TypeCheck(object, type)) + { + PyErr_SetString(PyExc_TypeError, "Object has to be of type Sound!"); + return nullptr; + } + + Sound* child = (Sound*)object; + try + { + (*reinterpret_cast<std::shared_ptr<SoundList>*>(self->sound))->addSound(*reinterpret_cast<std::shared_ptr<ISound>*>(child->sound)); + Py_RETURN_NONE; + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +#ifdef WITH_CONVOLUTION + +PyDoc_STRVAR(M_aud_Sound_convolver_doc, + "convolver()\n\n" + "Creates a sound that will apply convolution to another sound.\n\n" + ":arg impulseResponse: The filter with which convolve the sound.\n" + ":type impulseResponse: :class:`ImpulseResponse`\n" + ":arg threadPool: A thread pool used to parallelize convolution.\n" + ":type threadPool: :class:`ThreadPool`\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_convolver(Sound* self, PyObject* args) +{ + PyTypeObject* type = Py_TYPE(self); + + PyObject* object1; + PyObject* object2; + + if(!PyArg_ParseTuple(args, "OO:convolver", &object1, &object2)) + return nullptr; + + ImpulseResponseP* filter = checkImpulseResponse(object1); + if(!filter) + return nullptr; + + ThreadPoolP* threadPool = checkThreadPool(object2); + if(!threadPool) + return nullptr; + + Sound* parent; + parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new ConvolverSound(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), *reinterpret_cast<std::shared_ptr<ImpulseResponse>*>(filter->impulseResponse), *reinterpret_cast<std::shared_ptr<ThreadPool>*>(threadPool->threadPool))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +PyDoc_STRVAR(M_aud_Sound_binaural_doc, + "convolver()\n\n" + "Creates a binaural sound using another sound as source. The original sound must be mono\n\n" + ":arg hrtfs: An HRTF set.\n" + ":type hrtf: :class:`HRTF`\n" + ":arg source: An object representing the source position of the sound.\n" + ":type source: :class:`Source`\n" + ":arg threadPool: A thread pool used to parallelize convolution.\n" + ":type threadPool: :class:`ThreadPool`\n" + ":return: The created :class:`Sound` object.\n" + ":rtype: :class:`Sound`"); + +static PyObject * +Sound_binaural(Sound* self, PyObject* args) +{ + PyTypeObject* type = Py_TYPE(self); + + PyObject* object1; + PyObject* object2; + PyObject* object3; + + if(!PyArg_ParseTuple(args, "OOO:binaural", &object1, &object2, &object3)) + return nullptr; + + HRTFP* hrtfs = checkHRTF(object1); + if(!hrtfs) + return nullptr; + + SourceP* source = checkSource(object2); + if(!hrtfs) + return nullptr; + + ThreadPoolP* threadPool = checkThreadPool(object3); + if(!threadPool) + return nullptr; + + Sound* parent; + parent = (Sound*)type->tp_alloc(type, 0); + + if(parent != nullptr) + { + try + { + parent->sound = new std::shared_ptr<ISound>(new BinauralSound(*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound), *reinterpret_cast<std::shared_ptr<HRTF>*>(hrtfs->hrtf), *reinterpret_cast<std::shared_ptr<Source>*>(source->source), *reinterpret_cast<std::shared_ptr<ThreadPool>*>(threadPool->threadPool))); + } + catch(Exception& e) + { + Py_DECREF(parent); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)parent; +} + +#endif + +static PyMethodDef Sound_methods[] = { + {"data", (PyCFunction)Sound_data, METH_NOARGS, + M_aud_Sound_data_doc + }, + {"write", (PyCFunction)Sound_write, METH_VARARGS | METH_KEYWORDS, + M_aud_Sound_write_doc + }, + {"buffer", (PyCFunction)Sound_buffer, METH_VARARGS | METH_CLASS, + M_aud_Sound_buffer_doc + }, + {"cache", (PyCFunction)Sound_cache, METH_NOARGS, + M_aud_Sound_cache_doc + }, + {"file", (PyCFunction)Sound_file, METH_VARARGS | METH_CLASS, + M_aud_Sound_file_doc + }, + {"sawtooth", (PyCFunction)Sound_sawtooth, METH_VARARGS | METH_CLASS, + M_aud_Sound_sawtooth_doc + }, + {"silence", (PyCFunction)Sound_silence, METH_NOARGS | METH_CLASS, + M_aud_Sound_silence_doc + }, + {"sine", (PyCFunction)Sound_sine, METH_VARARGS | METH_CLASS, + M_aud_Sound_sine_doc + }, + {"square", (PyCFunction)Sound_square, METH_VARARGS | METH_CLASS, + M_aud_Sound_square_doc + }, + {"triangle", (PyCFunction)Sound_triangle, METH_VARARGS | METH_CLASS, + M_aud_Sound_triangle_doc + }, + {"accumulate", (PyCFunction)Sound_accumulate, METH_VARARGS, + M_aud_Sound_accumulate_doc + }, + {"ADSR", (PyCFunction)Sound_ADSR, METH_VARARGS, + M_aud_Sound_ADSR_doc + }, + {"delay", (PyCFunction)Sound_delay, METH_VARARGS, + M_aud_Sound_delay_doc + }, + {"envelope", (PyCFunction)Sound_envelope, METH_VARARGS, + M_aud_Sound_envelope_doc + }, + {"fadein", (PyCFunction)Sound_fadein, METH_VARARGS, + M_aud_Sound_fadein_doc + }, + {"fadeout", (PyCFunction)Sound_fadeout, METH_VARARGS, + M_aud_Sound_fadeout_doc + }, + {"filter", (PyCFunction)Sound_filter, METH_VARARGS, + M_aud_Sound_filter_doc + }, + {"highpass", (PyCFunction)Sound_highpass, METH_VARARGS, + M_aud_Sound_highpass_doc + }, + {"limit", (PyCFunction)Sound_limit, METH_VARARGS, + M_aud_Sound_limit_doc + }, + {"loop", (PyCFunction)Sound_loop, METH_VARARGS, + M_aud_Sound_loop_doc + }, + {"lowpass", (PyCFunction)Sound_lowpass, METH_VARARGS, + M_aud_Sound_lowpass_doc + }, + {"pitch", (PyCFunction)Sound_pitch, METH_VARARGS, + M_aud_Sound_pitch_doc + }, + {"rechannel", (PyCFunction)Sound_rechannel, METH_VARARGS, + M_aud_Sound_rechannel_doc + }, + {"resample", (PyCFunction)Sound_resample, METH_VARARGS, + M_aud_Sound_resample_doc + }, + {"reverse", (PyCFunction)Sound_reverse, METH_NOARGS, + M_aud_Sound_reverse_doc + }, + {"sum", (PyCFunction)Sound_sum, METH_NOARGS, + M_aud_Sound_sum_doc + }, + {"threshold", (PyCFunction)Sound_threshold, METH_VARARGS, + M_aud_Sound_threshold_doc + }, + {"volume", (PyCFunction)Sound_volume, METH_VARARGS, + M_aud_Sound_volume_doc + }, + {"join", (PyCFunction)Sound_join, METH_O, + M_aud_Sound_join_doc + }, + {"mix", (PyCFunction)Sound_mix, METH_O, + M_aud_Sound_mix_doc + }, + { "pingpong", (PyCFunction)Sound_pingpong, METH_NOARGS, + M_aud_Sound_pingpong_doc + }, + { "list", (PyCFunction)Sound_list, METH_VARARGS | METH_CLASS, + M_aud_Sound_list_doc + }, + { "mutable", (PyCFunction)Sound_mutable, METH_NOARGS, + M_aud_Sound_mutable_doc + }, + { "addSound", (PyCFunction)Sound_list_addSound, METH_O, + M_aud_Sound_list_addSound_doc + }, +#ifdef WITH_CONVOLUTION + { "convolver", (PyCFunction)Sound_convolver, METH_VARARGS, + M_aud_Sound_convolver_doc + }, + { "binaural", (PyCFunction)Sound_binaural, METH_VARARGS, + M_aud_Sound_binaural_doc + }, +#endif + {nullptr} /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_Sound_specs_doc, + "The sample specification of the sound as a tuple with rate and channel count."); + +static PyObject * +Sound_get_specs(Sound* self, void* nothing) +{ + try + { + Specs specs = (*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound))->createReader()->getSpecs(); + return Py_BuildValue("(di)", specs.rate, specs.channels); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Sound_length_doc, + "The sample specification of the sound as a tuple with rate and channel count."); + +static PyObject * +Sound_get_length(Sound* self, void* nothing) +{ + try + { + int length = (*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound))->createReader()->getLength(); + return Py_BuildValue("i", length); + } + catch(Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static PyGetSetDef Sound_properties[] = { + {(char*)"specs", (getter)Sound_get_specs, nullptr, + M_aud_Sound_specs_doc, nullptr }, + {(char*)"length", (getter)Sound_get_length, nullptr, + M_aud_Sound_length_doc, nullptr }, + {nullptr} /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_Sound_doc, + "Sound objects are immutable and represent a sound that can be " + "played simultaneously multiple times. They are called factories " + "because they create reader objects internally that are used for " + "playback."); + +PyTypeObject SoundType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.Sound", /* tp_name */ + sizeof(Sound), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Sound_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_Sound_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Sound_methods, /* tp_methods */ + 0, /* tp_members */ + Sound_properties, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Sound_new, /* tp_new */ +}; + +AUD_API PyObject* Sound_empty() +{ + return SoundType.tp_alloc(&SoundType, 0); +} + +AUD_API Sound* checkSound(PyObject* sound) +{ + if(!PyObject_TypeCheck(sound, &SoundType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type Sound!"); + return nullptr; + } + + return (Sound*)sound; +} + + +bool initializeSound() +{ + import_array(); + + return PyType_Ready(&SoundType) >= 0; +} + + +void addSoundToModule(PyObject* module) +{ + Py_INCREF(&SoundType); + PyModule_AddObject(module, "Sound", (PyObject *)&SoundType); +} diff --git a/extern/audaspace/bindings/python/PySound.h b/extern/audaspace/bindings/python/PySound.h new file mode 100644 index 00000000000..657bb2131e6 --- /dev/null +++ b/extern/audaspace/bindings/python/PySound.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_ISound; + +typedef struct { + PyObject_HEAD + Reference_ISound* sound; +} Sound; + +extern AUD_API PyObject* Sound_empty(); +extern AUD_API Sound* checkSound(PyObject* sound); + +bool initializeSound(); +void addSoundToModule(PyObject* module); diff --git a/extern/audaspace/bindings/python/PySource.cpp b/extern/audaspace/bindings/python/PySource.cpp new file mode 100644 index 00000000000..a948cf46645 --- /dev/null +++ b/extern/audaspace/bindings/python/PySource.cpp @@ -0,0 +1,260 @@ +/******************************************************************************* +* Copyright 2009-2015 Juan Francisco Crespo Galán +* +* 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 "PySource.h" + +#include "Exception.h" +#include "fx/Source.h" + +#include <memory> + +extern PyObject* AUDError; + +static PyObject * +Source_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + SourceP* self = (SourceP*)type->tp_alloc(type, 0); + + if(self != nullptr) + { + float azimuth, elevation, distance; + if(!PyArg_ParseTuple(args, "fff:angles", &azimuth, &elevation, &distance)) + return nullptr; + + try + { + self->source = new std::shared_ptr<aud::Source>(new aud::Source(azimuth, elevation, distance)); + } + catch(aud::Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +static void +Source_dealloc(SourceP* self) +{ + if(self->source) + delete reinterpret_cast<std::shared_ptr<aud::Source>*>(self->source); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyMethodDef Source_methods[] = { + { nullptr } /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_Source_azimuth_doc, + "The azimuth angle."); + +static int +Source_set_azimuth(SourceP* self, PyObject* args, void* nothing) +{ + float azimuth; + + if(!PyArg_Parse(args, "f:azimuth", &azimuth)) + return -1; + + try + { + (*reinterpret_cast<std::shared_ptr<aud::Source>*>(self->source))->setAzimuth(azimuth); + return 0; + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyObject * +Source_get_azimuth(SourceP* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::Source>*>(self->source))->getAzimuth()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Source_elevation_doc, + "The elevation angle."); + +static int +Source_set_elevation(SourceP* self, PyObject* args, void* nothing) +{ + float elevation; + + if(!PyArg_Parse(args, "f:elevation", &elevation)) + return -1; + + try + { + (*reinterpret_cast<std::shared_ptr<aud::Source>*>(self->source))->setElevation(elevation); + return 0; + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyObject * +Source_get_elevation(SourceP* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::Source>*>(self->source))->getElevation()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +PyDoc_STRVAR(M_aud_Source_distance_doc, + "The distance value. 0 is min, 1 is max."); + +static int +Source_set_distance(SourceP* self, PyObject* args, void* nothing) +{ + float distance; + + if(!PyArg_Parse(args, "f:distance", &distance)) + return -1; + + try + { + (*reinterpret_cast<std::shared_ptr<aud::Source>*>(self->source))->setDistance(distance); + return 0; + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + } + + return -1; +} + +static PyObject * +Source_get_distance(SourceP* self, void* nothing) +{ + try + { + return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::Source>*>(self->source))->getDistance()); + } + catch(aud::Exception& e) + { + PyErr_SetString(AUDError, e.what()); + return nullptr; + } +} + +static PyGetSetDef Source_properties[] = { + { (char*)"azimuth", (getter)Source_get_azimuth, (setter)Source_set_azimuth, + M_aud_Source_azimuth_doc, nullptr }, + { (char*)"elevation", (getter)Source_get_elevation, (setter)Source_set_elevation, + M_aud_Source_elevation_doc, nullptr }, + { (char*)"distance", (getter)Source_get_distance, (setter)Source_set_distance, + M_aud_Source_distance_doc, nullptr }, + { nullptr } /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_Source_doc, + "The source object represents the source position of a binaural sound."); + +PyTypeObject SourceType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.Source", /* tp_name */ + sizeof(SourceP), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Source_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_Source_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Source_methods, /* tp_methods */ + 0, /* tp_members */ + Source_properties, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Source_new, /* tp_new */ +}; + +AUD_API PyObject* Source_empty() +{ + return SourceType.tp_alloc(&SourceType, 0); +} + + +AUD_API SourceP* checkSource(PyObject* source) +{ + if(!PyObject_TypeCheck(source, &SourceType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type Source!"); + return nullptr; + } + + return (SourceP*)source; +} + + +bool initializeSource() +{ + return PyType_Ready(&SourceType) >= 0; +} + + +void addSourceToModule(PyObject* module) +{ + Py_INCREF(&SourceType); + PyModule_AddObject(module, "Source", (PyObject *)&SourceType); +} diff --git a/extern/audaspace/bindings/python/PySource.h b/extern/audaspace/bindings/python/PySource.h new file mode 100644 index 00000000000..19960d80901 --- /dev/null +++ b/extern/audaspace/bindings/python/PySource.h @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright 2009-2015 Juan Francisco Crespo Galán +* +* 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. +******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_Source; + +typedef struct { + PyObject_HEAD + Reference_Source* source; +} SourceP; + +extern AUD_API PyObject* Source_empty(); +extern AUD_API SourceP* checkSource(PyObject* source); + +bool initializeSource(); +void addSourceToModule(PyObject* module);
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/PyThreadPool.cpp b/extern/audaspace/bindings/python/PyThreadPool.cpp new file mode 100644 index 00000000000..75811f08273 --- /dev/null +++ b/extern/audaspace/bindings/python/PyThreadPool.cpp @@ -0,0 +1,134 @@ +/******************************************************************************* +* Copyright 2009-2015 Juan Francisco Crespo Galán +* +* 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 "PyThreadPool.h" + +#include "Exception.h" +#include "util/ThreadPool.h" + +extern PyObject* AUDError; + +static PyObject * +ThreadPool_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + ThreadPoolP* self = (ThreadPoolP*)type->tp_alloc(type, 0); + + if(self != nullptr) + { + unsigned int nThreads; + if(!PyArg_ParseTuple(args, "I:nThreads", &nThreads)) + return nullptr; + + try + { + self->threadPool = new std::shared_ptr<aud::ThreadPool>(new aud::ThreadPool(nThreads)); + } + catch(aud::Exception& e) + { + Py_DECREF(self); + PyErr_SetString(AUDError, e.what()); + return nullptr; + } + } + + return (PyObject *)self; +} + +static void +ThreadPool_dealloc(ThreadPoolP* self) +{ + if(self->threadPool) + delete reinterpret_cast<std::shared_ptr<aud::ThreadPool>*>(self->threadPool); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyMethodDef ThreadPool_methods[] = { + { nullptr } /* Sentinel */ +}; + +PyDoc_STRVAR(M_aud_ThreadPool_doc, + "A ThreadPool is used to parallelize convolution efficiently."); + +PyTypeObject ThreadPoolType = { + PyVarObject_HEAD_INIT(nullptr, 0) + "aud.ThreadPool", /* tp_name */ + sizeof(ThreadPoolP), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ThreadPool_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_ThreadPool_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ThreadPool_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + ThreadPool_new, /* tp_new */ +}; + +AUD_API PyObject* ThreadPool_empty() +{ + return ThreadPoolType.tp_alloc(&ThreadPoolType, 0); +} + + +AUD_API ThreadPoolP* checkThreadPool(PyObject* threadPool) +{ + if(!PyObject_TypeCheck(threadPool, &ThreadPoolType)) + { + PyErr_SetString(PyExc_TypeError, "Object is not of type ThreadPool!"); + return nullptr; + } + + return (ThreadPoolP*)threadPool; +} + + +bool initializeThreadPool() +{ + return PyType_Ready(&ThreadPoolType) >= 0; +} + + +void addThreadPoolToModule(PyObject* module) +{ + Py_INCREF(&ThreadPoolType); + PyModule_AddObject(module, "ThreadPool", (PyObject *)&ThreadPoolType); +} diff --git a/extern/audaspace/bindings/python/PyThreadPool.h b/extern/audaspace/bindings/python/PyThreadPool.h new file mode 100644 index 00000000000..e38d905f52a --- /dev/null +++ b/extern/audaspace/bindings/python/PyThreadPool.h @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright 2009-2015 Juan Francisco Crespo Galán +* +* 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. +******************************************************************************/ + +#pragma once + +#include <Python.h> +#include "Audaspace.h" + +typedef void Reference_ThreadPool; + +typedef struct { + PyObject_HEAD + Reference_ThreadPool* threadPool; +} ThreadPoolP; + +extern AUD_API PyObject* ThreadPool_empty(); +extern AUD_API ThreadPoolP* checkThreadPool(PyObject* ThreadPool); + +bool initializeThreadPool(); +void addThreadPoolToModule(PyObject* module);
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/examples/binaural.py b/extern/audaspace/bindings/python/examples/binaural.py new file mode 100644 index 00000000000..e7a2f6cf6d9 --- /dev/null +++ b/extern/audaspace/bindings/python/examples/binaural.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +import aud, sys, time, multiprocessing +device = aud.Device() +hrtf = aud.HRTF().loadLeftHrtfSet(".wav", sys.argv[2]) +threadPool = aud.ThreadPool(multiprocessing.cpu_count()) +source = aud.Source(0, 0, 0) +sound = aud.Sound.file(sys.argv[1]).rechannel(1).binaural(hrtf, source, threadPool) +handle = device.play(sound) + +while handle.status: + source.azimuth += 1 + print("Azimuth: " + str(source.azimuth)) + time.sleep(0.1)
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/examples/convolution.py b/extern/audaspace/bindings/python/examples/convolution.py new file mode 100644 index 00000000000..4de25b0336a --- /dev/null +++ b/extern/audaspace/bindings/python/examples/convolution.py @@ -0,0 +1,10 @@ +#!/usr/bin/python +import aud, sys, time, multiprocessing +device = aud.Device() +ir = aud.ImpulseResponse(aud.Sound.file(sys.argv[2])) +threadPool = aud.ThreadPool(multiprocessing.cpu_count()) +sound = aud.Sound.file(sys.argv[1]).convolver(ir, threadPool) +handle = device.play(sound) +handle.volume = 0.1 +while handle.status: + time.sleep(0.1)
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/examples/dynamicmusic.py b/extern/audaspace/bindings/python/examples/dynamicmusic.py new file mode 100644 index 00000000000..348e2496c0a --- /dev/null +++ b/extern/audaspace/bindings/python/examples/dynamicmusic.py @@ -0,0 +1,20 @@ +import aud, sys, time + +device=aud.Device() +dMusic = aud.DynamicMusic(device) +sound1 = aud.Sound.file(sys.argv[1]) +sound2 = aud.Sound.file(sys.argv[2]) +effect = aud.Sound.file(sys.argv[3]) + +dMusic.addScene(sound1) +dMusic.addScene(sound2) +dMusic.addTransition(1,2,effect) + +dMusic.fadeTime=3 +dMusic.volume=0.5 + +dMusic.scene=1 +time.sleep(5) +dMusic.scene=2 + +time.sleep(500)
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/examples/playbackmanager.py b/extern/audaspace/bindings/python/examples/playbackmanager.py new file mode 100644 index 00000000000..2aa1c283545 --- /dev/null +++ b/extern/audaspace/bindings/python/examples/playbackmanager.py @@ -0,0 +1,27 @@ +import aud, sys, time + +device=aud.Device() +manager = aud.PlaybackManager(device) +sound1 = aud.Sound.file(sys.argv[1]) +sound2 = aud.Sound.file(sys.argv[2]) +sound3 = aud.Sound.file(sys.argv[3]) +sound4 = aud.Sound.file(sys.argv[4]) + +manager.play(sound1, 0) +manager.play(sound2, 0) +manager.play(sound3, 1) +manager.play(sound4, 1) + +manager.setVolume(0.2, 0) +time.sleep(5) +manager.setVolume(0.0, 1) +time.sleep(5) +manager.pause(0) +time.sleep(5) +manager.setVolume(0.5, 1) +manager.setVolume(1.0, 0) +time.sleep(5) +manager.stop(1) +manager.resume(0) + +time.sleep(500)
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/examples/player.py b/extern/audaspace/bindings/python/examples/player.py new file mode 100644 index 00000000000..8acf4ac833f --- /dev/null +++ b/extern/audaspace/bindings/python/examples/player.py @@ -0,0 +1,7 @@ +#!/usr/bin/python +import aud, sys, time +device = aud.Device() +sound = aud.Sound.file(sys.argv[1]) +handle = device.play(sound) +while handle.status: + time.sleep(0.1) diff --git a/extern/audaspace/bindings/python/examples/randomSounds.py b/extern/audaspace/bindings/python/examples/randomSounds.py new file mode 100644 index 00000000000..113b0921f09 --- /dev/null +++ b/extern/audaspace/bindings/python/examples/randomSounds.py @@ -0,0 +1,21 @@ +import aud, sys, time + +device=aud.Device() +sound1 = aud.Sound.file(sys.argv[1]) +sound2 = aud.Sound.file(sys.argv[2]) +sound3 = aud.Sound.file(sys.argv[3]) +sound4 = aud.Sound.file(sys.argv[4]) +list=aud.Sound.list(True) + +list.addSound(sound1) +list.addSound(sound2) +list.addSound(sound3) +list.addSound(sound4) +mutable=aud.Sound.mutable(list) + +device.lock() +handle=device.play(mutable) +handle.loop_count=2 +device.unlock() + +time.sleep(500)
\ No newline at end of file diff --git a/extern/audaspace/bindings/python/examples/simple.py b/extern/audaspace/bindings/python/examples/simple.py new file mode 100644 index 00000000000..7aa45b41042 --- /dev/null +++ b/extern/audaspace/bindings/python/examples/simple.py @@ -0,0 +1,7 @@ +#!/usr/bin/python +import aud, time +device = aud.Device() +sine = aud.Sound.sine(440) +square = sine.threshold() +handle = device.play(square) +time.sleep(3) diff --git a/extern/audaspace/bindings/python/examples/siren.py b/extern/audaspace/bindings/python/examples/siren.py new file mode 100644 index 00000000000..071279b162d --- /dev/null +++ b/extern/audaspace/bindings/python/examples/siren.py @@ -0,0 +1,19 @@ +#!/usr/bin/python +import aud, math, time +length = 0.5 +fadelength = 0.05 + +device = aud.Device() +high = aud.Sound.sine(880).limit(0, length).fadein(0, fadelength).fadeout(length - fadelength, length) +low = aud.Sound.sine(700).limit(0, length).fadein(0, fadelength).fadeout(length - fadelength, length).volume(0.6) +sound = high.join(low) +handle = device.play(sound) +handle.loop_count = -1 + +start = time.time() + +while time.time() - start < 10: + angle = time.time() - start + + handle.location = [math.sin(angle), 0, -math.cos(angle)] + diff --git a/extern/audaspace/bindings/python/examples/siren2.py b/extern/audaspace/bindings/python/examples/siren2.py new file mode 100644 index 00000000000..35e1a600581 --- /dev/null +++ b/extern/audaspace/bindings/python/examples/siren2.py @@ -0,0 +1,23 @@ +#!/usr/bin/python +import aud, math, time +length = 0.5 +fadelength = 0.05 +runtime = 10 +distance = 100 +velocity = 2 * distance / runtime + +device = aud.Device() +high = aud.Sound.sine(880).limit(0, length).fadein(0, fadelength).fadeout(length - fadelength, length) +low = aud.Sound.sine(700).limit(0, length).fadein(0, fadelength).fadeout(length - fadelength, length).volume(0.6) +sound = high.join(low) +handle = device.play(sound) +handle.loop_count = -1 + +handle.velocity = [velocity, 0, 0] + +start = time.time() + +while time.time() - start < runtime: + location = -distance + velocity * (time.time() - start) + + handle.location = [location, 10, 0] diff --git a/extern/audaspace/bindings/python/examples/tetris.py b/extern/audaspace/bindings/python/examples/tetris.py new file mode 100644 index 00000000000..236a6fa59c1 --- /dev/null +++ b/extern/audaspace/bindings/python/examples/tetris.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +import aud, math, time + +def parseNotes(notes, bpm, basefreq, rate = 44100, + notechars = "XXXCXDXEFXGXAXHcXdXefXgXaXhp"): + pos = 0 + fadelength = 60/bpm/10 + halfchars = "#b" + durationchars = "2345678" + sound = None + + while pos < len(notes): + char = notes[pos] + mod = None + dur = 1 + pos += 1 + while pos < len(notes) and notes[pos] not in notechars: + if notes[pos] in halfchars: + mod = notes[pos] + elif notes[pos] in durationchars: + dur = notes[pos] + pos += 1 + + freq = notechars.find(char) + if mod == '#': + freq += 1 + elif mod == 'b': + freq -= 1 + + freq = math.pow(2, freq/12)*basefreq + length = float(dur)*60/bpm + + snd = aud.Sound.square(freq, rate) + if char == 'p': + snd = snd.volume(0) + snd = snd.limit(0, length) + snd = snd.fadein(0, fadelength) + snd = snd.fadeout(length - fadelength, fadelength) + + if sound: + sound = sound.join(snd) + else: + sound = snd + return sound + +def tetris(bpm = 300, freq = 220, rate = 44100): + notes = "e2Hcd2cH A2Ace2dc H3cd2e2 c2A2A4 pd2fa2gf e3ce2dc H2Hcd2e2 c2A2A2p2" + s11 = parseNotes(notes, bpm, freq, rate) + + notes = "e4c4 d4H4 c4A4 G#4p4 e4c4 d4H4 A2c2a4 g#4p4" + s12 = parseNotes(notes, bpm, freq, rate) + + notes = "EeEeEeEe AaAaAaAa AbabAbabAbabAbab AaAaAAHC DdDdDdDd CcCcCcCc HhHhHhHh AaAaA2p2" + s21 = parseNotes(notes, bpm, freq, rate, notechars = "AXHCXDXEFXGXaXhcXdXefXgXp") + + notes = "aeaeaeae g#dg#dg#dg#d aeaeaeae g#dg#dg#2p2 aeaeaeae g#dg#dg#dg#d aeaeaeae g#dg#dg#2p2" + s22 = parseNotes(notes, bpm, freq/2, rate) + + return s11.join(s12).join(s11).volume(0.5).mix(s21.join(s22).join(s21).volume(0.3)) + +if __name__ == "__main__": + dev = aud.Device() + handle = dev.play(tetris(300, 220, dev.rate)) + while handle.status: + time.sleep(0.1) + diff --git a/extern/audaspace/bindings/python/examples/tetris2.py b/extern/audaspace/bindings/python/examples/tetris2.py new file mode 100644 index 00000000000..08708581af6 --- /dev/null +++ b/extern/audaspace/bindings/python/examples/tetris2.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +import aud, math, time + +def parseNotes(notes, bpm, basefreq, rate = 44100, + notechars = "XXXCXDXEFXGXAXHcXdXefXgXaXhp"): + pos = 0 + fadelength = 60/bpm/10 + halfchars = "#b" + durationchars = "2345678" + position = 0 + sequence = aud.Sequence() + + while pos < len(notes): + char = notes[pos] + mod = None + dur = 1 + pos += 1 + while pos < len(notes) and notes[pos] not in notechars: + if notes[pos] in halfchars: + mod = notes[pos] + elif notes[pos] in durationchars: + dur = notes[pos] + pos += 1 + + freq = notechars.find(char) + if mod == '#': + freq += 1 + elif mod == 'b': + freq -= 1 + + freq = math.pow(2, freq/12)*basefreq + length = float(dur)*60/bpm + + note = aud.Sound.square(freq, rate).fadein(0, fadelength).fadeout(length - fadelength, fadelength) + + entry = sequence.add(note, position, position + length, 0) + if char == 'p': + entry.muted = True + + position += length + + return sequence.limit(0, position) + +def tetris(bpm = 300, freq = 220, rate = 44100): + notes = "e2Hcd2cH A2Ace2dc H3cd2e2 c2A2A4 pd2fa2gf e3ce2dc H2Hcd2e2 c2A2A2p2" + s11 = parseNotes(notes, bpm, freq, rate) + + notes = "e4c4 d4H4 c4A4 G#4p4 e4c4 d4H4 A2c2a4 g#4p4" + s12 = parseNotes(notes, bpm, freq, rate) + + notes = "EeEeEeEe AaAaAaAa AbabAbabAbabAbab AaAaAAHC DdDdDdDd CcCcCcCc HhHhHhHh AaAaA2p2" + s21 = parseNotes(notes, bpm, freq, rate, notechars = "AXHCXDXEFXGXaXhcXdXefXgXp") + + notes = "aeaeaeae g#dg#dg#dg#d aeaeaeae g#dg#dg#2p2 aeaeaeae g#dg#dg#dg#d aeaeaeae g#dg#dg#2p2" + s22 = parseNotes(notes, bpm, freq/2, rate) + + return s11.join(s12).join(s11).volume(0.5).mix(s21.join(s22).join(s21).volume(0.3)) + +if __name__ == "__main__": + dev = aud.Device() + handle = dev.play(tetris(300, 220, dev.rate)) + while handle.status: + time.sleep(0.1) + diff --git a/extern/audaspace/bindings/python/examples/tetris3.py b/extern/audaspace/bindings/python/examples/tetris3.py new file mode 100644 index 00000000000..aa66d5457d3 --- /dev/null +++ b/extern/audaspace/bindings/python/examples/tetris3.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +import aud, math, time + +def parseNotes(notes, bpm, basefreq, rate = 44100, + notechars = "XXXCXDXEFXGXAXHcXdXefXgXaXhp"): + pos = 0 + fadelength = 60/bpm/10 + halfchars = "#b" + durationchars = "2345678" + position = 0 + sequence = aud.Sequence() + + while pos < len(notes): + char = notes[pos] + mod = None + dur = 1 + pos += 1 + while pos < len(notes) and notes[pos] not in notechars: + if notes[pos] in halfchars: + mod = notes[pos] + elif notes[pos] in durationchars: + dur = notes[pos] + pos += 1 + + freq = notechars.find(char) + if mod == '#': + freq += 1 + elif mod == 'b': + freq -= 1 + + freq = math.pow(2, freq/12)*basefreq + length = float(dur)*60/bpm + + if char != 'p': + note = aud.Sound.square(freq, rate).fadein(0, fadelength).fadeout(length - fadelength, fadelength) + + sequence.add(note, position, position + length, 0) + + position += length + + return sequence.limit(0, position) + +def tetris(bpm = 300, freq = 220, rate = 44100): + notes = "e2Hcd2cH A2Ace2dc H3cd2e2 c2A2A4 pd2fa2gf e3ce2dc H2Hcd2e2 c2A2A2p2" + s11 = parseNotes(notes, bpm, freq, rate) + + notes = "e4c4 d4H4 c4A4 G#4p4 e4c4 d4H4 A2c2a4 g#4p4" + s12 = parseNotes(notes, bpm, freq, rate) + + notes = "EeEeEeEe AaAaAaAa AbabAbabAbabAbab AaAaAAHC DdDdDdDd CcCcCcCc HhHhHhHh AaAaA2p2" + s21 = parseNotes(notes, bpm, freq, rate, notechars = "AXHCXDXEFXGXaXhcXdXefXgXp") + + notes = "aeaeaeae g#dg#dg#dg#d aeaeaeae g#dg#dg#2p2 aeaeaeae g#dg#dg#dg#d aeaeaeae g#dg#dg#2p2" + s22 = parseNotes(notes, bpm, freq/2, rate) + + return s11.join(s12).join(s11).volume(0.5).mix(s21.join(s22).join(s21).volume(0.3)) + +if __name__ == "__main__": + dev = aud.Device() + handle = dev.play(tetris(300, 220, dev.rate)) + while handle.status: + time.sleep(0.1) + diff --git a/extern/audaspace/bindings/python/setup.py.in b/extern/audaspace/bindings/python/setup.py.in new file mode 100644 index 00000000000..add1a2d1475 --- /dev/null +++ b/extern/audaspace/bindings/python/setup.py.in @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +import sys +import os +import codecs +import numpy + +from distutils.core import setup, Extension + +if len(sys.argv) > 2 and sys.argv[1] == '--build-docs': + import subprocess + from distutils.core import Distribution + from distutils.command.build import build + + dist = Distribution() + cmd = build(dist) + cmd.finalize_options() + #print(cmd.build_platlib) + + os.environ['PYTHONPATH'] = os.path.join(os.getcwd(), cmd.build_platlib) + os.environ['LD_LIBRARY_PATH'] = os.getcwd() + + ret = subprocess.call(sys.argv[2:]) + sys.exit(ret) + + +# the following line is not working due to https://bugs.python.org/issue9023 +#source_directory = os.path.relpath('@PYTHON_SOURCE_DIRECTORY@') +source_directory = '@PYTHON_SOURCE_DIRECTORY@' + +extra_args = [] + +if sys.platform == 'win32': + extra_args.append('/EHsc') + extra_args.append('/DAUD_BUILD_SHARED_LIBRARY') +else: + extra_args.append('-std=c++11') + +audaspace = Extension( + 'aud', + include_dirs = ['@CMAKE_CURRENT_BINARY_DIR@', '@FFTW_INCLUDE_DIR@', os.path.join(source_directory, '../../include'), numpy.get_include()], + libraries = ['audaspace'], + library_dirs = ['.', 'Release', 'Debug'], + language = 'c++', + extra_compile_args = extra_args, + sources = [os.path.join(source_directory, file) for file in ['PyAPI.cpp', 'PyDevice.cpp', 'PyHandle.cpp', 'PySound.cpp', 'PySequenceEntry.cpp', 'PySequence.cpp', 'PyPlaybackManager.cpp', 'PyDynamicMusic.cpp', 'PyThreadPool.cpp', 'PySource.cpp'] + (['PyImpulseResponse.cpp', 'PyHRTF.cpp'] if '@WITH_FFTW@' == 'ON' else [])] +) + +setup( + name = 'audaspace', + version = '@AUDASPACE_LONG_VERSION@', + description = 'Audaspace is a high level audio library.', + author = 'Jörg Müller', + author_email = 'nexyon@gmail.com', + url = 'https://github.com/audaspace/audaspace', + license = 'Apache License 2.0', + long_description = codecs.open(os.path.join(source_directory, '../../README.md'), 'r', 'utf-8').read(), + ext_modules = [audaspace], + headers = [os.path.join(source_directory, file) for file in ['PyAPI.h', 'PyDevice.h', 'PyHandle.h', 'PySound.h', 'PySequenceEntry.h', 'PySequence.h', 'PyPlaybackManager.h', 'PyDynamicMusic.h', 'PyThreadPool.h', 'PySource.h'] + (['PyImpulseResponse.h', 'PyHRTF.h'] if '@WITH_FFTW@' == 'ON' else [])] + ['Audaspace.h'] +) + |