/******************************************************************************* * 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 #include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include using namespace aud; extern PyObject* AUDError; static void Sound_dealloc(Sound* self) { if(self->sound) delete reinterpret_cast*>(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(kwlist), &filename)) { Py_DECREF(self); return nullptr; } try { self->sound = new std::shared_ptr(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, ".. 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 sound = *reinterpret_cast*>(self->sound); auto stream_buffer = std::dynamic_pointer_cast(sound); if(!stream_buffer) stream_buffer = std::make_shared(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(PyArray_SimpleNew(2, dimensions, NPY_FLOAT)); sample_t* data = reinterpret_cast(PyArray_DATA(array)); std::memcpy(data, buffer->getBuffer(), buffer->getSize()); return reinterpret_cast(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(kwlist), &filename, &rate, &channels, &format, &container, &codec, &bitrate, &buffersize)) return nullptr; try { std::shared_ptr reader = (*reinterpret_cast*>(self->sound))->createReader(); DeviceSpecs specs; specs.specs = reader->getSpecs(); if((rate != RATE_INVALID) && (specs.rate != rate)) { specs.rate = rate; reader = std::make_shared(reader, rate); } if((channels != CHANNELS_INVALID) && (specs.channels != channels)) { specs.channels = channels; reader = std::make_shared(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 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(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(PyArray_DIM(array, 1)); int size = PyArray_DIM(array, 0) * AUD_SAMPLE_SIZE(specs); std::shared_ptr buffer = std::make_shared(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(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(new StreamBuffer(*reinterpret_cast*>(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; 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(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, ".. 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(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(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(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(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(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(new Accumulator(*reinterpret_cast*>(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(new ADSR(*reinterpret_cast*>(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(new Delay(*reinterpret_cast*>(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(new Envelope(*reinterpret_cast*>(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(new Fader(*reinterpret_cast*>(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(new Fader(*reinterpret_cast*>(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 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(new IIRFilter(*reinterpret_cast*>(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(new Highpass(*reinterpret_cast*>(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(new Limiter(*reinterpret_cast*>(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(new Loop(*reinterpret_cast*>(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(new Lowpass(*reinterpret_cast*>(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(new Modulator(*reinterpret_cast*>(self->sound), *reinterpret_cast*>(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(new Pitch(*reinterpret_cast*>(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); specs.rate = RATE_INVALID; specs.format = FORMAT_INVALID; parent->sound = new std::shared_ptr(new ChannelMapper(*reinterpret_cast*>(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(new JOSResample(*reinterpret_cast*>(self->sound), specs)); else parent->sound = new std::shared_ptr(new LinearResample(*reinterpret_cast*>(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(new Reverse(*reinterpret_cast*>(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(new Sum(*reinterpret_cast*>(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(new Threshold(*reinterpret_cast*>(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(new Volume(*reinterpret_cast*>(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(new Double(*reinterpret_cast*>(self->sound), *reinterpret_cast*>(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(new Superpose(*reinterpret_cast*>(self->sound), *reinterpret_cast*>(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(new PingPong(*reinterpret_cast*>(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(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(new MutableSound(*reinterpret_cast*>(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*>(self->sound))->addSound(*reinterpret_cast*>(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(new ConvolverSound(*reinterpret_cast*>(self->sound), *reinterpret_cast*>(filter->impulseResponse), *reinterpret_cast*>(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(new BinauralSound(*reinterpret_cast*>(self->sound), *reinterpret_cast*>(hrtfs->hrtf), *reinterpret_cast*>(source->source), *reinterpret_cast*>(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*>(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*>(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); }