This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/extern/audaspace/bindings/python/PySound.cpp
Joerg Mueller bdbc7e12a0 Audaspace: added audio file streams functionality.
On the blender side this commit fixes importing video files with audio
and video streams that do not share the same start time and duration.

Differential Revision: https://developer.blender.org/D12353
2021-09-18 21:45:33 +02:00

2032 lines
53 KiB
C++

/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "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/Modulator.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", "stream", nullptr};
const char* filename = nullptr;
int stream = 0;
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|i:Sound", const_cast<char**>(kwlist), &filename, &stream))
{
Py_DECREF(self);
return nullptr;
}
try
{
self->sound = new std::shared_ptr<ISound>(new File(filename, stream));
}
catch(Exception& e)
{
Py_DECREF(self);
PyErr_SetString(AUDError, e.what());
return nullptr;
}
}
return (PyObject *)self;
}
PyDoc_STRVAR(M_aud_Sound_data_doc,
".. classmethod:: 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());
return reinterpret_cast<PyObject*>(array);
}
PyDoc_STRVAR(M_aud_Sound_write_doc,
".. classmethod:: 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");
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,
".. classmethod:: 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,
".. classmethod:: cache()\n\n"
" Caches a sound into RAM.\n\n"
" This saves CPU usage needed for decoding and file access if the\n"
" underlying sound reads from a file on the harddisk,\n"
" 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::\n\n"
" Raw PCM data needs a lot of space, only buffer\n"
" 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,
".. classmethod:: 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::\n\n"
" If the file doesn't exist or can't be read you will\n"
" not get an exception immediately, but when you try to start\n"
" playback of that sound.\n");
static PyObject *
Sound_file(PyTypeObject* type, PyObject* args)
{
const char* filename = nullptr;
int stream = 0;
if(!PyArg_ParseTuple(args, "s|i:file", &filename, &stream))
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, stream));
}
catch(Exception& e)
{
Py_DECREF(self);
PyErr_SetString(AUDError, e.what());
return nullptr;
}
}
return (PyObject *)self;
}
PyDoc_STRVAR(M_aud_Sound_sawtooth_doc,
".. classmethod:: 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\n"
" 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,
".. classmethod:: silence(rate=48000)\n\n"
" Creates a silence sound which plays simple silence.\n\n"
" :arg rate: The sampling rate in Hz. It's recommended to set this\n"
" 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_silence(PyTypeObject* type, PyObject* args)
{
double rate = 48000;
if(!PyArg_ParseTuple(args, "|d:sawtooth", &rate))
return nullptr;
Sound* self;
self = (Sound*)type->tp_alloc(type, 0);
if(self != nullptr)
{
try
{
self->sound = new std::shared_ptr<ISound>(new Silence((SampleRate)rate));
}
catch(Exception& e)
{
Py_DECREF(self);
PyErr_SetString(AUDError, e.what());
return nullptr;
}
}
return (PyObject *)self;
}
PyDoc_STRVAR(M_aud_Sound_sine_doc,
".. classmethod:: 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\n"
" 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,
".. classmethod:: 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\n"
" 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,
".. classmethod:: 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\n"
" 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,
".. classmethod:: accumulate(additive=False)\n\n"
" Accumulates a sound by summing over positive input\n"
" differences thus generating a monotonic sigal.\n"
" If additivity is set to true negative input differences get added too,\n"
" but positive ones with a factor of two.\n\n"
" 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,
".. classmethod:: ADSR(attack, decay, sustain, release)\n\n"
" Attack-Decay-Sustain-Release envelopes the volume of a sound.\n"
" 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,
".. classmethod:: 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,
".. classmethod:: 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,
".. classmethod:: fadein(start, length)\n\n"
" Fades a sound in by raising the volume linearly in the given\n"
" 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,
".. classmethod:: fadeout(start, length)\n\n"
" Fades a sound in by lowering the volume linearly in the given\n"
" 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::\n\n"
" After the fade this sound plays silence, so that\n"
" 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,
".. classmethod:: 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\n"
" If the first value of the a sequence is 0,\n"
" it will be set to 1 automatically.\n"
" If the first value of the a sequence is neither 0 nor 1, all\n"
" filter coefficients will be scaled by this value so that it is 1\n"
" 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,
".. classmethod:: highpass(frequency, Q=0.5)\n\n"
" Creates a second order highpass filter based on the transfer\n"
" function :math:`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,
".. classmethod:: 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,
".. classmethod:: loop(count)\n\n"
" Loops a sound.\n\n"
" :arg count: How often the sound should be looped.\n"
" Negative values mean endlessly.\n"
" :type count: integer\n"
" :return: The created :class:`Sound` object.\n"
" :rtype: :class:`Sound`\n\n"
" .. note::\n\n"
" This is a filter function, you might consider using\n"
" :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,
".. classmethod:: lowpass(frequency, Q=0.5)\n\n"
" Creates a second order lowpass filter based on the transfer "
" function :math:`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_modulate_doc,
".. classmethod:: modulate(sound)\n\n"
" Modulates two factories.\n\n"
" :arg sound: The sound to modulate over the other.\n"
" :type sound: :class:`Sound`\n"
" :return: The created :class:`Sound` object.\n"
" :rtype: :class:`Sound`\n\n"
" .. note::\n\n"
" The two factories have to have the same specifications\n"
" (channels and samplerate).");
static PyObject *
Sound_modulate(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 Modulator(*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_pitch_doc,
".. classmethod:: 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::\n\n"
" This is done by changing the sample rate of the\n"
" underlying sound, which has to be an integer, so the factor\n"
" value rounded and the factor may not be 100 % accurate.\n\n"
" .. note::\n\n"
" This is a filter function, you might consider using\n"
" :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,
".. classmethod:: 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,
".. classmethod:: 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,
".. classmethod:: reverse()\n\n"
" Plays a sound reversed.\n\n"
" :return: The created :class:`Sound` object.\n"
" :rtype: :class:`Sound`\n\n"
" .. note::\n\n"
" The sound has to have a finite length and has to be seekable.\n"
" It's recommended to use this only with factories with\n"
" fast and accurate seeking, which is not true for encoded audio\n"
" files, such ones should be buffered using :meth:`cache` before\n"
" being played reversed.\n\n"
" .. warning::\n\n"
" If seeking is not accurate in the underlying sound\n"
" 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,
".. classmethod:: 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,
".. classmethod:: threshold(threshold = 0)\n\n"
" Makes a threshold wave out of an audio wave by setting all samples\n"
" with a amplitude >= threshold to 1, all <= -threshold to -1 and\n"
" all between to 0.\n\n"
" :arg threshold: Threshold value over which an amplitude counts\n"
" non-zero.\n\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,
".. classmethod:: 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::\n\n"
" Should be in the range [0, 1] to avoid clipping.\n\n"
" .. note::\n\n"
" This is a filter function, you might consider using\n"
" :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,
".. classmethod:: 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::\n\n"
" The two factories have to have the same specifications\n"
" (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,
".. classmethod:: 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::\n\n"
" The two factories have to have the same specifications\n"
" (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,
".. classmethod:: 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,
".. classmethod:: list()\n\n"
" Creates an empty sound list that can contain several sounds.\n\n"
" :arg random: whether 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,
".. classmethod:: 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,
".. classmethod:: 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,
".. classmethod:: 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,
".. classmethod:: 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_VARARGS | 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
},
{"modulate", (PyCFunction)Sound_modulate, METH_O,
M_aud_Sound_modulate_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_array1(false);
return PyType_Ready(&SoundType) >= 0;
}
void addSoundToModule(PyObject* module)
{
Py_INCREF(&SoundType);
PyModule_AddObject(module, "Sound", (PyObject *)&SoundType);
}