Audaspace: merging modulator effect from upstream.
authorJörg Müller <nexyon@gmail.com>
Wed, 3 Apr 2019 17:56:25 +0000 (19:56 +0200)
committerJörg Müller <nexyon@gmail.com>
Wed, 3 Apr 2019 17:56:25 +0000 (19:56 +0200)
extern/audaspace/CMakeLists.txt
extern/audaspace/bindings/C/AUD_Sound.cpp
extern/audaspace/bindings/C/AUD_Sound.h
extern/audaspace/bindings/python/PySound.cpp
extern/audaspace/include/fx/Modulator.h [new file with mode: 0644]
extern/audaspace/include/fx/ModulatorReader.h [new file with mode: 0644]
extern/audaspace/src/fx/Modulator.cpp [new file with mode: 0644]
extern/audaspace/src/fx/ModulatorReader.cpp [new file with mode: 0644]

index de701a1..c9f709b 100644 (file)
@@ -73,6 +73,8 @@ set(SRC
        src/fx/LoopReader.cpp
        src/fx/LowpassCalculator.cpp
        src/fx/Lowpass.cpp
+       src/fx/Modulator.cpp
+       src/fx/ModulatorReader.cpp
        src/fx/MutableReader.cpp
        src/fx/MutableSound.cpp
        src/fx/Pitch.cpp
@@ -181,6 +183,8 @@ set(PUBLIC_HDR
        include/fx/LoopReader.h
        include/fx/LowpassCalculator.h
        include/fx/Lowpass.h
+       include/fx/Modulator.h
+       include/fx/ModulatorReader.h
        include/fx/MutableReader.h
        include/fx/MutableSound.h
        include/fx/Pitch.h
index 1f40f5c..00a59f4 100644 (file)
@@ -32,6 +32,7 @@
 #include "fx/Limiter.h"
 #include "fx/Loop.h"
 #include "fx/Lowpass.h"
+#include "fx/Modulator.h"
 #include "fx/Pitch.h"
 #include "fx/Reverse.h"
 #include "fx/Sum.h"
@@ -465,6 +466,21 @@ AUD_API AUD_Sound* AUD_Sound_lowpass(AUD_Sound* sound, float frequency, float Q)
        }
 }
 
