[multimedia/kwave] /: refactoring of the streaming engine

Thomas Eschenbacher <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit bc2fc81e7045fe8f5b802bb1ac1cfb494d71f86a by Thomas Eschenbacher.
Committed on 02/08/2026 at 16:30.
Pushed by eschenbacher into branch 'master'.

refactoring of the streaming engine

- use calls instead of signals and slots
- use worker threads for data processing

This shall speed up processing on multi core CPUs significantly and the bottleneck of signal/slot processing is no longer relevant. Future development should make more use of QFuture for data processing.

M  +3    -0    .gitignore
M  +0    -4    LICENSES
M  +2    -4    libgui/FilterPlugin.cpp
M  +0    -4    libkwave/CMakeLists.txt
M  +27   -43   libkwave/Connect.cpp
M  +10   -11   libkwave/Connect.h
M  +3    -14   libkwave/MimeData.cpp
M  +3    -18   libkwave/MultiPlaybackSink.cpp
M  +19   -8    libkwave/MultiPlaybackSink.h
M  +0    -2    libkwave/MultiStreamWriter.cpp
M  +0    -3    libkwave/MultiStreamWriter.h
M  +28   -2    libkwave/MultiTrackSink.h
M  +26   -2    libkwave/MultiTrackSource.h
D  +0    -43   libkwave/PlaybackSink.cpp
D  +0    -61   libkwave/PlaybackSink.h
M  +1    -1    libkwave/SampleReader.cpp
M  +0    -6    libkwave/SampleReader.h
M  +0    -1    libkwave/SampleSink.h
M  +3    -5    libkwave/StreamWriter.cpp
M  +2    -12   libkwave/StreamWriter.h
M  +1    -1    libkwave/TrackWriter.cpp
M  +2    -3    libkwave/TrackWriter.h
M  +2    -2    libkwave/Writer.cpp
M  +10   -10   libkwave/Writer.h
M  +15   -84   libkwave/modules/ChannelMixer.cpp
M  +104  -91   libkwave/modules/ChannelMixer.h
M  +1    -1    libkwave/modules/CurveStreamAdapter.cpp
M  +0    -6    libkwave/modules/CurveStreamAdapter.h
M  +2    -2    libkwave/modules/Delay.cpp
M  +4    -6    libkwave/modules/Delay.h
D  +0    -42   libkwave/modules/Indexer.cpp
D  +0    -67   libkwave/modules/Indexer.h
M  +4    -3    libkwave/modules/Mul.cpp
M  +58   -47   libkwave/modules/Mul.h
M  +1    -1    libkwave/modules/Osc.cpp
M  +45   -48   libkwave/modules/Osc.h
M  +3    -3    libkwave/modules/RateConverter.cpp
M  +3    -7    libkwave/modules/RateConverter.h
M  +2    -2    libkwave/modules/SampleBuffer.cpp
M  +6    -9    libkwave/modules/SampleBuffer.h
M  +52   -0    libkwave/modules/StreamObject.cpp
M  +101  -20   libkwave/modules/StreamObject.h
M  +4    -12   plugins/amplifyfree/AmplifyFreePlugin.cpp
M  +2    -2    plugins/band_pass/BandPass.cpp
M  +3    -8    plugins/band_pass/BandPass.h
M  +2    -7    plugins/codec_ogg/OpusDecoder.cpp
M  +3    -12   plugins/codec_ogg/OpusEncoder.cpp
M  +2    -2    plugins/lowpass/LowPassFilter.cpp
M  +3    -8    plugins/lowpass/LowPassFilter.h
M  +2    -2    plugins/noise/NoiseGenerator.cpp
M  +3    -8    plugins/noise/NoiseGenerator.h
M  +3    -9    plugins/normalize/NormalizePlugin.cpp
M  +2    -2    plugins/normalize/Normalizer.cpp
M  +3    -7    plugins/normalize/Normalizer.h
M  +2    -2    plugins/notch_filter/NotchFilter.cpp
M  +3    -7    plugins/notch_filter/NotchFilter.h
M  +2    -2    plugins/pitch_shift/PitchShiftFilter.cpp
M  +3    -7    plugins/pitch_shift/PitchShiftFilter.h
M  +3    -4    plugins/playback/PlayBack-Qt.h
M  +4    -13   plugins/playback/PlayBackPlugin.cpp
M  +3    -9    plugins/samplerate/SampleRatePlugin.cpp
M  +2    -6    plugins/volume/VolumePlugin.cpp

https://invent.kde.org/multimedia/kwave/-/commit/bc2fc81e7045fe8f5b802bb1ac1cfb494d71f86a

diff --git a/.gitignore b/.gitignore
index d16a653a..6fbca945 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,10 @@
 CMakeLists.txt.user*
 /compile_commands.json
 .clangd
+.cproject
 .idea
 /cmake-build*
 .cache
 .flatpak-builder
+.project
+.settings
diff --git a/LICENSES b/LICENSES
index 2c8d4fd0..87eb6ae8 100644
--- a/LICENSES
+++ b/LICENSES
@@ -373,8 +373,6 @@ COMPLETE LIST OF FILES AND THEIR LICENSE
         libkwave/PlaybackDeviceFactory.h                GPL2+
         libkwave/PlayBackDevice.h                       GPL2+
         libkwave/PlayBackParam.h                        GPL2+
-        libkwave/PlaybackSink.cpp                       GPL2+
-        libkwave/PlaybackSink.h                         GPL2+
         libkwave/PlayBackTypesMap.cpp                   GPL2+
         libkwave/PlayBackTypesMap.h                     GPL2+
         libkwave/Plugin.cpp                             GPL2+
@@ -442,8 +440,6 @@ COMPLETE LIST OF FILES AND THEIR LICENSE
         libkwave/modules/CurveStreamAdapter.h           GPL2+
         libkwave/modules/Delay.cpp                      GPL2+
         libkwave/modules/Delay.h                        GPL2+
-        libkwave/modules/Indexer.cpp                    GPL2+
-        libkwave/modules/Indexer.h                      GPL2+
         libkwave/modules/Mul.cpp                        GPL2+
         libkwave/modules/Mul.h                          GPL2+
         libkwave/modules/Osc.cpp                        GPL2+
diff --git a/libgui/FilterPlugin.cpp b/libgui/FilterPlugin.cpp
index 1d0f4875..c0c6de4d 100644
--- a/libgui/FilterPlugin.cpp
+++ b/libgui/FilterPlugin.cpp
@@ -151,10 +151,8 @@ void Kwave::FilterPlugin::run(QStringList params)
     updateFilter(filter, true);
 
     // connect them
