Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package audaspace for openSUSE:Factory 
checked in at 2026-03-23 17:11:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/audaspace (Old)
 and      /work/SRC/openSUSE:Factory/.audaspace.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "audaspace"

Mon Mar 23 17:11:28 2026 rev:25 rq:1341585 version:1.9.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/audaspace/audaspace.changes      2025-11-13 
17:30:38.591449511 +0100
+++ /work/SRC/openSUSE:Factory/.audaspace.new.8177/audaspace.changes    
2026-03-23 17:11:45.457653857 +0100
@@ -1,0 +2,13 @@
+Fri Mar 20 17:48:37 UTC 2026 - Marcus Rueckert <[email protected]>
+
+- Update to release 1.9.0
+  - New Echo effect (thanks AGoldenDragon)
+  - Bugfix for Catmull-Rom spline interpolation
+  - Build fixes for Mac (thanks Jonas Holzman)
+  - Python API documentation for class constructors (thanks Andrej
+    Zhilenkov)
+  - CMake fixes (thanks Campbell Barton)
+  - Sample rate type warnings (thanks Jacques Lucke and Ray
+    Molenkamp)
+
+-------------------------------------------------------------------

Old:
----
  audaspace-1.8.0.tar.gz

New:
----
  audaspace-1.9.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ audaspace.spec ++++++
--- /var/tmp/diff_new_pack.aeYK0z/_old  2026-03-23 17:11:46.145682514 +0100
+++ /var/tmp/diff_new_pack.aeYK0z/_new  2026-03-23 17:11:46.145682514 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package audaspace
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,8 +18,8 @@
 
 # See also http://en.opensuse.org/openSUSE:Shared_library_packaging_policy
 # NOTE: sover follows version.
-%define sover 1_8
-%define soversion 1.8
+%define sover 1_9
+%define soversion 1.9
 
 %if %{pkg_vcmp pipewire-devel >= 1.4.0}
 %bcond_without pipewire
@@ -28,7 +28,7 @@
 %endif
 
 Name:           audaspace
-Version:        1.8.0
+Version:        1.9.0
 Release:        0
 Summary:        A High-Level Audio Library
 License:        Apache-2.0

++++++ audaspace-1.8.0.tar.gz -> audaspace-1.9.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/AUTHORS new/audaspace-1.9.0/AUTHORS
--- old/audaspace-1.8.0/AUTHORS 2025-11-13 08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/AUTHORS 2026-03-20 18:10:20.000000000 +0100
@@ -31,6 +31,10 @@
 
 - Kacey La
 
+The echo effect has been provided by
+
+- AGoldenDragon
+
 Several people provided fixes:
 
 - Aaron Carlisle
@@ -40,3 +44,7 @@
 - Lalit Shankar Chowdhury
 - Nathan Burnham
 - Weizhen Huang