+AUD_API AUD_Sound* AUD_Sound_modulate(AUD_Sound* first, AUD_Sound* second)
+{
+       assert(first);
+       assert(second);
+
+       try
+       {
+               return new AUD_Sound(new Modulator(*first, *second));
+       }
+       catch(Exception&)
+       {
+               return nullptr;
+       }
+}
+
 AUD_API AUD_Sound* AUD_Sound_pitch(AUD_Sound* sound, float factor)
 {
        assert(sound);
index b18e3c3..66d6c53 100644 (file)
@@ -246,6 +246,14 @@ extern AUD_API AUD_Sound* AUD_Sound_loop(AUD_Sound* sound, int count);
  */
 extern AUD_API AUD_Sound* AUD_Sound_lowpass(AUD_Sound* sound, float frequency, float Q);
 
+/**
+ * Modulates two sound, which means multiplying the sound samples.
+ * \param first The first sound.
+ * \param second The second sound.
+ * \return A handle of the modulated sound.
+ */
+extern AUD_API AUD_Sound* AUD_Sound_modulate(AUD_Sound* first, AUD_Sound* second);
+
 /**
  * Changes the pitch of a sound.
  * \param sound The sound to change.
index 82c6036..17fcdbe 100644 (file)
@@ -42,6 +42,7 @@
 #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"
@@ -1129,6 +1130,47 @@ Sound_lowpass(Sound* self, PyObject* args)
        return (PyObject *)parent;
 }
 
+PyDoc_STRVAR(M_aud_Sound_modulate_doc,
+                        "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:: The two factories have to have the same specifications "
+                        "(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,
                         "pitch(factor)\n\n"
                         "Changes the pitch of a sound with a specific factor.\n\n"
@@ -1791,6 +1833,9 @@ static PyMethodDef Sound_methods[] = {
        {"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
        },
diff --git a/extern/audaspace/include/fx/Modulator.h b/extern/audaspace/include/fx/Modulator.h
new file mode 100644 (file)
index 0000000..db79d4a
--- /dev/null
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+/**
+ * @file Modulator.h
+ * @ingroup fx
+ * The Modulator class.
+ */
+
+#include "ISound.h"
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This sound plays two other factories, playing them the same time and modulating/multiplying them.
+ * \note Readers from the underlying factories must have the same sample rate
+ *       and channel count.
+ */
+class AUD_API Modulator : public ISound
+{
+private:
+       /**
+        * First played sound.
+        */
+       std::shared_ptr<ISound> m_sound1;
+
+       /**
+        * Second played sound.
+        */
+       std::shared_ptr<ISound> m_sound2;
+
+       // delete copy constructor and operator=
+       Modulator(const Modulator&) = delete;
+       Modulator& operator=(const Modulator&) = delete;
+
+public:
+       /**
+        * Creates a new modulator sound.
+        * \param sound1 The first input sound.
+        * \param sound2 The second input sound.
+        */
+       Modulator(std::shared_ptr<ISound> sound1, std::shared_ptr<ISound> sound2);
+
+       virtual std::shared_ptr<IReader> createReader();
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/include/fx/ModulatorReader.h b/extern/audaspace/include/fx/ModulatorReader.h
new file mode 100644 (file)
index 0000000..40cc843
--- /dev/null
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ******************************************************************************/
+
+#pragma once
+
+/**
+ * @file ModulatorReader.h
+ * @ingroup fx
+ * The ModulatorReader class.
+ */
+
+#include "IReader.h"
+#include "util/Buffer.h"
+
+#include <memory>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This reader plays two readers with the same specs in parallel multiplying their samples.
+ */
+class AUD_API ModulatorReader : public IReader
+{
+private:
+       /**
+        * The first reader.
+        */
+       std::shared_ptr<IReader> m_reader1;
+
+       /**
+        * The second reader.
+        */
+       std::shared_ptr<IReader> m_reader2;
+
+       /**
+        * Buffer used for mixing.
+        */
+       Buffer m_buffer;
+
+       // delete copy constructor and operator=
+       ModulatorReader(const ModulatorReader&) = delete;
+       ModulatorReader& operator=(const ModulatorReader&) = delete;
+
+public:
+       /**
+        * Creates a new modulator reader.
+        * \param reader1 The first reader to read from.
+        * \param reader2 The second reader to read from.
+        * \exception Exception Thrown if the specs from the readers differ.
+        */
+       ModulatorReader(std::shared_ptr<IReader> reader1, std::shared_ptr<IReader> reader2);
+
+       /**
+        * Destroys the reader.
+        */
+       virtual ~ModulatorReader();
+
+       virtual bool isSeekable() const;
+       virtual void seek(int position);
+       virtual int getLength() const;
+       virtual int getPosition() const;
+       virtual Specs getSpecs() const;
+       virtual void read(int& length, bool& eos, sample_t* buffer);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/fx/Modulator.cpp b/extern/audaspace/src/fx/Modulator.cpp
new file mode 100644 (file)
index 0000000..a1af1b8
--- /dev/null
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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 "fx/Modulator.h"
+#include "fx/ModulatorReader.h"
+
+AUD_NAMESPACE_BEGIN
+
+Modulator::Modulator(std::shared_ptr<ISound> sound1, std::shared_ptr<ISound> sound2) :
+       m_sound1(sound1), m_sound2(sound2)
+{
+}
+
+std::shared_ptr<IReader> Modulator::createReader()
+{
+       std::shared_ptr<IReader> reader1 = m_sound1->createReader();
+       std::shared_ptr<IReader> reader2 = m_sound2->createReader();
+
+       return std::shared_ptr<IReader>(new ModulatorReader(reader1, reader2));
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/fx/ModulatorReader.cpp b/extern/audaspace/src/fx/ModulatorReader.cpp
new file mode 100644 (file)
index 0000000..c17cee0
--- /dev/null
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * 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 "fx/ModulatorReader.h"
+#include "Exception.h"
+
+#include <algorithm>
+#include <cstring>
+
+AUD_NAMESPACE_BEGIN
+
+ModulatorReader::ModulatorReader(std::shared_ptr<IReader> reader1, std::shared_ptr<IReader> reader2) :
+    m_reader1(reader1), m_reader2(reader2)
+{
+}
+
+ModulatorReader::~ModulatorReader()
+{
+}
+
+bool ModulatorReader::isSeekable() const
+{
+       return m_reader1->isSeekable() && m_reader2->isSeekable();
+}
+
+void ModulatorReader::seek(int position)
+{
+       m_reader1->seek(position);
+       m_reader2->seek(position);
+}
+
+int ModulatorReader::getLength() const
+{
+       int len1 = m_reader1->getLength();
+       int len2 = m_reader2->getLength();
+       if((len1 < 0) || (len2 < 0))
+               return -1;
+       return std::min(len1, len2);
+}
+
+int ModulatorReader::getPosition() const
+{
+       int pos1 = m_reader1->getPosition();
+       int pos2 = m_reader2->getPosition();
+       return std::max(pos1, pos2);
+}
+
+Specs ModulatorReader::getSpecs() const
+{
+       return m_reader1->getSpecs();
+}
+
+void ModulatorReader::read(int& length, bool& eos, sample_t* buffer)
+{
+       Specs specs = m_reader1->getSpecs();
+       Specs s2 = m_reader2->getSpecs();
+       if(!AUD_COMPARE_SPECS(specs, s2))
+               AUD_THROW(StateException, "Two readers with different specifiactions cannot be modulated.");
+
+       int samplesize = AUD_SAMPLE_SIZE(specs);
+
+       m_buffer.assureSize(length * samplesize);
+
+       int len1 = length;
+       m_reader1->read(len1, eos, buffer);
+
+       if(len1 < length)
+               std::memset(buffer + len1 * specs.channels, 0, (length - len1) * samplesize);
+
+       int len2 = length;
+       bool eos2;
+       sample_t* buf = m_buffer.getBuffer();
+       m_reader2->read(len2, eos2, buf);
+
+       for(int i = 0; i < len2 * specs.channels; i++)
+               buffer[i] *= buf[i];
+
+       length = std::max(len1, len2);
+       eos &= eos2;
+}
+
+AUD_NAMESPACE_END