-    Kwave::connect(source,  SIGNAL(output(Kwave::SampleArray)),
-                   *filter, SLOT(input(Kwave::SampleArray)));
-    Kwave::connect(*filter, SIGNAL(output(Kwave::SampleArray)),
-                   *m_sink, SLOT(input(Kwave::SampleArray)));
+    Kwave::connect(source,  *filter);
+    Kwave::connect(*filter, *m_sink);
 
     // transport the samples
     while (!shouldStop() && (!source.done() || m_listen)) {
diff --git a/libkwave/CMakeLists.txt b/libkwave/CMakeLists.txt
index d82dcb9e..9dbef503 100644
--- a/libkwave/CMakeLists.txt
+++ b/libkwave/CMakeLists.txt
@@ -58,7 +58,6 @@ SET(libkwave_LIB_SRCS
     MultiWriter.cpp
     Parser.cpp
     PlaybackController.cpp
-    PlaybackSink.cpp
     PlayBackTypesMap.cpp
     Plugin.cpp
     PluginManager.cpp
@@ -117,7 +116,6 @@ SET(libkwave_LIB_SRCS
     MultiWriter.h
     Parser.h
     PlaybackController.h
-    PlaybackSink.h
     PlayBackTypesMap.h
     Plugin.h
     PluginManager.h
@@ -145,7 +143,6 @@ SET(libkwave_LIB_SRCS
 
     modules/ChannelMixer.cpp
     modules/CurveStreamAdapter.cpp
-    modules/Indexer.cpp
     modules/Delay.cpp
     modules/Mul.cpp
     modules/Osc.cpp
@@ -155,7 +152,6 @@ SET(libkwave_LIB_SRCS
 
     modules/ChannelMixer.h
     modules/CurveStreamAdapter.h
-    modules/Indexer.h
     modules/Delay.h
     modules/Mul.h
     modules/Osc.h
diff --git a/libkwave/Connect.cpp b/libkwave/Connect.cpp
index a1d126df..e5394a65 100644
--- a/libkwave/Connect.cpp
+++ b/libkwave/Connect.cpp
@@ -15,6 +15,7 @@
  *                                                                         *
  ***************************************************************************/
 
+#include "Encoder.h"
 #include "config.h"
 
 #include <QObject>
@@ -27,57 +28,40 @@
 namespace Kwave {
 
     //***********************************************************************
-    static bool _connect_one_by_one(
-        Kwave::StreamObject &src, const char *output, unsigned int src_idx,
-        Kwave::StreamObject &dst, const char *input,  unsigned int dst_idx)
+    bool connect(Kwave::StreamObject &source,
+                 Kwave::StreamObject &sink,
+                 unsigned int port)
     {
-        Kwave::StreamObject *s = src.port(output, src_idx);
-        Kwave::StreamObject *d = dst.port(input,  dst_idx);
-        Q_ASSERT(s);
-        Q_ASSERT(d);
-        Q_ASSERT(input);
-        Q_ASSERT(output);
-        if (!s || !d || !input || !output) return false;
+        unsigned int src_tracks = source.tracksOut();
+        unsigned int dst_tracks = sink.tracksIn();
 
-        QObject::connect(s, output, d, input, Qt::DirectConnection);
+        // if (tracks == 0) -> use in() / out() instead
+        unsigned int outputs = (src_tracks != 0) ?
+                                src_tracks : source.outputs();
+        unsigned int inputs  = (dst_tracks != 0) ?
+                                dst_tracks : source.inputs();
 
-        QObject::connect(s, SIGNAL(sigCancel()), d, SLOT(cancel()),
-                         Qt::DirectConnection);
-
-        return true;
-    }
-
-    //***********************************************************************
-    bool connect(Kwave::StreamObject &source, const char *output,
-                 Kwave::StreamObject &sink,   const char *input)
-    {
-        unsigned int src_tracks = source.tracksOfPort(output);
-        unsigned int dst_tracks = sink.tracksOfPort(input);
-
-        Q_ASSERT(output);
-        Q_ASSERT(input);
-        if (!src_tracks || !dst_tracks || !output || !input)
-            return false;
-
-        if ((src_tracks == 1) && (dst_tracks > 1)) {
-            // 1 output -> N inputs
-            for (unsigned int track = 0; track < dst_tracks; track++) {
-                if (!_connect_one_by_one(
-                    source, output, 0,
-                    sink,   input,  track)) return false;
-            }
-        } else if (src_tracks == dst_tracks) {
+        if ((outputs == 1) && (inputs > 1)) {
+            // 1 output  -> N inputs
+        } else if (outputs == inputs) {
             // N outputs -> N inputs
-            for (unsigned int track=0; track < dst_tracks; track++) {
-                if (!_connect_one_by_one(
-                    source, output, track,
-                    sink,   input,  track)) return false;
-            }
         } else {
             qWarning("invalid source/sink combination, %u:%u tracks",
-                src_tracks, dst_tracks);
+                inputs, outputs);
             return false;
         }
+
+        for (unsigned int i = 0; i < inputs; i++) {
+            unsigned int src_idx = (outputs == inputs) ? i : 0;
+            unsigned int dst_idx = (dst_tracks != 0)   ? i : 0;
+            StreamObject *s = source.out(src_idx);
+            StreamObject *d = sink.in(dst_idx);
+            Q_ASSERT(s != nullptr);
+            Q_ASSERT(d != nullptr);
+            if ((s == nullptr) || (d == nullptr)) return false;
+            s->connectTo(d, (dst_tracks != 0) ? port : i);
+        }
+
         return true;
     }
 }
diff --git a/libkwave/Connect.h b/libkwave/Connect.h
index fa124868..2e47a64e 100644
--- a/libkwave/Connect.h
+++ b/libkwave/Connect.h
@@ -22,38 +22,37 @@
 #include "libkwave_export.h"
 
 #include <QtGlobal>
+#include <QSemaphore>
 
+#include "libkwave/SampleArray.h"
+#include "libkwave/modules/StreamObject.h"
 
 namespace Kwave
 {
-
     class StreamObject;
 
     /**
      * Connect an output of a Kwave::SampleSource to the input
      * of a Kwave::SampleSink. The following combinations of
-     * single-track and multi-track sources/sinks are allowed.:
+     * single-track and multi-track sources/sinks are allowed:
      * \li single -> single (1:1)
      * \li single -> multi  (1:N)
      * \li multi  -> multi  (N:N)
      *
      * @param source a Kwave::SampleSource that produces data
-     * @param output name of the output, must be formed with the
-     *               Qt macro \c SIGNAL(...) \c out of
-     *               a Qt style signal.
      * @param sink a Kwave::SampleSink that can receive data
-     * @param input name of the inputm must be formed with the
-     *              Qt macro \c SLOT(...) \c out of a Qt style
-     *              (public) slot .
+     * @param input index of the input (optional, default is 0)
      * @return true if successful or false if either
      *         \li an invalid combination of single/multi track
      *             source/sink has been passed
      *         \li a source or sink's track is NULL (missing)
-     *         \li input or output name is zero-length
+     *         \li sink index is invalid
      */
-    bool connect(Kwave::StreamObject &source, const char *output,
-                 Kwave::StreamObject &sink,   const char *input)
+    bool connect(StreamObject &source,
+                 StreamObject &sink,
+                 unsigned int port = 0)
                  LIBKWAVE_EXPORT;
+
 }
 
 #endif /* CONNECT_H */
diff --git a/libkwave/MimeData.cpp b/libkwave/MimeData.cpp
index 0bc34abc..55f719c7 100644
--- a/libkwave/MimeData.cpp
+++ b/libkwave/MimeData.cpp
@@ -340,29 +340,18 @@ sample_index_t Kwave::MimeData::decode(QWidget *widget, const QMimeData *e,
 
             if (mixer) {
                 // connect the channel mixer
-                ok = Kwave::connect(
-                    *last_output, SIGNAL(output(Kwave::SampleArray)),
-                    *mixer,       SLOT(input(Kwave::SampleArray))
-                );
+                ok = Kwave::connect(*last_output, *mixer);
                 last_output = mixer;
             }
 
             if (ok && rate_converter) {
                 // connect the rate converter
-                ok = Kwave::connect(
-                    *last_output,    SIGNAL(output(Kwave::SampleArray)),
-                    *rate_converter, SLOT(input(Kwave::SampleArray))
-                );
+                ok = Kwave::connect(*last_output, *rate_converter);
                 last_output = rate_converter;
             }
 
             // connect the sink
-            if (ok) {
-                ok = Kwave::connect(
-                    *last_output, SIGNAL(output(Kwave::SampleArray)),
-                    dst,          SLOT(input(Kwave::SampleArray))
-                );
-            }
+            if (ok) ok = Kwave::connect(*last_output, dst);
 
             // this also starts the conversion automatically
             if (ok)
diff --git a/libkwave/MultiPlaybackSink.cpp b/libkwave/MultiPlaybackSink.cpp
index c515c2c5..ec2b4929 100644
--- a/libkwave/MultiPlaybackSink.cpp
+++ b/libkwave/MultiPlaybackSink.cpp
@@ -23,38 +23,25 @@
 #include <QtGlobal>
 #include <QThread>
 
+#include "libkwave/Connect.h"
 #include "libkwave/MultiPlaybackSink.h"
 #include "libkwave/PlayBackDevice.h"
-#include "libkwave/PlaybackSink.h"
 
 //***************************************************************************
 Kwave::MultiPlaybackSink::MultiPlaybackSink(unsigned int tracks,
                                             Kwave::PlayBackDevice *device)
-    :Kwave::MultiTrackSink<Kwave::PlaybackSink, false>(0),
+    :Kwave::SampleSink(),
      m_tracks(tracks), m_device(device), m_in_buffer(tracks),
      m_in_buffer_filled(tracks),
      m_out_buffer(tracks), m_lock()
 {
     m_in_buffer.fill(Kwave::SampleArray(0));
     m_in_buffer_filled.fill(false);
-
-    for (unsigned int track = 0; track < m_tracks; track++) {
-        // allocate a sink
-        Kwave::PlaybackSink *sink =
-            new(std::nothrow) Kwave::PlaybackSink(track);
-        insert(track, sink);
-        connect(sink, SIGNAL(output(uint,Kwave::SampleArray)),
-                this, SLOT(input(uint,Kwave::SampleArray)),
-                Qt::DirectConnection);
-    }
 }
 
 //***************************************************************************
 Kwave::MultiPlaybackSink::~MultiPlaybackSink()
 {
-    // close all stream objects
-    clear();
-
     // close the device
     if (m_device) {
         m_device->close();
@@ -70,7 +57,7 @@ Kwave::MultiPlaybackSink::~MultiPlaybackSink()
 
 //***************************************************************************
 void Kwave::MultiPlaybackSink::input(unsigned int track,
-                                     Kwave::SampleArray data)
+                                     Kwave::SampleArray &data)
 {
     QMutexLocker lock(&m_lock);
 
@@ -121,5 +108,3 @@ void Kwave::MultiPlaybackSink::input(unsigned int track,
 
 //***************************************************************************
 //***************************************************************************
-
-#include "moc_MultiPlaybackSink.cpp"
diff --git a/libkwave/MultiPlaybackSink.h b/libkwave/MultiPlaybackSink.h
index 7bd8b9e3..43559899 100644
--- a/libkwave/MultiPlaybackSink.h
+++ b/libkwave/MultiPlaybackSink.h
@@ -28,16 +28,14 @@
 
 #include "libkwave/MultiTrackSink.h"
 #include "libkwave/PlayBackDevice.h"
-#include "libkwave/PlaybackSink.h"
 #include "libkwave/SampleArray.h"
 
 namespace Kwave
 {
 
-    class LIBKWAVE_EXPORT MultiPlaybackSink
-        :public Kwave::MultiTrackSink<Kwave::PlaybackSink, false>
+    class LIBKWAVE_EXPORT MultiPlaybackSink : public Kwave::SampleSink
     {
-        Q_OBJECT
+        using StreamObject::input;
     public:
         /**
          * Constructor
@@ -49,14 +47,27 @@ namespace Kwave
         /** Destructor */
         ~MultiPlaybackSink() override;
 
-    private slots:
-
         /**
          * receives data from one of the tracks
-         * @param track index of the track [0...tracks-1]
+         * @param track index of the track [0 ... tracks - 1]
          * @param data sample data for the given track
          */
-        void input(unsigned int track, Kwave::SampleArray data);
+        void input(unsigned int track, Kwave::SampleArray &data) override;
+
+        /**
+         * always return 0 -> use "multi port mode"
+         * @return 0 always
+         */
+        unsigned int tracks() const override { return 0; }
+
+        /**
+         * Returns the number of inputs.
+         * @return number of inputs
+         */
+        unsigned int inputs() const override
+        {
+            return m_tracks;
+        }
 
     private:
 
diff --git a/libkwave/MultiStreamWriter.cpp b/libkwave/MultiStreamWriter.cpp
index 7400e621..3a5ac116 100644
--- a/libkwave/MultiStreamWriter.cpp
+++ b/libkwave/MultiStreamWriter.cpp
@@ -49,5 +49,3 @@ Kwave::MultiStreamWriter::~MultiStreamWriter()
 
 //***************************************************************************
 //***************************************************************************
-
-#include "moc_MultiStreamWriter.cpp"
diff --git a/libkwave/MultiStreamWriter.h b/libkwave/MultiStreamWriter.h
index 5853ee19..fd9e680b 100644
--- a/libkwave/MultiStreamWriter.h
+++ b/libkwave/MultiStreamWriter.h
@@ -24,7 +24,6 @@
 
 #include <QtGlobal>
 #include <QList>
-#include <QObject>
 
 #include "libkwave/MultiWriter.h"
 
@@ -37,8 +36,6 @@ namespace Kwave
      */
     class LIBKWAVE_EXPORT MultiStreamWriter: public Kwave::MultiWriter
     {
-        Q_OBJECT
-
     public:
 
         /** Default constructor */
diff --git a/libkwave/MultiTrackSink.h b/libkwave/MultiTrackSink.h
index 38f8a307..8fce3899 100644
--- a/libkwave/MultiTrackSink.h
+++ b/libkwave/MultiTrackSink.h
@@ -26,6 +26,7 @@
 #include <QObject>
 
 #include "libkwave/SampleSink.h"
+#include "modules/StreamObject.h"
 
 namespace Kwave
 {
@@ -78,15 +79,40 @@ namespace Kwave
          * if the object has multiple tracks. For single-track objects
          * it returns "this" for the first index and 0 for all others
          */
-        inline virtual SINK *at(unsigned int track) const {
+        inline SINK *at(unsigned int track) const {
             return QList<SINK *>::at(track);
         }
 
         /** @see the Kwave::MultiTrackSink.at()... */
-        inline virtual SINK * operator [] (unsigned int track) override {
+        inline SINK * operator [] (unsigned int track) override {
             return at(track);
         }
 
+        /**
+         * Returns the sink that corresponds to one specific track
+         * if the object has multiple tracks. For single-track objects
+         * it returns "this" for the first index and 0 for all others
+         *
+         * @see the Kwave::MultiTrackSink.at()...
+         * @param track index of the track
+         * @return a stream object or nullptr
+         */
+        Kwave::StreamObject *in(unsigned int track) override
+        {
+            return at(track);
+        }
+
+        /**
+         * Returns the same as in (for convenience)
+         *
+         * @param track index of the track
+         * @return a stream object or nullptr
+         */
+        Kwave::StreamObject *out(unsigned int track) override
+        {
+            return in(track);
+        }
+
         /**
          * Insert a new track with a sink.
          *
diff --git a/libkwave/MultiTrackSource.h b/libkwave/MultiTrackSource.h
index 079c5f34..03122d90 100644
--- a/libkwave/MultiTrackSource.h
+++ b/libkwave/MultiTrackSource.h
@@ -29,6 +29,7 @@
 #include <QtConcurrentRun>
 
 #include "libkwave/SampleSource.h"
+#include "modules/StreamObject.h"
 
 namespace Kwave
 {
@@ -113,8 +114,31 @@ namespace Kwave
         }
 
         /** @see the Kwave::MultiTrackSource.at()... */
-        inline virtual SOURCE * operator [] (unsigned int track)
-            override
+        inline SOURCE * operator [] (unsigned int track) override {
+            return at(track);
+        }
+
+        /**
+         * Returns the same as in (for convenience)
+         *
+         * @param track index of the track
+         * @return a stream object or nullptr
+         */
+        Kwave::StreamObject *in(unsigned int track) override
+        {
+            return out(track);
+        }
+
+        /**
+         * Returns the source that corresponds to one specific track
+         * if the object has multiple tracks. For single-track objects
+         * it returns "this" for the first index and 0 for all others
+         *
+         * @see the Kwave::MultiTrackSink.at()...
+         * @param track index of the track
+         * @return a stream object or nullptr
+         */
+        Kwave::StreamObject *out(unsigned int track) override
         {
             return at(track);
         }
diff --git a/libkwave/PlaybackSink.cpp b/libkwave/PlaybackSink.cpp
deleted file mode 100644
index 3cb83f1c..00000000
--- a/libkwave/PlaybackSink.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/***************************************************************************
-       PlaybackSink.cpp  -  multi-track Kwave compatible sink for playback
-                             -------------------
-    begin                : Sun Nov 04 2007
-    copyright            : (C) 2007 by Thomas Eschenbacher
-    email                : [email protected]
- ***************************************************************************/
-
-/***************************************************************************
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- ***************************************************************************/
-
-#include "config.h"
-
-#include "libkwave/PlaybackSink.h"
-#include "libkwave/SampleArray.h"
-
-//***************************************************************************
-Kwave::PlaybackSink::PlaybackSink(unsigned int track)
-    :Kwave::SampleSink(), m_track(track)
-{
-}
-
-//***************************************************************************
-Kwave::PlaybackSink::~PlaybackSink()
-{
-}
-
-//***************************************************************************
-void Kwave::PlaybackSink::input(Kwave::SampleArray data)
-{
-    emit output(m_track, data);
-}
-
-//***************************************************************************
-//***************************************************************************
-
-#include "moc_PlaybackSink.cpp"
diff --git a/libkwave/PlaybackSink.h b/libkwave/PlaybackSink.h
deleted file mode 100644
index b6f1556c..00000000
--- a/libkwave/PlaybackSink.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/***************************************************************************
-         PlaybackSink.h  -  multi-track Kwave compatible sink for playback
-                             -------------------
-    begin                : Sun Nov 04 2007
-    copyright            : (C) 2007 by Thomas Eschenbacher
-    email                : [email protected]
- ***************************************************************************/
-
-/***************************************************************************
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- ***************************************************************************/
-
-#ifndef PLAYBACK_SINK_H
-#define PLAYBACK_SINK_H
-
-#include "config.h"
-
-#include <QObject>
-
-#include "libkwave/SampleArray.h"
-#include "libkwave/SampleSink.h"
-
-namespace Kwave
-{
-
-    class PlaybackSink: public Kwave::SampleSink
-    {
-        Q_OBJECT
-    public:
-        /**
-         * Constructor
-         * @param track index of this playback channel
-         */
-        explicit PlaybackSink(unsigned int track);
-
-        /** Destructor */
-        ~PlaybackSink() override;
-
-    signals:
-        /** emits back the sample data received through input(...) */
-        void output(unsigned int track, Kwave::SampleArray data);
-
-    public slots:
-        /** receives sample data for this playback channel */
-        void input(Kwave::SampleArray data);
-
-    private:
-        /** index of the track of this playback channel */
-        unsigned int m_track;
-    };
-}
-
-#endif /* PLAYBACK_SINK_H */
-
-//***************************************************************************
-//***************************************************************************
diff --git a/libkwave/SampleReader.cpp b/libkwave/SampleReader.cpp
index 16a4583e..1df34fb3 100644
--- a/libkwave/SampleReader.cpp
+++ b/libkwave/SampleReader.cpp
@@ -293,7 +293,7 @@ void Kwave::SampleReader::goOn()
 {
     Kwave::SampleArray buffer(blockSize());
     (*this) >> buffer;
-    emit output(buffer);
+    output(buffer);
 }
 
 //***************************************************************************
diff --git a/libkwave/SampleReader.h b/libkwave/SampleReader.h
index 4c055fc1..b322e011 100644
--- a/libkwave/SampleReader.h
+++ b/libkwave/SampleReader.h
@@ -142,12 +142,6 @@ namespace Kwave
          */
         void proceeded();
 
-        /**
-         * Interface for the signal/slot based streaming API.
-         * @param data sample data that has been read
-         */
-        void output(Kwave::SampleArray data);
-
     protected:
 
         /** Fills the sample buffer */
diff --git a/libkwave/SampleSink.h b/libkwave/SampleSink.h
index 21ce18c1..217e406e 100644
--- a/libkwave/SampleSink.h
+++ b/libkwave/SampleSink.h
@@ -29,7 +29,6 @@
 
 namespace Kwave
 {
-
     class LIBKWAVE_EXPORT SampleSink: public Kwave::StreamObject
     {
         Q_OBJECT
diff --git a/libkwave/StreamWriter.cpp b/libkwave/StreamWriter.cpp
index 3a710115..5c1ba1bc 100644
--- a/libkwave/StreamWriter.cpp
+++ b/libkwave/StreamWriter.cpp
@@ -39,7 +39,7 @@ Kwave::StreamWriter::~StreamWriter()
 }
 
 //***************************************************************************
-bool Kwave::StreamWriter::write(const Kwave::SampleArray &buffer,
+bool Kwave::StreamWriter::write(Kwave::SampleArray &buffer,
                                 unsigned int &count)
 {
     // NOTE: even zero length input has to be passed, needed for flushing!
@@ -51,10 +51,10 @@ bool Kwave::StreamWriter::write(const Kwave::SampleArray &buffer,
             return false; // out-of-memory ?
 
         // emit the resized copy
-        emit output(data);
+        output(data);
     } else {
         // directly emit the buffer - fast :-)
-        emit output(buffer);
+        output(buffer);
     }
 
     count = 0;
@@ -63,5 +63,3 @@ bool Kwave::StreamWriter::write(const Kwave::SampleArray &buffer,
 
 //***************************************************************************
 //***************************************************************************
-
-#include "moc_StreamWriter.cpp"
diff --git a/libkwave/StreamWriter.h b/libkwave/StreamWriter.h
index ec399d1e..d9b5b42a 100644
--- a/libkwave/StreamWriter.h
+++ b/libkwave/StreamWriter.h
@@ -23,7 +23,6 @@
 #include "libkwave_export.h"
 
 #include <QtGlobal>
-#include <QObject>
 
 #include "libkwave/Writer.h"
 
@@ -36,13 +35,9 @@ namespace Kwave
     /**
      * @class StreamWriter
      * Input stream for transferring samples into a Track.
-     *
-     * @warning THIS CLASS IS NOT THREADSAFE! It is intended to be owned by
-     *          and used from only one thread.
      */
     class LIBKWAVE_EXPORT StreamWriter: public Kwave::Writer
     {
-        Q_OBJECT
     public:
 
         /**
@@ -66,13 +61,8 @@ namespace Kwave
          *              will be internally set to zero if successful
          * @return true if successful, false if failed (e.g. out of memory)
          */
-        virtual bool write(const Kwave::SampleArray &buffer,
-                            unsigned int &count) override;
-
-    signals:
-
-        /** emits a block with sine wave data */
-        void output(Kwave::SampleArray data);
+        bool write(Kwave::SampleArray &buffer,
+                   unsigned int &count) override;
 
     };
 
diff --git a/libkwave/TrackWriter.cpp b/libkwave/TrackWriter.cpp
index 071d1e9b..62cfea08 100644
--- a/libkwave/TrackWriter.cpp
+++ b/libkwave/TrackWriter.cpp
@@ -45,7 +45,7 @@ Kwave::TrackWriter::~TrackWriter()
 }
 
 //***************************************************************************
-bool Kwave::TrackWriter::write(const Kwave::SampleArray &buffer,
+bool Kwave::TrackWriter::write(Kwave::SampleArray &buffer,
                                unsigned int &count)
 {
     if (count == 0) return true; // nothing to write
diff --git a/libkwave/TrackWriter.h b/libkwave/TrackWriter.h
index dd84a5d9..8884a6c8 100644
--- a/libkwave/TrackWriter.h
+++ b/libkwave/TrackWriter.h
@@ -73,9 +73,8 @@ namespace Kwave
          *              will be internally set to zero if successful
          * @return true if successful, false if failed (e.g. out of memory)
          */
-        virtual bool write(const Kwave::SampleArray &buffer,
-                           unsigned int &count)
-            override;
+        virtual bool write(Kwave::SampleArray &buffer,
+                           unsigned int &count) override;
 
     private:
 
diff --git a/libkwave/Writer.cpp b/libkwave/Writer.cpp
index 7a365ef5..e735bd73 100644
--- a/libkwave/Writer.cpp
+++ b/libkwave/Writer.cpp
@@ -65,7 +65,7 @@ Kwave::Writer::~Writer()
 }
 
 //***************************************************************************
-Kwave::Writer &Kwave::Writer::operator << (const Kwave::SampleArray &samples)
+Kwave::Writer &Kwave::Writer::operator << (Kwave::SampleArray &samples)
 {
     unsigned int count = samples.size();
 
@@ -149,7 +149,7 @@ Kwave::Writer &flush(Kwave::Writer &s)
 }
 
 //***************************************************************************
-void Kwave::Writer::input(Kwave::SampleArray data)
+void Kwave::Writer::input(Kwave::SampleArray &data)
 {
     if (data.size()) (*this) << data;
 }
diff --git a/libkwave/Writer.h b/libkwave/Writer.h
index 883f4e4e..e8592431 100644
--- a/libkwave/Writer.h
+++ b/libkwave/Writer.h
@@ -27,6 +27,7 @@
 
 #include "libkwave/InsertMode.h"
 #include "libkwave/SampleSink.h"
+#include "modules/StreamObject.h"
 
 namespace Kwave
 {
@@ -36,6 +37,7 @@ namespace Kwave
     class LIBKWAVE_EXPORT Writer: public Kwave::SampleSink
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /** default constructor */
@@ -57,7 +59,7 @@ namespace Kwave
         ~Writer() override;
 
         /** operator for inserting an array of samples */
-        virtual Writer &operator << (const Kwave::SampleArray &samples);
+        virtual Writer &operator << (Kwave::SampleArray &samples);
 
         /** operator for inserting a single sample */
         virtual Writer &operator << (const sample_t &sample);
@@ -85,7 +87,7 @@ namespace Kwave
          *              will be internally set to zero if successful
          * @return true if successful, false if failed (e.g. out of memory)
          */
-        virtual bool write(const Kwave::SampleArray &buffer,
+        virtual bool write(Kwave::SampleArray &buffer,
                            unsigned int &count) = 0;
 
         /**
@@ -125,6 +127,12 @@ namespace Kwave
         /** Returns the insert mode */
         inline Kwave::InsertMode mode() const { return m_mode; }
 
+        /**
+         * Interface for the streaming API.
+         * @param data sample data to write
+         */
+        void input(Kwave::SampleArray &data) override;
+
     signals:
 
         /**
@@ -138,14 +146,6 @@ namespace Kwave
          */
         void proceeded();
 
-    public slots:
-
-        /**
-         * Interface for the signal/slot based streaming API.
-         * @param data sample data to write
-         */
-        void input(Kwave::SampleArray data);
-
     protected:
 
         /** first sample */
diff --git a/libkwave/modules/ChannelMixer.cpp b/libkwave/modules/ChannelMixer.cpp
index 4fa7a2c5..71ee94fd 100644
--- a/libkwave/modules/ChannelMixer.cpp
+++ b/libkwave/modules/ChannelMixer.cpp
@@ -24,14 +24,12 @@
 #include <QByteArray>
 #include <QMetaObject>
 #include <QMutexLocker>
-#include <QObject>
 #include <QVarLengthArray>
 
 #include "libkwave/MixerMatrix.h"
 #include "libkwave/Sample.h"
 #include "libkwave/Utils.h"
 #include "libkwave/modules/ChannelMixer.h"
-#include "libkwave/modules/Indexer.h"
 #include "libkwave/modules/StreamObject.h"
 
 //***************************************************************************
@@ -40,7 +38,6 @@ Kwave::ChannelMixer::ChannelMixer(unsigned int inputs, unsigned int outputs)
      m_matrix(nullptr),
      m_inputs(inputs),
      m_outputs(outputs),
-     m_indexer(),
      m_input_queue(),
      m_output_buffer(),
      m_lock()
@@ -67,21 +64,6 @@ bool Kwave::ChannelMixer::init()
         m_output_buffer.append(out_buffer);
     }
 
-    // create indexing proxies and connect their output to this mixer
-    for (unsigned int index = 0; index < m_inputs; ++index) {
-        Kwave::StreamObject *indexer =
-            new(std::nothrow) Kwave::Indexer(index);
-        Q_ASSERT(indexer);
-        if (!indexer) return false;
-
-        m_indexer.append(indexer);
-        bool ok = Kwave::connect(
-            *indexer, SIGNAL(output(uint,Kwave::SampleArray)),
-            *this,    SLOT(idxInput(uint,Kwave::SampleArray)));
-        Q_ASSERT(ok);
-        if (!ok) return false;
-    }
-
     // create the mixer matrix
     // create a translation matrix for mixing up/down to the desired
     // number of output channels
@@ -98,11 +80,6 @@ Kwave::ChannelMixer::~ChannelMixer()
 {
     QMutexLocker _lock(&m_lock);
 
-    while (!m_indexer.isEmpty()) {
-        delete m_indexer[0];
-        m_indexer.remove(0);
-    }
-
     m_input_queue.clear();
 
     while (!m_output_buffer.isEmpty()) {
@@ -112,69 +89,14 @@ Kwave::ChannelMixer::~ChannelMixer()
 }
 
 //***************************************************************************
-static inline QByteArray _sig(const char *sig)
-{
-    return QMetaObject::normalizedSignature(sig);
-}
-
-//***************************************************************************
-unsigned int Kwave::ChannelMixer::tracksOfPort(const char *port) const
-{
-    unsigned int retval = 0;
-    QMutexLocker _lock(const_cast<QMutex *>(&m_lock));
-
-    if (_sig(port) == _sig(SLOT(input(Kwave::SampleArray)))) {
-        // input ports
-        retval = m_inputs; // init is done
-    } else if (_sig(port) == _sig(SIGNAL(output(Kwave::SampleArray)))) {
-        // output ports
-        retval = m_outputs;
-    } else if (_sig(port) ==
-               _sig(SLOT(idxInput(uint,Kwave::SampleArray)))) {
-        retval = 1;
-    } else {
-        qFatal("unknown port");
-    }
-
-    return retval;
-}
-
-//***************************************************************************
-Kwave::StreamObject *Kwave::ChannelMixer::port(const char *port,
-                                               unsigned int track)
-{
-    Kwave::StreamObject *retval = nullptr;
-    QMutexLocker _lock(&m_lock);
-
-    if (_sig(port) == _sig(SLOT(input(Kwave::SampleArray)))) {
-        // input proxy
-        Q_ASSERT(Kwave::toInt(track) < m_indexer.count());
-        if (Kwave::toInt(track) >= m_indexer.count()) return nullptr;
-        retval = m_indexer.at(track);
-    } else if (_sig(port) == _sig(SIGNAL(output(Kwave::SampleArray)))) {
-        // output proxy
-        Q_ASSERT(Kwave::toInt(track) < m_output_buffer.count());
-        if (Kwave::toInt(track) >= m_output_buffer.count()) return nullptr;
-        retval = m_output_buffer[track];
-    } else if (_sig(port) ==
-               _sig(SLOT(idxInput(uint,Kwave::SampleArray)))) {
-        retval = this;
-    } else {
-        qFatal("unknown port");
-    }
-
-    return retval;
-}
-
-//***************************************************************************
-void Kwave::ChannelMixer::idxInput(unsigned int index, Kwave::SampleArray data)
+void Kwave::ChannelMixer::input(unsigned int index, Kwave::SampleArray &data)
 {
     QMutexLocker _lock(&m_lock);
 
     // put the data into the corresponding input queue
-    Q_ASSERT(index < m_inputs);
-    Q_ASSERT(Kwave::toInt(index) < m_input_queue.count());
-    if (Kwave::toInt(index) < m_input_queue.count())
+    Q_ASSERT(index < Kwave::toUint(m_inputs));
+    Q_ASSERT(index < Kwave::toUint(m_input_queue.count()));
+    if (index < m_input_queue.count())
         m_input_queue[index].enqueue(data);
 
     // check: if there is one empty queue we are not yet ready for mixing
@@ -190,6 +112,17 @@ void Kwave::ChannelMixer::idxInput(unsigned int index, Kwave::SampleArray data)
     if (ready && m_matrix) mix();
 }
 
+//***************************************************************************
+Kwave::StreamObject *Kwave::ChannelMixer::out(unsigned int track)
+{
+    Q_ASSERT(track < Kwave::toUint(m_outputs));
+    Q_ASSERT(track < Kwave::toUint(m_output_buffer.count()));
+    if (track >= m_output_buffer.count()) return nullptr;
+
+    Kwave::SampleBuffer *buffer = m_output_buffer[track];
+    return buffer;
+}
+
 //***************************************************************************
 void Kwave::ChannelMixer::mix()
 {
@@ -262,5 +195,3 @@ void Kwave::ChannelMixer::mix()
 
 //***************************************************************************
 //***************************************************************************
-
-#include "moc_ChannelMixer.cpp"
diff --git a/libkwave/modules/ChannelMixer.h b/libkwave/modules/ChannelMixer.h
index e5dc15a7..90fa520e 100644
--- a/libkwave/modules/ChannelMixer.h
+++ b/libkwave/modules/ChannelMixer.h
@@ -21,14 +21,12 @@
 #include "config.h"
 #include "libkwave_export.h"
 
-#include <QObject>
 #include <QPointer>
 #include <QQueue>
 #include <QString>
 #include <QVector>
 
 #include "libkwave/Connect.h"
-#include "libkwave/SampleSink.h"
 #include "libkwave/SampleSource.h"
 #include "libkwave/modules/SampleBuffer.h"
 #include "libkwave/modules/StreamObject.h"
@@ -36,102 +34,117 @@
 //***************************************************************************
 namespace Kwave
 {
-
     class MixerMatrix;
 
     class LIBKWAVE_EXPORT ChannelMixer: public Kwave::SampleSource
     {
         Q_OBJECT
-        public:
-            /**
-             * Constructor
-             * @param inputs number of input channels
-             * @param outputs number of output channels
-             */
-            ChannelMixer(unsigned int inputs, unsigned int outputs);
-
-            /** Destructor */
-            ~ChannelMixer() override;
-
-            /**
-             * Init function, you must call it once after creating and before
-             * using this object. If the return value is false, you should
-             * delete this object.
-             * @return true if succeeded, false if failed
-             */
-            virtual bool init();
-
-            /**
-             * Returns the number of tracks of a input or output port.
-             * Can be overwritten for objects that have a different count
-             * of inputs and outputs.
-             * @param port name of the port (name of signal or slot)
-             * @return number of tracks of a input or output, default is
-             *         the same as tracks()
-             */
-            virtual unsigned int tracksOfPort(const char *port) const
-                override;
-
-            /**
-             * Returns an indexed port, identified by name
-             * @param port name of the port (name of signal or slot)
-             * @param track index of the track
-             */
-            virtual Kwave::StreamObject *port(
-                const char *port, unsigned int track) override;
-
-            /** does nothing, work is done automatically in mix() */
-            void goOn() override
-            {
-            }
-
-        signals:
-
-            /** emits a block with output data */
-            void output(Kwave::SampleArray data);
-
-        public slots:
-
-            /**
-             * dummy implementation, the real "input" is a multi-track slot
-             * and available through the port(...) interface only
-             */
-            void input(Kwave::SampleArray data) { Q_UNUSED(data) }
-
-        private slots:
-
-            /** receives a block with index + input data */
-            void idxInput(unsigned int index, Kwave::SampleArray data);
-
-        private:
-
-            /** does the calculation */
-            virtual void mix();
-
-        private:
-
-            /** mixer matrix */
-            Kwave::MixerMatrix *m_matrix;
-
-            /** number of inputs */
-            unsigned int m_inputs;
-
-            /** number of outputs */
-            unsigned int m_outputs;
-
-            QVector< QPointer<Kwave::StreamObject> > m_indexer;
-
-            /** queues for input data */
-            QVector< QQueue<Kwave::SampleArray> > m_input_queue;
-
-            /** buffers with output data */
-            QVector< QPointer<Kwave::SampleBuffer> > m_output_buffer;
-
-            /** mutex for locking access to the queues */
-            QMutex m_lock;
+        using StreamObject::input;
+    public:
+        /**
+         * Constructor
+         * @param inputs number of input channels
+         * @param outputs number of output channels
+         */
+        ChannelMixer(unsigned int inputs, unsigned int outputs);
+
+        /** Destructor */
+        ~ChannelMixer() override;
+
+        /**
+         * Init function, you must call it once after creating and before
+         * using this object. If the return value is false, you should
+         * delete this object.
+         * @return true if succeeded, false if failed
+         */
+        virtual bool init();
+
+        /**
+         * Returns the number of inputs.
+         * @return number of inputs
+         */
+        unsigned int inputs() const override
+        {
+            return m_inputs;
+        }
+
+        /**
+         * Returns the number of outputs.
+         * @return number of outputs
+         */
+        unsigned int outputs() const override
+        {
+            return m_outputs;
+        }
+
+        /**
+         * returns the number of tracks
+         * @return always 0 -> use inputs() or outputs()
+         */
+        unsigned int tracks() const override { return 0; }
+
+        /**
+         * Returns the number of tracks used as output, usually defaults to
+         * the number of tracks
+         * @return number of output tracks
+         */
+        unsigned int tracksOut() const override { return m_outputs; }
+
+        /**
+         * Returns this for track 0, otherwise a nullptr
+         *
+         * @param track index of the track
+         * @return a stream object or nullptr
+         */
+        Kwave::StreamObject *in(unsigned int track) override
+        {
+            return (track == 0) ? this : nullptr;
+        }
+
+        /**
+         * Returns the source that corresponds to one specific track
+         * if the object has multiple tracks. For single-track objects
+         * it returns "this" for the first index and 0 for all others
+         * @param track index of the track
+         * @return a stream object or nullptr
+         */
+        Kwave::StreamObject *out(unsigned int track) override;
+
+        /** does nothing, work is done automatically in mix() */
+        void goOn() override
+        {
+        }
+
+        /** receives a block with index + input data */
+        virtual void input(unsigned int port,
+                           Kwave::SampleArray &data) override;
+
+    private:
+
+        /** does the calculation */
+        virtual void mix();
+
+    private:
+
+        /** mixer matrix */
+        Kwave::MixerMatrix *m_matrix;
+
+        /** number of inputs */
+        unsigned int m_inputs;
+
+        /** number of outputs */
+        unsigned int m_outputs;
+
+        /** queues for input data */
+        QVector< QQueue<Kwave::SampleArray> > m_input_queue;
+
+        /** buffers with output data */
+        QVector< QPointer<Kwave::SampleBuffer> > m_output_buffer;
+
+        /** mutex for locking access to the queues */
+        QMutex m_lock;
     };
 
-
 }
 
 #endif /* CHANNEL_MIXER_H */
diff --git a/libkwave/modules/CurveStreamAdapter.cpp b/libkwave/modules/CurveStreamAdapter.cpp
index cd70dc23..a4e93464 100644
--- a/libkwave/modules/CurveStreamAdapter.cpp
+++ b/libkwave/modules/CurveStreamAdapter.cpp
@@ -52,7 +52,7 @@ void Kwave::CurveStreamAdapter::goOn()
             m_position = 0;
     }
 
-    emit output(m_buffer);
+    output(m_buffer);
 }
 
 /***************************************************************************/
diff --git a/libkwave/modules/CurveStreamAdapter.h b/libkwave/modules/CurveStreamAdapter.h
index 86479e27..5045db1b 100644
--- a/libkwave/modules/CurveStreamAdapter.h
+++ b/libkwave/modules/CurveStreamAdapter.h
@@ -46,12 +46,6 @@ namespace Kwave
         /** @see Kwave::KwaveSampleSource */
         void goOn() override;
 
-
-    signals:
-
-        /** emits a block with the interpolated curve */
-        void output(Kwave::SampleArray data);
-
     private:
 
         /** position within the interpolation */
diff --git a/libkwave/modules/Delay.cpp b/libkwave/modules/Delay.cpp
index 47e5ef23..c59593c3 100644
--- a/libkwave/modules/Delay.cpp
+++ b/libkwave/modules/Delay.cpp
@@ -35,11 +35,11 @@ Kwave::Delay::~Delay()
 void Kwave::Delay::goOn()
 {
     m_fifo.get(m_out_buffer);
-    emit output(m_out_buffer);
+    output(m_out_buffer);
 }
 
 //***************************************************************************
-void Kwave::Delay::input(Kwave::SampleArray data)
+void Kwave::Delay::input(Kwave::SampleArray &data)
 {
     m_fifo.put(data);
 }
diff --git a/libkwave/modules/Delay.h b/libkwave/modules/Delay.h
index 6683a1ea..0ca31809 100644
--- a/libkwave/modules/Delay.h
+++ b/libkwave/modules/Delay.h
@@ -35,6 +35,8 @@ namespace Kwave
     class LIBKWAVE_EXPORT Delay: public Kwave::SampleSource
     {
         Q_OBJECT
+        using StreamObject::input;
+
         public:
             /** Constructor */
             Delay();
@@ -45,15 +47,11 @@ namespace Kwave
             /** does the calculation */
             void goOn() override;
 
-        signals:
-            /** emits a block with delayed wave data */
-            void output(Kwave::SampleArray data);
+            /** receives input data */
+            void input(Kwave::SampleArray &data) override;
 
         public slots:
 
-            /** receives input data */
-            void input(Kwave::SampleArray data);
-
             /**
              * Sets the delay time, normed to samples.
              * The default setting is zero.
diff --git a/libkwave/modules/Indexer.cpp b/libkwave/modules/Indexer.cpp
deleted file mode 100644
index 83126107..00000000
--- a/libkwave/modules/Indexer.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*************************************************************************
-            Indexer.cpp  -  add an index to a stream
-                             -------------------
-    begin                : Sat Oct 16 2010
-    copyright            : (C) 2010 by Thomas Eschenbacher
-    email                : [email protected]
- ***************************************************************************/
-
-/***************************************************************************
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- ***************************************************************************/
-
-#include "config.h"
-
-#include "libkwave/modules/Indexer.h"
-
-//***************************************************************************
-Kwave::Indexer::Indexer(unsigned int index)
-    :Kwave::StreamObject(), m_index(index)
-{
-}
-
-//***************************************************************************
-Kwave::Indexer::~Indexer()
-{
-}
-
-//***************************************************************************
-void Kwave::Indexer::input(Kwave::SampleArray data)
-{
-    emit output(m_index, data);
-}
-
-//***************************************************************************
-//***************************************************************************
-
-#include "moc_Indexer.cpp"
diff --git a/libkwave/modules/Indexer.h b/libkwave/modules/Indexer.h
deleted file mode 100644
index 12b75154..00000000
--- a/libkwave/modules/Indexer.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*************************************************************************
-              Indexer.h  -  add an index to a stream
-                             -------------------
-    begin                : Sat Oct 16 2010
-    copyright            : (C) 2010 by Thomas Eschenbacher
-    email                : [email protected]
- ***************************************************************************/
-
-/***************************************************************************
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- ***************************************************************************/
-
-#ifndef INDEXER_H
-#define INDEXER_H
-
-#include "config.h"
-
-#include <QObject>
-#include <QString>
-
-#include "libkwave/SampleArray.h"
-#include "libkwave/modules/StreamObject.h"
-
-//***************************************************************************
-namespace Kwave
-{
-
-    class Indexer :public Kwave::StreamObject
-    {
-        Q_OBJECT
-    public:
-        /**
-         * Constructor
-         * @param index an index that is attached to the input data
-         */
-        Indexer(unsigned int index);
-
-        /** Destructor */
-        virtual ~Indexer() override;
-
-    public slots:
-
-        /** receives input data */
-        void input(Kwave::SampleArray data);
-
-    signals:
-
-        /** forwards index + data as output */
-        void output(unsigned int index, Kwave::SampleArray data);
-
-    private:
-
-        /** the index that is attached to each output data */
-        unsigned int m_index;
-    };
-
-}
-
-#endif /* INDEXER_H */
-
-//***************************************************************************
-//***************************************************************************
diff --git a/libkwave/modules/Mul.cpp b/libkwave/modules/Mul.cpp
index ac95ff0a..3ba90c58 100644
--- a/libkwave/modules/Mul.cpp
+++ b/libkwave/modules/Mul.cpp
@@ -95,8 +95,9 @@ void Kwave::Mul::multiply()
 
     // special handling for zero length input
     if (!count) {
-        emit output(Kwave::SampleArray()); // emit zero length output
-        return;                            // and bail out
+        Kwave::SampleArray empty;
+        output(empty); // emit zero length output
+        return;        // and bail out
     }
 
 //     if (!m_a_is_const && !m_b_is_const && (m_a.size() != m_b.size()))
@@ -127,7 +128,7 @@ void Kwave::Mul::multiply()
     }
 
     // emit the result
-    emit output(m_buffer_x);
+    output(m_buffer_x);
 }
 
 /***************************************************************************/
diff --git a/libkwave/modules/Mul.h b/libkwave/modules/Mul.h
index 8629a904..7a697b91 100644
--- a/libkwave/modules/Mul.h
+++ b/libkwave/modules/Mul.h
@@ -38,76 +38,87 @@ namespace Kwave
     class LIBKWAVE_EXPORT Mul: public Kwave::SampleSource
     {
         Q_OBJECT
-        public:
-            /** Constructor */
-            Mul();
+        using StreamObject::input;
 
-            /** Destructor */
-            ~Mul() override;
+    public:
+        /** Constructor */
+        Mul();
 
-            /** does nothing, work is done automatically in multiply() */
-            void goOn() override;
+        /** Destructor */
+        ~Mul() override;
 
-        signals:
-            /** emits a block with the interpolated curve */
-            void output(Kwave::SampleArray data);
+        /** does nothing, work is done automatically in multiply() */
+        void goOn() override;
 
-        public slots:
+        virtual void input(unsigned int port,
+                           Kwave::SampleArray &data) override
+        {
+            switch (port)
+            {
+                case 0: input_a(data); break;
+                case 1: input_b(data); break;
+                default:
+                    Q_ASSERT(port < 2);
+                    break;
+            }
+        }
 
-            /** receives input data for input A */
-            void input_a(Kwave::SampleArray data);
+        /** receives input data for input A */
+        void input_a(Kwave::SampleArray data);
 
-            /** receives input data for input B */
-            void input_b(Kwave::SampleArray data);
+        /** receives input data for input B */
+        void input_b(Kwave::SampleArray data);
 
-            /** sets input A to a constant value (as float) */
-            void set_a(const QVariant &a);
+    public slots:
 
-            /** sets input B to a constant value (as float) */
-            void set_b(const QVariant &b);
+        /** sets input A to a constant value (as float) */
+        void set_a(const QVariant &a);
 
-        private:
+        /** sets input B to a constant value (as float) */
+        void set_b(const QVariant &b);
 
-            /** does the calculation */
-            virtual void multiply();
+    private:
 
-        private:
+        /** does the calculation */
+        virtual void multiply();
 
-            /** queue for input A */
-            QQueue<Kwave::SampleArray> m_queue_a;
+    private:
 
-            /** queue for input B */
-            QQueue<Kwave::SampleArray> m_queue_b;
+        /** queue for input A */
+        QQueue<Kwave::SampleArray> m_queue_a;
 
-            /** semaphore to wait for input on input A */
-            QSemaphore m_sem_a;
+        /** queue for input B */
+        QQueue<Kwave::SampleArray> m_queue_b;
 
-            /** semaphore to wait for input on input B */
-            QSemaphore m_sem_b;
+        /** semaphore to wait for input on input A */
+        QSemaphore m_sem_a;
 
-            /** buffer for input A (currently in work) */
-            Kwave::SampleArray m_a;
+        /** semaphore to wait for input on input B */
+        QSemaphore m_sem_b;
 
-            /** buffer for input B (currently in work) */
-            Kwave::SampleArray m_b;
+        /** buffer for input A (currently in work) */
+        Kwave::SampleArray m_a;
 
-            /** buffer for output data */
-            Kwave::SampleArray m_buffer_x;
+        /** buffer for input B (currently in work) */
+        Kwave::SampleArray m_b;
 
-            /** if true, input A is a constant */
-            bool m_a_is_const;
+        /** buffer for output data */
+        Kwave::SampleArray m_buffer_x;
 
-            /** if true, input B is a constant */
-            bool m_b_is_const;
+        /** if true, input A is a constant */
+        bool m_a_is_const;
 
-            /** if m_a_is_const is set, the value of A */
-            float m_value_a;
+        /** if true, input B is a constant */
+        bool m_b_is_const;
 
-            /** if m_b_is_const is set, the value of B */
-            float m_value_b;
+        /** if m_a_is_const is set, the value of A */
+        float m_value_a;
 
-            /** mutex for locking access to the queues */
-            QMutex m_lock;
+        /** if m_b_is_const is set, the value of B */
+        float m_value_b;
+
+        /** mutex for locking access to the queues */
+        QMutex m_lock;
     };
 }
 
diff --git a/libkwave/modules/Osc.cpp b/libkwave/modules/Osc.cpp
index 20263f64..e9d980a4 100644
--- a/libkwave/modules/Osc.cpp
+++ b/libkwave/modules/Osc.cpp
@@ -55,7 +55,7 @@ void Kwave::Osc::goOn()
             m_omega_t -= two_pi;
     }
 
-    emit output(m_buffer);
+    output(m_buffer);
 }
 
 //***************************************************************************
diff --git a/libkwave/modules/Osc.h b/libkwave/modules/Osc.h
index 3061e56b..441773fd 100644
--- a/libkwave/modules/Osc.h
+++ b/libkwave/modules/Osc.h
@@ -33,54 +33,51 @@ namespace Kwave
     class LIBKWAVE_EXPORT Osc: public Kwave::SampleSource
     {
         Q_OBJECT
-        public:
-            /** Constructor */
-            Osc();
-
-            /** Destructor */
-            ~Osc() override;
-
-            /** does the calculation */
-            void goOn() override;
-
-        signals:
-            /** emits a block with sine wave data */
-            void output(Kwave::SampleArray data);
-
-        public slots:
-            /**
-             * Sets the frequency of the sine wave, normed to the
-             * sample frequency. You should pass the frequency that
-             * you want, divided through the sample frequency.
-             * If you never call this, the frequency will be undefined!
-             */
-            void setFrequency(const QVariant &f);
-
-            /**
-             * Sets the phase of the sine wave in RAD [0...2*Pi].
-             * The default setting is zero.
-             */
-            void setPhase(const QVariant &p);
-
-            /**
-             * Sets the amplitude of the sine wave, normed to the
-             * range of [0.0 ... 1.0]. The default is 1.0.
-             */
-            void setAmplitude(const QVariant &a);
-
-        private:
-
-            /** buffer for output data */
-            Kwave::SampleArray m_buffer;
-
-            /** current time multiplied by 2*Pi*f */
-            double m_omega_t;
-
-            /** frequency [samples/period] */
-            double m_f;
-
-            /** amplitude [0...1] */
-            double m_a;
+    public:
+        /** Constructor */
+        Osc();
+
+        /** Destructor */
+        ~Osc() override;
+
+        /** does the calculation */
+        void goOn() override;
+
+    public slots:
+
+        /**
+         * Sets the frequency of the sine wave, normed to the
+         * sample frequency. You should pass the frequency that
+         * you want, divided through the sample frequency.
+         * If you never call this, the frequency will be undefined!
+         */
+        void setFrequency(const QVariant &f);
+
+        /**
+         * Sets the phase of the sine wave in RAD [0...2*Pi].
+         * The default setting is zero.
+         */
+        void setPhase(const QVariant &p);
+
+        /**
+         * Sets the amplitude of the sine wave, normed to the
+         * range of [0.0 ... 1.0]. The default is 1.0.
+         */
+        void setAmplitude(const QVariant &a);
+
+    private:
+
+        /** buffer for output data */
+        Kwave::SampleArray m_buffer;
+
+        /** current time multiplied by 2*Pi*f */
+        double m_omega_t;
+
+        /** frequency [samples/period] */
+        double m_f;
+
+        /** amplitude [0...1] */
+        double m_a;
     };
 }
 
diff --git a/libkwave/modules/RateConverter.cpp b/libkwave/modules/RateConverter.cpp
index 60ee09b3..a7b544a8 100644
--- a/libkwave/modules/RateConverter.cpp
+++ b/libkwave/modules/RateConverter.cpp
@@ -47,11 +47,11 @@ void Kwave::RateConverter::goOn()
 }
 
 //***************************************************************************
-void Kwave::RateConverter::input(Kwave::SampleArray data)
+void Kwave::RateConverter::input(Kwave::SampleArray &data)
 {
     // shortcut for ratio == 1:1
     if ((m_ratio == 1.0) || data.isEmpty()) {
-        emit output(data);
+        output(data);
         return;
     }
 
@@ -124,7 +124,7 @@ void Kwave::RateConverter::input(Kwave::SampleArray data)
     for (; remaining; remaining--, ++s_out, ++f_out)
         *s_out = float2sample(*f_out);
 
-    emit output(out);
+    output(out);
 }
 
 //***************************************************************************
diff --git a/libkwave/modules/RateConverter.h b/libkwave/modules/RateConverter.h
index 70826965..e1f38108 100644
--- a/libkwave/modules/RateConverter.h
+++ b/libkwave/modules/RateConverter.h
@@ -37,6 +37,7 @@ namespace Kwave
     class LIBKWAVE_EXPORT RateConverter: public Kwave::SampleSource
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /** Constructor */
@@ -48,16 +49,11 @@ namespace Kwave
         /** does nothing, processing is done in input() */
         void goOn() override;
 
-    signals:
-
-        /** emits a block with the filtered data */
-        void output(Kwave::SampleArray data);
+        /** receives input data and also directly does the calculation */
+        void input(Kwave::SampleArray &data) override;
 
     public slots:
 
-        /** receives input data and also directly does the calculation */
-        void input(Kwave::SampleArray data);
-
         /**
          * Sets the conversion ratio, ((new rate) / (old rate))
          */
diff --git a/libkwave/modules/SampleBuffer.cpp b/libkwave/modules/SampleBuffer.cpp
index a6d23903..1842b2f1 100644
--- a/libkwave/modules/SampleBuffer.cpp
+++ b/libkwave/modules/SampleBuffer.cpp
@@ -112,7 +112,7 @@ void Kwave::SampleBuffer::finished()
 }
 
 //***************************************************************************
-void Kwave::SampleBuffer::input(Kwave::SampleArray data)
+void Kwave::SampleBuffer::input(Kwave::SampleArray &data)
 {
     // if we have buffered data, flush that first
     if (m_buffered) {
@@ -140,7 +140,7 @@ void Kwave::SampleBuffer::enqueue(Kwave::SampleArray data)
 void Kwave::SampleBuffer::emitData(Kwave::SampleArray data)
 {
     // NOTE: this signal could be connected to a slot that blocks for a while
-    emit output(data);
+    output(data);
     m_sema.release();
 }
 
diff --git a/libkwave/modules/SampleBuffer.h b/libkwave/modules/SampleBuffer.h
index b9128b5d..1eabb028 100644
--- a/libkwave/modules/SampleBuffer.h
+++ b/libkwave/modules/SampleBuffer.h
@@ -34,6 +34,7 @@ namespace Kwave
     class LIBKWAVE_EXPORT SampleBuffer: public Kwave::SampleSink
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /**
@@ -76,10 +77,11 @@ namespace Kwave
         /** emit the sample data stored in m_data */
         virtual void finished();
 
-    public slots:
-
-        /** slot for taking input data, stores it into m_data */
-        virtual void input(Kwave::SampleArray data);
+        /**
+         * Interface for the streaming API.
+         * @param data sample data to write
+         */
+        void input(Kwave::SampleArray &data) override;
 
     protected:
 
@@ -96,11 +98,6 @@ namespace Kwave
         /** enqueue some data */
         void enqueue(Kwave::SampleArray data);
 
-    signals:
-
-        /** emits the data received via input() */
-        void output(Kwave::SampleArray data);
-
     private:
 
         /** array with sample data */
diff --git a/libkwave/modules/StreamObject.cpp b/libkwave/modules/StreamObject.cpp
index 2b579faf..2d7967e6 100644
--- a/libkwave/modules/StreamObject.cpp
+++ b/libkwave/modules/StreamObject.cpp
@@ -17,9 +17,13 @@
 
 #include "config.h"
 
+#include <QFutureSynchronizer>
 #include <QString>
 #include <QVariant>
+#include <QtConcurrentRun>
 
+#include "libkwave/SampleSink.h"
+#include "libkwave/SampleSource.h"
 #include "libkwave/modules/StreamObject.h"
 
 /** interactive mode */
@@ -39,6 +43,20 @@ Kwave::StreamObject::~StreamObject()
 {
 }
 
+//***************************************************************************
+Kwave::StreamObject *Kwave::StreamObject::in(unsigned int track)
+{
+    return (track == 0) ?
+        dynamic_cast<Kwave::SampleSink*>(this) : nullptr;
+}
+
+//***************************************************************************
+Kwave::StreamObject *Kwave::StreamObject::out(unsigned int track)
+{
+    return (track == 0) ?
+        dynamic_cast<Kwave::SampleSource *>(this) : nullptr;
+}
+
 //***************************************************************************
 void Kwave::StreamObject::setAttribute(const char *attribute,
                                        const QVariant &value)
@@ -75,6 +93,40 @@ void Kwave::StreamObject::setInteractive(bool interactive)
     m_interactive = interactive;
 }
 
+//***************************************************************************
+void Kwave::StreamObject::connectTo(Kwave::StreamObject *sink,
+                                    unsigned int port)
+{
+    m_sinks.append({sink, port});
+}
+
+//***************************************************************************
+void Kwave::StreamObject::input(Kwave::SampleArray &data)
+{
+    Q_UNUSED(data);
+}
+
+//***************************************************************************
+void Kwave::StreamObject::output(Kwave::SampleArray &data)
+{
+    qsizetype count = m_sinks.count();
+    if (count > 1) {
+        QFutureSynchronizer<void> synchronizer;
+        for (auto &sink : m_sinks) {
+            if (sink.m_sink == nullptr) continue;
+            synchronizer.addFuture(QtConcurrent::run(
+                [&sink, &data] () {
+                    sink.m_sink->input(sink.m_port, data);
+            }));
+        }
+        synchronizer.waitForFinished();
+    } else if (count == 1) {
+        auto &sink = m_sinks.first();
+        if (sink.m_sink != nullptr)
+            sink.m_sink->input(sink.m_port, data);
+    }
+}
+
 //***************************************************************************
 void Kwave::StreamObject::cancel()
 {
diff --git a/libkwave/modules/StreamObject.h b/libkwave/modules/StreamObject.h
index c64f67c0..d589fbdc 100644
--- a/libkwave/modules/StreamObject.h
+++ b/libkwave/modules/StreamObject.h
@@ -22,13 +22,18 @@
 #include "libkwave_export.h"
 
 #include <QtGlobal>
+#include <QList>
 #include <QObject>
 #include <QRecursiveMutex>
 
+#include "libkwave/SampleArray.h"
+
 class QVariant;
 
 namespace Kwave
 {
+    class SampleSink;
+    class SampleSource;
 
     class LIBKWAVE_EXPORT StreamObject: public QObject
     {
@@ -50,42 +55,63 @@ namespace Kwave
          */
         virtual unsigned int tracks() const { return 1; }
 
+        virtual Kwave::StreamObject * operator [] (unsigned int track)
+        {
+            return (track == 0) ? this : nullptr;
+        }
+
+        /**
+         * Returns the number of tracks used as input, usually defaults to
+         * the number of tracks
+         * @return number of input tracks
+         */
+        virtual unsigned int tracksIn() const { return tracks(); }
+
+        /**
+         * Returns the number of tracks used as output, usually defaults to
+         * the number of tracks
+         * @return number of output tracks
+         */
+        virtual unsigned int tracksOut() const { return tracks(); }
+
+        /**
+         * Returns the sink that corresponds to one specific track
+         * if the object has multiple tracks. For single-track objects
+         * it returns "this" for the first index and 0 for all others
+         * @param track index of the track
+         * @return a stream object or nullptr
+         */
+        virtual Kwave::StreamObject *in(unsigned int track);
+
         /**
          * Returns the source that corresponds to one specific track
          * if the object has multiple tracks. For single-track objects
          * it returns "this" for the first index and 0 for all others
          * @param track index of the track
-         * @return a stream object or NULL
+         * @return a stream object or nullptr
          */
-        virtual Kwave::StreamObject * operator [] (unsigned int track)
-        {
-            return (track == 0) ? this : nullptr;
-        }
+        virtual Kwave::StreamObject *out(unsigned int track);
 
         /**
-         * Returns the number of tracks of a input or output port.
-         * Can be overwritten for objects that have a different count
-         * of inputs and outputs.
-         * @param port name of the port (name of signal or slot)
-         * @return number of tracks of a input or output, default is
-         *         the same as tracks()
+         * Returns the number of input. Defaults to the number of
+         * tracks, but can be overwritten for objects that have different
+         * numbers of tracks / inputs.
+         * @return number of inputs
          */
-        virtual unsigned int tracksOfPort(const char *port) const
+        virtual unsigned int inputs() const
         {
-            Q_UNUSED(port)
             return tracks();
         }
 
         /**
-         * Returns an indexed port, identified by name
-         * @param port name of the port (name of signal or slot)
-         * @param track index of the track
-         * @return the corresponding stream object
+         * Returns the number of outputs. Defaults to the number of
+         * tracks, but can be overwritten for objects that have different
+         * numbers of tracks / outputs.
+         * @return number of outputs
          */
-        virtual Kwave::StreamObject *port(const char *port, unsigned int track)
+        virtual unsigned int outputs() const
         {
-            Q_UNUSED(port)
-            return (*this)[track];
+            return tracks();
         }
 
         /**
@@ -114,6 +140,48 @@ namespace Kwave
         /** returns true if the transfer has been canceled */
         virtual bool isCanceled() const { return m_canceled; }
 
+        /**
+         * Connect the output of this stream object to another stream
+         * object that acts as a sink.
+         *
+         * @param sink pointer to a StreamObject
+         * @param port index of the input or 0
+         */
+        virtual void connectTo(Kwave::StreamObject *sink,
+                               unsigned int port);
+
+        /**
+         * Receive input data, version without port.
+         *
+         * @param port index of the input, use zero if only
+         *                    one input exists
+         * @param data the sample data to process
+         */
+        virtual void input(Kwave::SampleArray &data);
+
+        /**
+         * Receive input data, default version which just calls
+         * input(0, data).
+         * @param port index of the input, use zero if only
+         *                    one input exists
+         * @param data the sample data to process
+         */
+        virtual void input(unsigned int port,
+                           Kwave::SampleArray &data)
+        {
+            Q_UNUSED(port);
+            Q_ASSERT(port == 0);
+            input(data);
+        }
+
+        /**
+         * Should be called from the worker function of the stream
+         * object to publish resulting data to the next stage.
+         * Can be connected to one or more inputs of of other stream
+         * objects.
+         */
+        virtual void output(Kwave::SampleArray &data);
+
     public slots:
 
         /**
@@ -138,6 +206,19 @@ namespace Kwave
 
     private:
 
+        /** info about a sink that is connected to one of our outputs */
+        struct ConnectedSink
+        {
+            /** pointer to the sink, must not be nullptr */
+            Kwave::StreamObject *m_sink;
+
+            /** zero based index of the input (default is 0) */
+            unsigned int         m_port;
+        };
+
+        /** List of connected sinks which receive our output */
+        QList<ConnectedSink> m_sinks;
+
         /** Mutex for locking access to setAttribute (recursive) */
         QRecursiveMutex m_lock_set_attribute;
 
diff --git a/plugins/amplifyfree/AmplifyFreePlugin.cpp b/plugins/amplifyfree/AmplifyFreePlugin.cpp
index 6c6e4740..13f55aa3 100644
--- a/plugins/amplifyfree/AmplifyFreePlugin.cpp
+++ b/plugins/amplifyfree/AmplifyFreePlugin.cpp
@@ -163,18 +163,10 @@ void Kwave::AmplifyFreePlugin::run(QStringList params)
     if (!sink.tracks()) return;
 
     // connect them
-    bool ok = Kwave::connect(
-        source, SIGNAL(output(Kwave::SampleArray)),
-        mul,    SLOT(input_a(Kwave::SampleArray)));
-    if (ok) ok = Kwave::connect(
-        curve,  SIGNAL(output(Kwave::SampleArray)),
-        mul,    SLOT(input_b(Kwave::SampleArray)));
-    if (ok) ok = Kwave::connect(
-        mul,    SIGNAL(output(Kwave::SampleArray)),
-        sink,   SLOT(input(Kwave::SampleArray)));
-    if (!ok) {
-        return;
-    }
+    bool    ok = Kwave::connect(source, mul, 0);
+    if (ok) ok = Kwave::connect(curve,  mul, 1);
+    if (ok) ok = Kwave::connect(mul,    sink);
+    if (!ok)return;
 
     // connect the progress dialog
     connect(&sink, SIGNAL(progress(qreal)),
diff --git a/plugins/band_pass/BandPass.cpp b/plugins/band_pass/BandPass.cpp
index 2c605b72..ee34b83d 100644
--- a/plugins/band_pass/BandPass.cpp
+++ b/plugins/band_pass/BandPass.cpp
@@ -42,7 +42,7 @@ Kwave::BandPass::~BandPass()
 //***************************************************************************
 void Kwave::BandPass::goOn()
 {
-    emit output(m_buffer);
+    output(m_buffer);
 }
 
 //***************************************************************************
@@ -107,7 +107,7 @@ void Kwave::BandPass::setfilter_2polebp(double freq, double R)
 }
 
 //***************************************************************************
-void Kwave::BandPass::input(Kwave::SampleArray data)
+void Kwave::BandPass::input(Kwave::SampleArray &data)
 {
     const Kwave::SampleArray &in = data;
 
diff --git a/plugins/band_pass/BandPass.h b/plugins/band_pass/BandPass.h
index 7a242142..68b722c6 100644
--- a/plugins/band_pass/BandPass.h
+++ b/plugins/band_pass/BandPass.h
@@ -32,11 +32,11 @@
 
 namespace Kwave
 {
-
     class BandPass: public Kwave::SampleSource,
                     public Kwave::TransmissionFunction
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /** Constructor */
@@ -51,16 +51,11 @@ namespace Kwave
         /** @see TransmissionFunction::at() */
         double at(double f) override;
 
-    signals:
-
-        /** emits a block with the filtered data */
-        void output(Kwave::SampleArray data);
+        /** receives input data */
+        void input(Kwave::SampleArray &data) override;
 
     public slots:
 
-        /** receives input data */
-        void input(Kwave::SampleArray data);
-
         /**
          * Sets the center frequency, normed to [0...2Pi]. The calculation is:
          * fc = frequency [Hz] * 2 * Pi / f_sample [Hz].
diff --git a/plugins/codec_ogg/OpusDecoder.cpp b/plugins/codec_ogg/OpusDecoder.cpp
index 8afa6dd0..d3201e91 100644
--- a/plugins/codec_ogg/OpusDecoder.cpp
+++ b/plugins/codec_ogg/OpusDecoder.cpp
@@ -450,10 +450,7 @@ int Kwave::OpusDecoder::open(QWidget *widget, Kwave::FileInfo &info)
                 QVariant(rate_to / rate_from)
             );
 
-            ok = Kwave::connect(
-                *m_buffer,         SIGNAL(output(Kwave::SampleArray)),
-                *m_rate_converter, SLOT(input(Kwave::SampleArray))
-            );
+            ok = Kwave::connect(*m_buffer, *m_rate_converter);
         }
 
         if (!ok) {
@@ -567,9 +564,7 @@ int Kwave::OpusDecoder::decode(Kwave::MultiWriter &dst)
     if (!m_output_is_connected) {
         Kwave::StreamObject *src =
             (m_rate_converter) ? m_rate_converter : m_buffer;
-        if (!Kwave::connect(*src, SIGNAL(output(Kwave::SampleArray)),
-                             dst, SLOT(input(Kwave::SampleArray))) )
-        {
+        if (!Kwave::connect(*src, dst)) {
             qWarning("OpusDecoder::decode() connecting output failed");
             return -1;
         }
diff --git a/plugins/codec_ogg/OpusEncoder.cpp b/plugins/codec_ogg/OpusEncoder.cpp
index 6a5b99cd..5154d23f 100644
--- a/plugins/codec_ogg/OpusEncoder.cpp
+++ b/plugins/codec_ogg/OpusEncoder.cpp
@@ -181,10 +181,7 @@ bool Kwave::OpusEncoder::setupDownMix(QWidget *widget, unsigned int tracks,
 
         // connect it to the end of the current preprocessing queue
         // (normally this is the original sample source)
-        if (!Kwave::connect(
-            *m_last_queue_element, SIGNAL(output(Kwave::SampleArray)),
-            *m_channel_mixer,      SLOT(input(Kwave::SampleArray))))
-        {
+        if (!Kwave::connect(*m_last_queue_element, *m_channel_mixer)) {
             qWarning("connecting the channel mixer failed");
             return false;
         }
@@ -295,10 +292,7 @@ bool Kwave::OpusEncoder::setupCodingRate(QWidget *widget,
     // connect the rate converter to the end of the current preprocessing
     // queue/ (normally this is either the original sample source or
     // a channel mixer)
-    if (!Kwave::connect(
-        *m_last_queue_element, SIGNAL(output(Kwave::SampleArray)),
-        *m_rate_converter,     SLOT(input(Kwave::SampleArray))))
-    {
+    if (!Kwave::connect(*m_last_queue_element, *m_rate_converter)) {
         qWarning("connecting the rate converter failed");
         return false;
     }
@@ -551,10 +545,7 @@ bool Kwave::OpusEncoder::open(QWidget *widget, const Kwave::FileInfo &info,
         qWarning("cannot create sample buffer");
         return false;
     }
-    if (!Kwave::connect(
-        *m_last_queue_element, SIGNAL(output(Kwave::SampleArray)),
-        *m_buffer,             SLOT(input(Kwave::SampleArray))) )
-    {
+    if (!Kwave::connect(*m_last_queue_element, *m_buffer)) {
         qWarning("failed to connect sample buffer");
         return false;
     }
diff --git a/plugins/lowpass/LowPassFilter.cpp b/plugins/lowpass/LowPassFilter.cpp
index 49c525c0..0acfcad3 100644
--- a/plugins/lowpass/LowPassFilter.cpp
+++ b/plugins/lowpass/LowPassFilter.cpp
@@ -40,7 +40,7 @@ Kwave::LowPassFilter::~LowPassFilter()
 //***************************************************************************
 void Kwave::LowPassFilter::goOn()
 {
-    emit output(m_buffer);
+    output(m_buffer);
 }
 
 //***************************************************************************
@@ -135,7 +135,7 @@ void Kwave::LowPassFilter::normed_setfilter_shelvelowpass(double freq)
 }
 
 //***************************************************************************
-void Kwave::LowPassFilter::input(Kwave::SampleArray data)
+void Kwave::LowPassFilter::input(Kwave::SampleArray &data)
 {
     const Kwave::SampleArray &in = data;
     bool ok = m_buffer.resize(in.size());
diff --git a/plugins/lowpass/LowPassFilter.h b/plugins/lowpass/LowPassFilter.h
index b010301d..8e857a81 100644
--- a/plugins/lowpass/LowPassFilter.h
+++ b/plugins/lowpass/LowPassFilter.h
@@ -33,6 +33,7 @@ namespace Kwave
                          public Kwave::TransmissionFunction
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /** Constructor */
@@ -47,16 +48,10 @@ namespace Kwave
         /** does the calculation */
         void goOn() override;
 
-    signals:
-
-        /** emits a block with the filtered data */
-        void output(Kwave::SampleArray data);
-
-    public slots:
-
         /** receives input data */
-        void input(Kwave::SampleArray data);
+        void input(Kwave::SampleArray &data) override;
 
+    public slots:
         /**
          * Sets the cutoff frequency, normed to [0...2Pi]. The calculation is:
          * fc = frequency [Hz] * 2 * Pi / f_sample [Hz].
diff --git a/plugins/noise/NoiseGenerator.cpp b/plugins/noise/NoiseGenerator.cpp
index a31d8150..4d80e8bf 100644
--- a/plugins/noise/NoiseGenerator.cpp
+++ b/plugins/noise/NoiseGenerator.cpp
@@ -39,11 +39,11 @@ Kwave::NoiseGenerator::~NoiseGenerator()
 //***************************************************************************
 void Kwave::NoiseGenerator::goOn()
 {
-    emit output(m_buffer);
+    output(m_buffer);
 }
 
 //***************************************************************************
-void Kwave::NoiseGenerator::input(Kwave::SampleArray data)
+void Kwave::NoiseGenerator::input(Kwave::SampleArray &data)
 {
     bool ok = m_buffer.resize(data.size());
     Q_ASSERT(ok);
diff --git a/plugins/noise/NoiseGenerator.h b/plugins/noise/NoiseGenerator.h
index 45c2c63e..b8929019 100644
--- a/plugins/noise/NoiseGenerator.h
+++ b/plugins/noise/NoiseGenerator.h
@@ -32,6 +32,7 @@ namespace Kwave
     class NoiseGenerator: public Kwave::SampleSource
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /** Constructor */
@@ -46,16 +47,10 @@ namespace Kwave
          */
         void goOn() override;
 
-    signals:
-
-        /** emits a block with noise */
-        void output(Kwave::SampleArray data);
-
-    public slots:
-
         /** receives input data */
-        void input(Kwave::SampleArray data);
+        void input(Kwave::SampleArray &data) override;
 
+    public slots:
         /**
          * Sets the noise level as a factor [0 .. 1.0]
          */
diff --git a/plugins/normalize/NormalizePlugin.cpp b/plugins/normalize/NormalizePlugin.cpp
index 9b63fe7a..b482811a 100644
--- a/plugins/normalize/NormalizePlugin.cpp
+++ b/plugins/normalize/NormalizePlugin.cpp
@@ -111,15 +111,9 @@ void Kwave::NormalizePlugin::run(QStringList params)
             Qt::BlockingQueuedConnection);
 
     // connect them
-    bool ok = Kwave::connect(
-        source,     SIGNAL(output(Kwave::SampleArray)),
-        normalizer, SLOT(input(Kwave::SampleArray)));
-    if (ok) ok = Kwave::connect(
-        normalizer, SIGNAL(output(Kwave::SampleArray)),
-        sink,       SLOT(input(Kwave::SampleArray)));
-    if (!ok) {
-        return;
-    }
+    bool    ok = Kwave::connect(source,     normalizer);
+    if (ok) ok = Kwave::connect(normalizer, sink);
+    if (!ok) return;
 
     double target = pow(10.0, (TARGET_LEVEL / 20.0));
     double gain = target / level;
diff --git a/plugins/normalize/Normalizer.cpp b/plugins/normalize/Normalizer.cpp
index fa6a19ca..e1738137 100644
--- a/plugins/normalize/Normalizer.cpp
+++ b/plugins/normalize/Normalizer.cpp
@@ -70,7 +70,7 @@ static inline double limiter(const double x, const double lmtr_lvl)
 }
 
 //***************************************************************************
-void Kwave::Normalizer::input(Kwave::SampleArray data)
+void Kwave::Normalizer::input(Kwave::SampleArray &data)
 {
     const unsigned int len = data.size();
     const bool use_limiter = (m_gain > 1.0);
@@ -82,7 +82,7 @@ void Kwave::Normalizer::input(Kwave::SampleArray data)
         data[i] = double2sample(s);
     }
 
-    emit output(data);
+    output(data);
 }
 
 //***************************************************************************
diff --git a/plugins/normalize/Normalizer.h b/plugins/normalize/Normalizer.h
index 035d24be..b0615a27 100644
--- a/plugins/normalize/Normalizer.h
+++ b/plugins/normalize/Normalizer.h
@@ -34,6 +34,7 @@ namespace Kwave
     class Normalizer: public Kwave::SampleSource
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /** Constructor */
@@ -45,16 +46,11 @@ namespace Kwave
         /** does the calculation */
         void goOn() override;
 
-    signals:
-
-        /** emits a block with the filtered data */
-        void output(Kwave::SampleArray data);
+        /** receives input data */
+        void input(Kwave::SampleArray &data) override;
 
     public slots:
 
-        /** receives input data */
-        void input(Kwave::SampleArray data);
-
         /**
          * Sets the gain of the amplifier [0...1]
          */
diff --git a/plugins/notch_filter/NotchFilter.cpp b/plugins/notch_filter/NotchFilter.cpp
index 6b87bb75..c2de4e8f 100644
--- a/plugins/notch_filter/NotchFilter.cpp
+++ b/plugins/notch_filter/NotchFilter.cpp
@@ -40,7 +40,7 @@ Kwave::NotchFilter::~NotchFilter()
 //***************************************************************************
 void Kwave::NotchFilter::goOn()
 {
-    emit output(m_buffer);
+    output(m_buffer);
 }
 
 //***************************************************************************
@@ -117,7 +117,7 @@ void Kwave::NotchFilter::setfilter_peaknotch2(double freq, double bw)
 }
 
 //***************************************************************************
-void Kwave::NotchFilter::input(Kwave::SampleArray data)
+void Kwave::NotchFilter::input(Kwave::SampleArray &data)
 {
     const Kwave::SampleArray &in = data;
     bool ok = m_buffer.resize(in.size());
diff --git a/plugins/notch_filter/NotchFilter.h b/plugins/notch_filter/NotchFilter.h
index 8127f81a..1443efe3 100644
--- a/plugins/notch_filter/NotchFilter.h
+++ b/plugins/notch_filter/NotchFilter.h
@@ -36,6 +36,7 @@ namespace Kwave
                        public Kwave::TransmissionFunction
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /** Constructor */
@@ -50,16 +51,11 @@ namespace Kwave
         /** @see TransmissionFunction::at() */
         double at(double f) override;
 
-    signals:
-
-        /** emits a block with the filtered data */
-        void output(Kwave::SampleArray data);
+        /** receives input data */
+        void input(Kwave::SampleArray &data) override;
 
     public slots:
 
-        /** receives input data */
-        void input(Kwave::SampleArray data);
-
         /**
          * Sets the center frequency, normed to [0...2Pi]. The calculation is:
          * fc = frequency [Hz] * 2 * Pi / f_sample [Hz].
diff --git a/plugins/pitch_shift/PitchShiftFilter.cpp b/plugins/pitch_shift/PitchShiftFilter.cpp
index f055df70..787bebe8 100644
--- a/plugins/pitch_shift/PitchShiftFilter.cpp
+++ b/plugins/pitch_shift/PitchShiftFilter.cpp
@@ -48,7 +48,7 @@ Kwave::PitchShiftFilter::~PitchShiftFilter()
 //***************************************************************************
 void Kwave::PitchShiftFilter::goOn()
 {
-    emit output(m_buffer);
+    output(m_buffer);
 }
 
 //***************************************************************************
@@ -72,7 +72,7 @@ void Kwave::PitchShiftFilter::initFilter()
 }
 
 //***************************************************************************
-void Kwave::PitchShiftFilter::input(Kwave::SampleArray data)
+void Kwave::PitchShiftFilter::input(Kwave::SampleArray &data)
 {
     const Kwave::SampleArray &in = data;
 
diff --git a/plugins/pitch_shift/PitchShiftFilter.h b/plugins/pitch_shift/PitchShiftFilter.h
index f00ed7fc..1e925921 100644
--- a/plugins/pitch_shift/PitchShiftFilter.h
+++ b/plugins/pitch_shift/PitchShiftFilter.h
@@ -38,6 +38,7 @@ namespace Kwave
     class PitchShiftFilter: public Kwave::SampleSource
     {
         Q_OBJECT
+        using StreamObject::input;
     public:
 
         /** Constructor */
@@ -49,16 +50,11 @@ namespace Kwave
         /** does the calculation */
         void goOn() override;
 
-    signals:
-
-        /** emits a block with the filtered data */
-        void output(Kwave::SampleArray data);
+        /** receives input data */
+        void input(Kwave::SampleArray &data) override;
 
     public slots:
 
-        /** receives input data */
-        void input(Kwave::SampleArray data);
-
         /**
          * Sets the speed factor
          * @param speed factor as a double
diff --git a/plugins/playback/PlayBack-Qt.h b/plugins/playback/PlayBack-Qt.h
index 4d1c9ebd..6fa61a52 100644
--- a/plugins/playback/PlayBack-Qt.h
+++ b/plugins/playback/PlayBack-Qt.h
@@ -176,7 +176,7 @@ namespace Kwave
              * @param len number of bytes to read
              * @return number of bytes that have been read
              */
-            virtual qint64 readData(char *data, qint64 len) override;
+            qint64 readData(char *data, qint64 len) override;
 
             /**
              * write data into the buffer, called from our own worker thread
@@ -184,11 +184,10 @@ namespace Kwave
              * @param len number of bytes to write
              * @return number of bytes written
              */
-            virtual qint64 writeData(const char *data, qint64 len)
-                override;
+            qint64 writeData(const char *data, qint64 len) override;
 
             /** returns the number of bytes available for reading */
-            virtual qint64 bytesAvailable() const override;
+            qint64 bytesAvailable() const override;
 
         private:
 
diff --git a/plugins/playback/PlayBackPlugin.cpp b/plugins/playback/PlayBackPlugin.cpp
index 58359a70..8e09b657 100644
--- a/plugins/playback/PlayBackPlugin.cpp
+++ b/plugins/playback/PlayBackPlugin.cpp
@@ -352,19 +352,10 @@ void Kwave::PlayBackPlugin::run(QStringList params)
     //                  ^
     //                  |
     //            osc --'
-
-    Kwave::connect(
-        curve_adapter,    SIGNAL(output(Kwave::SampleArray)),
-        delay,            SLOT(input(Kwave::SampleArray)));
-    Kwave::connect(
-        delay,            SIGNAL(output(Kwave::SampleArray)),
-        mul,              SLOT(input_a(Kwave::SampleArray)));
-    Kwave::connect(
-        osc,              SIGNAL(output(Kwave::SampleArray)),
-        mul,              SLOT(input_b(Kwave::SampleArray)));
-    Kwave::connect(
-        mul,              SIGNAL(output(Kwave::SampleArray)),
-        *m_playback_sink, SLOT(input(Kwave::SampleArray)));
+    Kwave::connect(curve_adapter, delay);
+    Kwave::connect(delay,         mul, 0);
+    Kwave::connect(osc,           mul, 1);
+    Kwave::connect(mul, *m_playback_sink);
 
     // show a progress dialog
 
diff --git a/plugins/samplerate/SampleRatePlugin.cpp b/plugins/samplerate/SampleRatePlugin.cpp
index 9839b963..4c655b39 100644
--- a/plugins/samplerate/SampleRatePlugin.cpp
+++ b/plugins/samplerate/SampleRatePlugin.cpp
@@ -172,15 +172,9 @@ void Kwave::SampleRatePlugin::run(QStringList params)
         first, first + new_length - 1);
 
     // connect the objects
-    bool ok = Kwave::connect(
-        source,    SIGNAL(output(Kwave::SampleArray)),
-        converter, SLOT(input(Kwave::SampleArray)));
-    if (ok) ok = Kwave::connect(
-        converter, SIGNAL(output(Kwave::SampleArray)),
-        sink,      SLOT(input(Kwave::SampleArray)));
-    if (!ok) {
-        return;
-    }
+    bool    ok = Kwave::connect(source,    converter);
+    if (ok) ok = Kwave::connect(converter, sink);
+    if (!ok) return;
 
     while (!shouldStop() && !source.eof()) {
         source.goOn();
diff --git a/plugins/volume/VolumePlugin.cpp b/plugins/volume/VolumePlugin.cpp
index 83be6007..35121960 100644
--- a/plugins/volume/VolumePlugin.cpp
+++ b/plugins/volume/VolumePlugin.cpp
@@ -148,15 +148,11 @@ void Kwave::VolumePlugin::run(QStringList params)
              Qt::BlockingQueuedConnection);
 
     // connect them
-    Kwave::connect(
-        source, SIGNAL(output(Kwave::SampleArray)),
-        mul,    SLOT(input_a(Kwave::SampleArray)));
+    Kwave::connect(source, mul);
 
     mul.setAttribute(SLOT(set_b(QVariant)),
                      QVariant(m_factor));
-    Kwave::connect(
-        mul,    SIGNAL(output(Kwave::SampleArray)),
-        sink,   SLOT(input(Kwave::SampleArray)));
+    Kwave::connect(mul, sink);
 
     // transport the samples
     qDebug("VolumePlugin: filter started...");
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.