+- Jonas Holzman
+- Andrej Zhilenkov
+- Jacques Lucke
+- Ray Molenkamp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/CHANGES new/audaspace-1.9.0/CHANGES
--- old/audaspace-1.8.0/CHANGES 2025-11-13 08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/CHANGES 2026-03-20 18:10:20.000000000 +0100
@@ -1,3 +1,23 @@
+Audaspace 1.9
+
+- New Echo effect (thanks AGoldenDragon)
+- Bugfix for Catmull-Rom spline interpolation
+- Build fixes for Mac (thanks Jonas Holzman)
+- Python API documentation for class constructors (thanks Andrej Zhilenkov)
+- CMake fixes (thanks Campbell Barton)
+- Sample rate type warnings (thanks Jacques Lucke and Ray Molenkamp)
+
+Detailed list of changes:
+
+e4a3d40 Porting fixes from blender
+3348de2 PyDoc: Add constructor docstrings to aud types
+7b04aa9 Build: macOS: Fix bitwise operation enum type warning in Audaspace
+a931d65 Bugfix: Catmull-Rom spline interpolation was wrong.
+1550e14 Fix AUD_Sound_Echo wrongly inside WITH_CONVOLUTION guard
+ddda09e Echo: change wet/dry behavior.
+31a59c4 Fixing Echo effect:
+c3d4ba9 Implemented a new Echo effect (#63)
+
 Audaspace 1.8
 
 - Time stretching and pitch scaling including animation and python API added 
(thanks Kacey La!).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/CMakeLists.txt 
new/audaspace-1.9.0/CMakeLists.txt
--- old/audaspace-1.8.0/CMakeLists.txt  2025-11-13 08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/CMakeLists.txt  2026-03-20 18:10:20.000000000 +0100
@@ -23,7 +23,7 @@
 
 project(audaspace)
 
-set(AUDASPACE_VERSION 1.8)
+set(AUDASPACE_VERSION 1.9)
 set(AUDASPACE_LONG_VERSION ${AUDASPACE_VERSION}.0)
 
 if(DEFINED AUDASPACE_CMAKE_CFG)
@@ -92,6 +92,8 @@
        src/fx/VolumeReader.cpp
        src/fx/VolumeSound.cpp
        src/fx/VolumeStorage.cpp
+       src/fx/Echo.cpp
+       src/fx/EchoReader.cpp
        src/generator/Sawtooth.cpp
        src/generator/SawtoothReader.cpp
        src/generator/Silence.cpp
@@ -206,6 +208,8 @@
        include/fx/VolumeReader.h
        include/fx/VolumeSound.h
        include/fx/VolumeStorage.h
+       include/fx/Echo.h
+       include/fx/EchoReader.h
        include/generator/Sawtooth.h
        include/generator/SawtoothReader.h
        include/generator/Silence.h
@@ -257,9 +261,9 @@
 set(INCLUDE ${CMAKE_CURRENT_BINARY_DIR} include)
 
 if(WIN32)
-       set(LIBRARIES)
+       set(LIBRARIES "")
        if(AUDASPACE_STANDALONE)
-               set(DLLS)
+               set(DLLS "")
                set(LIBRARY_PATH "../lib" CACHE PATH "Path which contains the 
libraries.")
                file(GLOB LIBRARY_DIRS ${LIBRARY_PATH}/*)
                list(APPEND CMAKE_PREFIX_PATH ${LIBRARY_DIRS})
@@ -942,7 +946,6 @@
 # directories
 
 include_directories(${INCLUDE})
-link_directories()
 
 # install configuration
 
@@ -1216,7 +1219,14 @@
        endif()
 
        add_library(audaspace-py ${LIBRARY_TYPE} ${PYTHON_SRC} ${PYTHON_HDR})
-       target_link_libraries(audaspace-py audaspace ${PYTHON_LIBRARIES})
+       # When Blender is built as a Python module, `PYTHON_LIBRARIES` won't be 
defined.
+       # NOTE: AUDASPACE has its own `WITH_PYTHON_MODULE` option with 
different meaning,
+       # so we check the variable is defined instead.
+       if(DEFINED PYTHON_LIBRARIES)
+               target_link_libraries(audaspace-py audaspace 
${PYTHON_LIBRARIES})
+       else()
+               target_link_libraries(audaspace-py audaspace)
+       endif()
        set_target_properties(audaspace-py PROPERTIES SOVERSION 
${AUDASPACE_VERSION})
 
        if(AUDASPACE_STANDALONE)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/C/AUD_Device.cpp 
new/audaspace-1.9.0/bindings/C/AUD_Device.cpp
--- old/audaspace-1.8.0/bindings/C/AUD_Device.cpp       2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/C/AUD_Device.cpp       2026-03-20 
18:10:20.000000000 +0100
@@ -51,7 +51,7 @@
                dspecs.channels = CHANNELS_STEREO;
        if(dspecs.format == FORMAT_INVALID)
                dspecs.format = FORMAT_FLOAT32;
-       if(dspecs.rate == RATE_INVALID)
+       if(dspecs.rate == double(RATE_INVALID))
                dspecs.rate = RATE_48000;
        if(buffersize < 128)
                buffersize = AUD_DEFAULT_BUFFER_SIZE;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/C/AUD_Sound.cpp 
new/audaspace-1.9.0/bindings/C/AUD_Sound.cpp
--- old/audaspace-1.8.0/bindings/C/AUD_Sound.cpp        2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/C/AUD_Sound.cpp        2026-03-20 
18:10:20.000000000 +0100
@@ -51,6 +51,8 @@
 #include "util/Buffer.h"
 #include "Exception.h"
 
+#include "fx/Echo.h"
+
 #ifdef WITH_CONVOLUTION
 #include "fx/BinauralSound.h"
 #include "fx/ConvolverSound.h"
@@ -172,7 +174,7 @@
                DeviceSpecs specs;
                specs.specs = reader->getSpecs();
 
-               if((rate != RATE_INVALID) && (specs.rate != rate))
+               if((rate != double(RATE_INVALID)) && (specs.rate != rate))
                {
                        specs.rate = rate;
                        reader = std::make_shared<JOSResampleReader>(reader, 
rate);
@@ -782,7 +784,22 @@
                return nullptr;
        }
 }
+#endif
 
+AUD_API AUD_Sound* AUD_Sound_Echo(AUD_Sound* sound, float delay, float 
feedback, float mix, bool resetBuffer)
+{
+       assert(sound);
+       try
+       {
+               return new AUD_Sound(new Echo(*sound, delay, feedback, mix, 
resetBuffer));
+       }
+       catch(Exception&)
+       {
+               return nullptr;
+       }
+}
+
+#ifdef WITH_CONVOLUTION
 AUD_API AUD_Sound* AUD_Sound_equalize(AUD_Sound* sound, float *definition, int 
size, float maxFreqEq, int sizeConversion)
 {
        assert(sound);
@@ -792,7 +809,6 @@
        AUD_Sound *equalizer=new AUD_Sound(new Equalizer(*sound, buf, size, 
maxFreqEq, sizeConversion));
        return equalizer;
 }
-
 #endif
 
 #ifdef WITH_RUBBERBAND
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/C/AUD_Sound.h 
new/audaspace-1.9.0/bindings/C/AUD_Sound.h
--- old/audaspace-1.8.0/bindings/C/AUD_Sound.h  2025-11-13 08:09:33.000000000 
+0100
+++ new/audaspace-1.9.0/bindings/C/AUD_Sound.h  2026-03-20 18:10:20.000000000 
+0100
@@ -394,6 +394,17 @@
 */
 extern AUD_API AUD_Sound* AUD_Sound_mutable(AUD_Sound* sound);
 
+    /**
+     * Adds Echo effect to the sound.
+     * \param sound The handle of the sound.
+     * \param delay The delay time in seconds.
+     * \param feedback The feedback amount (0.0 to 1.0).
+     * \param mix The wet/dry mix (0.0 to 1.0).
+     * \param resetBuffer Whether to reset the delay buffer on seek.
+     * \return A handle of the time-stretched, pitch scaled sound.
+     */
+extern AUD_API AUD_Sound* AUD_Sound_Echo(AUD_Sound* sound, float delay, float 
feedback, float mix, bool resetBuffer);
+
 #ifdef WITH_CONVOLUTION
        extern AUD_API AUD_Sound* AUD_Sound_Convolver(AUD_Sound* sound, 
AUD_ImpulseResponse* filter, AUD_ThreadPool* threadPool);
        extern AUD_API AUD_Sound* AUD_Sound_Binaural(AUD_Sound* sound, 
AUD_HRTF* hrtfs, AUD_Source* source, AUD_ThreadPool* threadPool);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/audaspace-1.8.0/bindings/python/PyAnimateableProperty.cpp 
new/audaspace-1.9.0/bindings/python/PyAnimateableProperty.cpp
--- old/audaspace-1.8.0/bindings/python/PyAnimateableProperty.cpp       
2025-11-13 08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PyAnimateableProperty.cpp       
2026-03-20 18:10:20.000000000 +0100
@@ -326,7 +326,13 @@
     {nullptr} /* Sentinel */
 };
 
-PyDoc_STRVAR(M_aud_AnimateableProperty_doc, "An AnimateableProperty object 
stores an array of float values for animating sound properties (e.g. pan, 
volume, pitch-scale)");
+PyDoc_STRVAR(M_aud_AnimateableProperty_doc,
+       ".. class:: AnimateableProperty(count, value=0.0, /)\n\n"
+       "   An AnimateableProperty object stores an array of float values for 
animating sound properties (e.g. pan, volume, pitch-scale).\n\n"
+       "   :arg count: The number of float values to store per frame.\n"
+       "   :type count: int\n"
+       "   :arg value: The initial value for all elements.\n"
+       "   :type value: float\n");
 
 // Note that AnimateablePropertyType name is already taken
 PyTypeObject AnimateablePropertyPyType = {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/python/PyDevice.cpp 
new/audaspace-1.9.0/bindings/python/PyDevice.cpp
--- old/audaspace-1.8.0/bindings/python/PyDevice.cpp    2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PyDevice.cpp    2026-03-20 
18:10:20.000000000 +0100
@@ -711,9 +711,22 @@
 };
 
 PyDoc_STRVAR(M_aud_Device_doc,
-                        "Device objects represent an audio output backend like 
OpenAL or "
+                        ".. class:: Device(type='', rate=48000.0, channels=2, 
format=36, buffer_size=1024, name='')\n\n"
+                        "   Device objects represent an audio output backend 
like OpenAL or "
                         "SDL, but might also represent a file output or RAM 
buffer "
-                        "output.");
+                        "output.\n\n"
+                        "   :arg type: The device type. An empty string means 
the default device.\n"
+                        "   :type type: string\n"
+                        "   :arg rate: The sample rate in Hz.\n"
+                        "   :type rate: double\n"
+                        "   :arg channels: The number of channels.\n"
+                        "   :type channels: int\n"
+                        "   :arg format: The sample format.\n"
+                        "   :type format: int\n"
+                        "   :arg buffer_size: The size of the audio buffer in 
samples.\n"
+                        "   :type buffer_size: int\n"
+                        "   :arg name: The name of the device.\n"
+                        "   :type name: string\n");
 
 static PyTypeObject DeviceType = {
        PyVarObject_HEAD_INIT(nullptr, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/python/PyDynamicMusic.cpp 
new/audaspace-1.9.0/bindings/python/PyDynamicMusic.cpp
--- old/audaspace-1.8.0/bindings/python/PyDynamicMusic.cpp      2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PyDynamicMusic.cpp      2026-03-20 
18:10:20.000000000 +0100
@@ -392,8 +392,11 @@
 };
 
 PyDoc_STRVAR(M_aud_DynamicMusic_doc,
-       "The DynamicMusic object allows to play music depending on a current 
scene, scene changes are managed by the class, with the possibility of custom 
transitions.\n"
-       "The default transition is a crossfade effect, and the default scene is 
silent and has id 0");
+       ".. class:: DynamicMusic(device, /)\n\n"
+       "   The DynamicMusic object allows to play music depending on a current 
scene, scene changes are managed by the class, with the possibility of custom 
transitions.\n"
+       "   The default transition is a crossfade effect, and the default scene 
is silent and has id 0.\n\n"
+       "   :arg device: The device that will be used to play sounds.\n"
+       "   :type device: :class:`Device`\n");
 
 PyTypeObject DynamicMusicType = {
        PyVarObject_HEAD_INIT(nullptr, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/python/PyHRTF.cpp 
new/audaspace-1.9.0/bindings/python/PyHRTF.cpp
--- old/audaspace-1.8.0/bindings/python/PyHRTF.cpp      2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PyHRTF.cpp      2026-03-20 
18:10:20.000000000 +0100
@@ -173,7 +173,8 @@
 };
 
 PyDoc_STRVAR(M_aud_HRTF_doc,
-       "An HRTF object represents a set of head related transfer functions as 
impulse responses. It's used for binaural sound");
+       ".. class:: HRTF()\n\n"
+       "   An HRTF object represents a set of head related transfer functions 
as impulse responses. It's used for binaural sound.\n");
 
 PyTypeObject HRTFType = {
        PyVarObject_HEAD_INIT(nullptr, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/audaspace-1.8.0/bindings/python/PyImpulseResponse.cpp 
new/audaspace-1.9.0/bindings/python/PyImpulseResponse.cpp
--- old/audaspace-1.8.0/bindings/python/PyImpulseResponse.cpp   2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PyImpulseResponse.cpp   2026-03-20 
18:10:20.000000000 +0100
@@ -63,7 +63,10 @@
 };
 
 PyDoc_STRVAR(M_aud_ImpulseResponse_doc,
-       "An ImpulseResponse object represents a filter with which to convolve a 
sound.");
+       ".. class:: ImpulseResponse(sound, /)\n\n"
+       "   An ImpulseResponse object represents a filter with which to 
convolve a sound.\n\n"
+       "   :arg sound: The sound to use as the impulse response.\n"
+       "   :type sound: :class:`Sound`\n");
 
 PyTypeObject ImpulseResponseType = {
        PyVarObject_HEAD_INIT(nullptr, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/audaspace-1.8.0/bindings/python/PyPlaybackManager.cpp 
new/audaspace-1.9.0/bindings/python/PyPlaybackManager.cpp
--- old/audaspace-1.8.0/bindings/python/PyPlaybackManager.cpp   2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PyPlaybackManager.cpp   2026-03-20 
18:10:20.000000000 +0100
@@ -316,7 +316,10 @@
 };
 
 PyDoc_STRVAR(M_aud_PlaybackManager_doc,
-       "A PlabackManager object allows to easily control groups os sounds 
organized in categories.");
+       ".. class:: PlaybackManager(device, /)\n\n"
+       "   A PlaybackManager object allows to easily control groups of sounds 
organized in categories.\n\n"
+       "   :arg device: The device that will be used to play sounds.\n"
+       "   :type device: :class:`Device`\n");
 
 PyTypeObject PlaybackManagerType = {
        PyVarObject_HEAD_INIT(nullptr, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/python/PySequence.cpp 
new/audaspace-1.9.0/bindings/python/PySequence.cpp
--- old/audaspace-1.8.0/bindings/python/PySequence.cpp  2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PySequence.cpp  2026-03-20 
18:10:20.000000000 +0100
@@ -579,7 +579,16 @@
 };
 
 PyDoc_STRVAR(M_aud_Sequence_doc,
-                        "This sound represents sequenced entries to play a 
sound sequence.");
+                        ".. class:: Sequence(channels=2, rate=48000.0, 
fps=30.0, muted=False)\n\n"
+                        "   This sound represents sequenced entries to play a 
sound sequence.\n\n"
+                        "   :arg channels: The number of channels.\n"
+                        "   :type channels: int\n"
+                        "   :arg rate: The sample rate in Hz.\n"
+                        "   :type rate: double\n"
+                        "   :arg fps: The frames per second of the sequence.\n"
+                        "   :type fps: float\n"
+                        "   :arg muted: Whether the sequence is muted.\n"
+                        "   :type muted: bool\n");
 
 extern PyTypeObject SoundType;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/python/PySound.cpp 
new/audaspace-1.9.0/bindings/python/PySound.cpp
--- old/audaspace-1.8.0/bindings/python/PySound.cpp     2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PySound.cpp     2026-03-20 
18:10:20.000000000 +0100
@@ -60,6 +60,8 @@
 #include "sequence/PingPong.h"
 #include "sequence/Superpose.h"
 
+#include "fx/Echo.h"
+
 #ifdef WITH_CONVOLUTION
 #include "fx/BinauralSound.h"
 #include "fx/ConvolverSound.h"
@@ -1685,6 +1687,53 @@
        }
 }
 
+PyDoc_STRVAR(M_aud_Sound_echo_doc, ".. method:: Echo(delay, feedback, mix)\n\n"
+                                                    "   Adds Echo effect to 
the sound.\n\n"
+                                                    "   :arg delay: The delay 
time in seconds.\n"
+                                                    "   :type delay: float\n"
+                                                    "   :arg feedback: The 
feedback amount (0.0 to 1.0).\n"
+                                                    "   :type feedback: 
float\n"
+                                                    "   :arg mix: The wet/dry 
mix (0.0 to 1.0).\n"
+                                                    "   :type mix: float\n"
+                                                    "   :arg reset_buffer: 
Whether to reset the delay buffer on seek.\n"
+                                                    "   :type reset_buffer: 
bool\n"
+                                                    "   :return: The created 
:class:`Sound` object.\n"
+                                                    "   :rtype: 
:class:`Sound`");
+static PyObject* Sound_echo(Sound* self, PyObject* args, PyObject* kwds)
+{
+       float delay = 0.5;
+       float feedback = 0.5;
+       float mix = 0.5;
+       bool reset_buffer = true;
+       static const char* kwlist[] = {"delay", "feedback", "mix", 
"reset_buffer", nullptr};
+
+       if(!PyArg_ParseTupleAndKeywords(args, kwds, "fff|b:echo", 
const_cast<char**>(kwlist), &delay, &feedback, &mix, &reset_buffer))
+       {
+               return nullptr;
+       }
+
+       PyTypeObject* type = Py_TYPE(self);
+       Sound* parent = (Sound*) type->tp_alloc(type, 0);
+
+       if(parent != nullptr)
+       {
+               try
+               {
+                       auto input = 
*reinterpret_cast<std::shared_ptr<ISound>*>(self->sound);
+                       auto echo = std::make_shared<Echo>(input, delay, 
feedback, mix, reset_buffer);
+                       parent->sound = new std::shared_ptr<ISound>(echo);
+               }
+               catch(Exception& e)
+               {
+                       Py_DECREF(parent);
+                       PyErr_SetString(AUDError, e.what());
+                       return nullptr;
+               }
+       }
+
+       return (PyObject*)parent;
+}
+
 #ifdef WITH_CONVOLUTION
 
 PyDoc_STRVAR(M_aud_Sound_convolver_doc,
@@ -2057,6 +2106,9 @@
        { "addSound", (PyCFunction)Sound_list_addSound, METH_O,
        M_aud_Sound_list_addSound_doc
        },
+       {"echo", (PyCFunction)Sound_echo, METH_VARARGS | METH_KEYWORDS,
+       M_aud_Sound_echo_doc},
+
 #ifdef WITH_CONVOLUTION
        { "convolver", (PyCFunction)Sound_convolver, METH_VARARGS,
        M_aud_Sound_convolver_doc
@@ -2119,10 +2171,16 @@
 };
 
 PyDoc_STRVAR(M_aud_Sound_doc,
-                        "Sound objects are immutable and represent a sound 
that can be "
+                        ".. class:: Sound(filename, stream=0)\n\n"
+                        "   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.");
+                        "playback.\n\n"
+                        "   :arg filename: Path of the file.\n"
+                        "   :type filename: string\n"
+                        "   :arg stream: The index of the audio stream within 
the file if it\n"
+                        "      contains multiple audio streams, 0 by 
default.\n"
+                        "   :type stream: int\n");
 
 PyTypeObject SoundType = {
        PyVarObject_HEAD_INIT(nullptr, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/python/PySource.cpp 
new/audaspace-1.9.0/bindings/python/PySource.cpp
--- old/audaspace-1.8.0/bindings/python/PySource.cpp    2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PySource.cpp    2026-03-20 
18:10:20.000000000 +0100
@@ -186,7 +186,14 @@
 };
 
 PyDoc_STRVAR(M_aud_Source_doc,
-       "The source object represents the source position of a binaural 
sound.");
+       ".. class:: Source(azimuth, elevation, distance, /)\n\n"
+       "   The source object represents the source position of a binaural 
sound.\n\n"
+       "   :arg azimuth: The azimuth angle in degrees.\n"
+       "   :type azimuth: float\n"
+       "   :arg elevation: The elevation angle in degrees.\n"
+       "   :type elevation: float\n"
+       "   :arg distance: The distance of the source.\n"
+       "   :type distance: float\n");
 
 PyTypeObject SourceType = {
        PyVarObject_HEAD_INIT(nullptr, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/bindings/python/PyThreadPool.cpp 
new/audaspace-1.9.0/bindings/python/PyThreadPool.cpp
--- old/audaspace-1.8.0/bindings/python/PyThreadPool.cpp        2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/bindings/python/PyThreadPool.cpp        2026-03-20 
18:10:20.000000000 +0100
@@ -60,7 +60,10 @@
 };
 
 PyDoc_STRVAR(M_aud_ThreadPool_doc,
-       "A ThreadPool is used to parallelize convolution efficiently.");
+       ".. class:: ThreadPool(nThreads, /)\n\n"
+       "   A ThreadPool is used to parallelize convolution efficiently.\n\n"
+       "   :arg nThreads: The number of threads in the pool.\n"
+       "   :type nThreads: int\n");
 
 PyTypeObject ThreadPoolType = {
        PyVarObject_HEAD_INIT(nullptr, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/include/fx/Echo.h 
new/audaspace-1.9.0/include/fx/Echo.h
--- old/audaspace-1.8.0/include/fx/Echo.h       1970-01-01 01:00:00.000000000 
+0100
+++ new/audaspace-1.9.0/include/fx/Echo.h       2026-03-20 18:10:20.000000000 
+0100
@@ -0,0 +1,27 @@
+#pragma once
+
+/**
+ * @file Echo.h
+ * @ingroup fx
+ * The Echo class.
+ */
+
+#include "fx/Effect.h"
+
+AUD_NAMESPACE_BEGIN
+
+class AUD_API Echo : public Effect
+{
+private:
+       float m_delay;      /* Delay time in seconds */
+       float m_feedback;   /* Feedback amount */
+       float m_mix;        /* Wet/dry mix */
+       bool m_resetBuffer; /* Whether to reset the delay buffer */
+
+public:
+       Echo(std::shared_ptr<ISound> sound, float delay, float feedback, float 
mix, bool resetBuffer = true);
+
+       virtual std::shared_ptr<IReader> createReader();
+};
+
+AUD_NAMESPACE_END
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/include/fx/EchoReader.h 
new/audaspace-1.9.0/include/fx/EchoReader.h
--- old/audaspace-1.8.0/include/fx/EchoReader.h 1970-01-01 01:00:00.000000000 
+0100
+++ new/audaspace-1.9.0/include/fx/EchoReader.h 2026-03-20 18:10:20.000000000 
+0100
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright 2009-2025 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ 
******************************************************************************/
+
+#pragma once
+
+/**
+ * @file EchoReader.h
+ * @ingroup fx
+ * The EchoReader class.
+ */
+
+#include <memory>
+
+#include "fx/EffectReader.h"
+#include "util/Buffer.h"
+
+AUD_NAMESPACE_BEGIN
+
+class AUD_API EchoReader : public EffectReader
+{
+private:
+       float m_delay;
+       float m_feedback;
+       float m_mix;
+       bool m_resetBuffer;
+
+       Buffer m_inBuffer;
+       Buffer m_delayBuffer;
+
+       int m_writePosition{0};
+       int m_samplesAvailable{0};
+
+public:
+       EchoReader(std::shared_ptr<IReader> reader, float delay, float 
feedback, float mix, bool resetBuffer = true);
+
+       virtual void read(int& length, bool& eos, sample_t* buffer) override;
+       virtual void seek(int position) override;
+};
+
+AUD_NAMESPACE_END
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/audaspace-1.8.0/plugins/coreaudio/CoreAudioDevice.cpp 
new/audaspace-1.9.0/plugins/coreaudio/CoreAudioDevice.cpp
--- old/audaspace-1.8.0/plugins/coreaudio/CoreAudioDevice.cpp   2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/plugins/coreaudio/CoreAudioDevice.cpp   2026-03-20 
18:10:20.000000000 +0100
@@ -157,7 +157,7 @@
 
        stream_basic_description.mSampleRate = m_specs.rate;
        stream_basic_description.mFormatID = kAudioFormatLinearPCM;
-       stream_basic_description.mFormatFlags |= kAudioFormatFlagsNativeEndian 
| kLinearPCMFormatFlagIsPacked;
+       stream_basic_description.mFormatFlags |= 
AudioFormatFlags(kAudioFormatFlagsNativeEndian) | 
AudioFormatFlags(kLinearPCMFormatFlagIsPacked);
        stream_basic_description.mBytesPerPacket = 
stream_basic_description.mBytesPerFrame = AUD_DEVICE_SAMPLE_SIZE(m_specs);
        stream_basic_description.mFramesPerPacket = 1;
        stream_basic_description.mChannelsPerFrame = m_specs.channels;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/plugins/openal/OpenALDevice.cpp 
new/audaspace-1.9.0/plugins/openal/OpenALDevice.cpp
--- old/audaspace-1.8.0/plugins/openal/OpenALDevice.cpp 2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/plugins/openal/OpenALDevice.cpp 2026-03-20 
18:10:20.000000000 +0100
@@ -931,7 +931,7 @@
 
                                        ALCint attribs[] = { ALC_FREQUENCY, 
(ALCint)specs.rate, 0 };
                                        ALCint* attributes = attribs;
-                                       if(specs.rate == RATE_INVALID)
+                                       if(specs.rate == double(RATE_INVALID))
                                                attributes = nullptr;
 
                                        m_context = alcCreateContext(m_device, 
attributes);
@@ -1152,7 +1152,7 @@
        // at least try to set the frequency
        ALCint attribs[] = { ALC_FREQUENCY, (ALCint)specs.rate, 0 };
        ALCint* attributes = attribs;
-       if(specs.rate == RATE_INVALID)
+       if(specs.rate == double(RATE_INVALID))
                attributes = nullptr;
 
        m_context = alcCreateContext(m_device, attributes);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/plugins/wasapi/WASAPIDevice.cpp 
new/audaspace-1.9.0/plugins/wasapi/WASAPIDevice.cpp
--- old/audaspace-1.8.0/plugins/wasapi/WASAPIDevice.cpp 2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/plugins/wasapi/WASAPIDevice.cpp 2026-03-20 
18:10:20.000000000 +0100
@@ -373,7 +373,7 @@
                specs.channels = CHANNELS_STEREO;
        if(specs.format == FORMAT_INVALID)
                specs.format = FORMAT_FLOAT32;
-       if(specs.rate == RATE_INVALID)
+       if(int(specs.rate) == RATE_INVALID)
                specs.rate = RATE_48000;
 
        if(FAILED(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, 
CLSCTX_ALL, IID_IMMDeviceEnumerator, 
reinterpret_cast<void**>(m_imm_device_enumerator.GetAddressOf()))))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/src/fx/Echo.cpp 
new/audaspace-1.9.0/src/fx/Echo.cpp
--- old/audaspace-1.8.0/src/fx/Echo.cpp 1970-01-01 01:00:00.000000000 +0100
+++ new/audaspace-1.9.0/src/fx/Echo.cpp 2026-03-20 18:10:20.000000000 +0100
@@ -0,0 +1,17 @@
+#include "fx/Echo.h"
+
+#include "fx/EchoReader.h"
+
+AUD_NAMESPACE_BEGIN
+
+Echo::Echo(std::shared_ptr<ISound> sound, float delay, float feedback, float 
mix, bool resetBuffer) :
+    Effect(sound), m_delay(delay), m_feedback(feedback), m_mix(mix), 
m_resetBuffer(resetBuffer)
+{
+}
+
+std::shared_ptr<IReader> Echo::createReader()
+{
+       return std::make_shared<EchoReader>(getReader(), m_delay, m_feedback, 
m_mix, m_resetBuffer);
+}
+
+AUD_NAMESPACE_END
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/src/fx/EchoReader.cpp 
new/audaspace-1.9.0/src/fx/EchoReader.cpp
--- old/audaspace-1.8.0/src/fx/EchoReader.cpp   1970-01-01 01:00:00.000000000 
+0100
+++ new/audaspace-1.9.0/src/fx/EchoReader.cpp   2026-03-20 18:10:20.000000000 
+0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright 2009-2025 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 "fx/EchoReader.h"
+
+#include <cstring>
+
+#include "IReader.h"
+
+#include "util/Buffer.h"
+
+AUD_NAMESPACE_BEGIN
+
+EchoReader::EchoReader(std::shared_ptr<IReader> reader, float delay, float 
feedback, float mix, bool resetBuffer) :
+    EffectReader(reader), m_delay(delay), m_feedback(feedback), m_mix(mix), 
m_resetBuffer(resetBuffer)
+{
+}
+
+void EchoReader::read(int& length, bool& eos, sample_t* buffer)
+{
+       auto specs = m_reader->getSpecs();
+       auto delaySamples = static_cast<int>(m_delay * specs.rate);
+
+       m_inBuffer.assureSize(length * AUD_SAMPLE_SIZE(specs));
+
+       // Note: should this ever do something another time than in the 
beginning,
+       // it will likely cause an audible glitch, as samples are not reordered
+       m_delayBuffer.assureSize(delaySamples * AUD_SAMPLE_SIZE(specs));
+
+       m_reader->read(length, eos, m_inBuffer.getBuffer());
+
+       sample_t* delayBuffer = m_delayBuffer.getBuffer();
+
+       for(int i = 0; i < length; i++)
+       {
+               for(int channel = 0; channel < specs.channels; channel++)
+               {
+                       int delayPosition = ((m_writePosition + i) % 
delaySamples) * specs.channels + channel;
+
+                       sample_t inSample = m_inBuffer.getBuffer()[i * 
specs.channels + channel];
+                       sample_t delayedSample = delayPosition < 
m_samplesAvailable * specs.channels ? delayBuffer[delayPosition] : 0;
+
+                       sample_t outSample = inSample + delayedSample * 
m_feedback;
+                       buffer[i * specs.channels + channel] = inSample * (1.0f 
- m_mix) + outSample * m_mix;
+
+                       // Update delay buffer with feedback
+                       delayBuffer[delayPosition] = outSample;
+               }
+       }
+
+       m_writePosition = (m_writePosition + length) % delaySamples;
+       m_samplesAvailable = std::min(delaySamples, m_samplesAvailable + 
length);
+}
+
+void EchoReader::seek(int position)
+{
+       m_reader->seek(position);
+
+       if(m_resetBuffer)
+       {
+               m_samplesAvailable = 0;
+               m_writePosition = 0;
+       }
+}
+
+AUD_NAMESPACE_END
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/audaspace-1.8.0/src/sequence/AnimateableProperty.cpp 
new/audaspace-1.9.0/src/sequence/AnimateableProperty.cpp
--- old/audaspace-1.8.0/src/sequence/AnimateableProperty.cpp    2025-11-13 
08:09:33.000000000 +0100
+++ new/audaspace-1.9.0/src/sequence/AnimateableProperty.cpp    2026-03-20 
18:10:20.000000000 +0100
@@ -223,8 +223,7 @@
                        m0 = (p2[i] - p0[i]) / 2.0f;
                        m1 = (p3[i] - p1[i]) / 2.0f;
 
-                       out[i] = (2 * t3 - 3 * t2 + 1) * p0[i] + (-2 * t3 + 3 * 
t2) * p1[i] +
-                                        (t3 - 2 * t2 + t) * m0 + (t3 - t2) * 
m1;
+                       out[i] = (2 * t3 - 3 * t2 + 1) * p1[i] + (-2 * t3 + 3 * 
t2) * p2[i] + (t3 - 2 * t2 + t) * m0 + (t3 - t2) * m1;
                }
        }
 }

Reply via email to