1 /*************************************************************************
2 ChannelMixer.cpp - matrix based mixer for multiple channels
3 -------------------
4 begin : Sun Oct 10 2010
5 copyright : (C) 2010 by Thomas Eschenbacher
6 email : Thomas.Eschenbacher@gmx.de
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "config.h"
19
20 #include <limits>
21 #include <new>
22
23 #include <QtGlobal>
24 #include <QByteArray>
25 #include <QMetaObject>
26 #include <QMutexLocker>
27 #include <QObject>
28 #include <QVarLengthArray>
29
30 #include "libkwave/MixerMatrix.h"
31 #include "libkwave/Sample.h"
32 #include "libkwave/Utils.h"
33 #include "libkwave/modules/ChannelMixer.h"
34 #include "libkwave/modules/Indexer.h"
35 #include "libkwave/modules/StreamObject.h"
36
37 //***************************************************************************
ChannelMixer(unsigned int inputs,unsigned int outputs)38 Kwave::ChannelMixer::ChannelMixer(unsigned int inputs, unsigned int outputs)
39 :Kwave::SampleSource(),
40 m_matrix(Q_NULLPTR),
41 m_inputs(inputs),
42 m_outputs(outputs),
43 m_indexer(),
44 m_input_queue(),
45 m_output_buffer(),
46 m_lock()
47 {
48 }
49
50 //***************************************************************************
init()51 bool Kwave::ChannelMixer::init()
52 {
53 if (!m_inputs || !m_outputs) return false;
54
55 // create queues for the input data
56 m_input_queue.resize(m_inputs);
57 Q_ASSERT(m_input_queue.count() == Kwave::toInt(m_inputs));
58 if (m_input_queue.count() != Kwave::toInt(m_inputs)) return false;
59
60 // create the buffers for the output data
61 for (unsigned int index = 0; index < m_outputs; ++index) {
62 // create a buffer for the input
63 Kwave::SampleBuffer *out_buffer =
64 new(std::nothrow) Kwave::SampleBuffer();
65 Q_ASSERT(out_buffer);
66 if (!out_buffer) return false;
67 m_output_buffer.append(out_buffer);
68 }
69
70 // create indexing proxies and connect their output to this mixer
71 for (unsigned int index = 0; index < m_inputs; ++index) {
72 Kwave::StreamObject *indexer =
73 new(std::nothrow) Kwave::Indexer(index);
74 Q_ASSERT(indexer);
75 if (!indexer) return false;
76
77 m_indexer.append(indexer);
78 bool ok = Kwave::connect(
79 *indexer, SIGNAL(output(uint,Kwave::SampleArray)),
80 *this, SLOT(idxInput(uint,Kwave::SampleArray)));
81 Q_ASSERT(ok);
82 if (!ok) return false;
83 }
84
85 // create the mixer matrix
86 // create a translation matrix for mixing up/down to the desired
87 // number of output channels
88 m_matrix = new(std::nothrow) Kwave::MixerMatrix(m_inputs, m_outputs);
89 Q_ASSERT(m_matrix);
90 if (!m_matrix) return false;
91
92 // everything succeeded
93 return true;
94 }
95
96 //***************************************************************************
~ChannelMixer()97 Kwave::ChannelMixer::~ChannelMixer()
98 {
99 QMutexLocker _lock(&m_lock);
100
101 while (!m_indexer.isEmpty()) {
102 Kwave::StreamObject *indexer = m_indexer[0];
103 if (indexer) delete indexer;
104 m_indexer.remove(0);
105 }
106
107 m_input_queue.clear();
108
109 while (!m_output_buffer.isEmpty()) {
110 Kwave::SampleBuffer *buffer = m_output_buffer[0];
111 if (buffer) delete buffer;
112 m_output_buffer.remove(0);
113 }
114 }
115
116 //***************************************************************************
_sig(const char * sig)117 static inline QByteArray _sig(const char *sig)
118 {
119 return QMetaObject::normalizedSignature(sig);
120 }
121
122 //***************************************************************************
tracksOfPort(const char * port) const123 unsigned int Kwave::ChannelMixer::tracksOfPort(const char *port) const
124 {
125 unsigned int retval = 0;
126 QMutexLocker _lock(const_cast<QMutex *>(&m_lock));
127
128 if (_sig(port) == _sig(SLOT(input(Kwave::SampleArray)))) {
129 // input ports
130 retval = m_inputs; // init is done
131 } else if (_sig(port) == _sig(SIGNAL(output(Kwave::SampleArray)))) {
132 // output ports
133 retval = m_outputs;
134 } else if (_sig(port) ==
135 _sig(SLOT(idxInput(uint,Kwave::SampleArray)))) {
136 retval = 1;
137 } else {
138 qFatal("unknown port");
139 }
140
141 return retval;
142 }
143
144 //***************************************************************************
port(const char * port,unsigned int track)145 Kwave::StreamObject *Kwave::ChannelMixer::port(const char *port,
146 unsigned int track)
147 {
148 Kwave::StreamObject *retval = Q_NULLPTR;
149 QMutexLocker _lock(&m_lock);
150
151 if (_sig(port) == _sig(SLOT(input(Kwave::SampleArray)))) {
152 // input proxy
153 Q_ASSERT(Kwave::toInt(track) < m_indexer.count());
154 if (Kwave::toInt(track) >= m_indexer.count()) return Q_NULLPTR;
155 retval = m_indexer.at(track);
156 } else if (_sig(port) == _sig(SIGNAL(output(Kwave::SampleArray)))) {
157 // output proxy
158 Q_ASSERT(Kwave::toInt(track) < m_output_buffer.count());
159 if (Kwave::toInt(track) >= m_output_buffer.count()) return Q_NULLPTR;
160 retval = m_output_buffer[track];
161 } else if (_sig(port) ==
162 _sig(SLOT(idxInput(uint,Kwave::SampleArray)))) {
163 retval = this;
164 } else {
165 qFatal("unknown port");
166 }
167
168 return retval;
169 }
170
171 //***************************************************************************
idxInput(unsigned int index,Kwave::SampleArray data)172 void Kwave::ChannelMixer::idxInput(unsigned int index, Kwave::SampleArray data)
173 {
174 QMutexLocker _lock(&m_lock);
175
176 // put the data into the corresponding input queue
177 Q_ASSERT(index < m_inputs);
178 Q_ASSERT(Kwave::toInt(index) < m_input_queue.count());
179 if (Kwave::toInt(index) < m_input_queue.count())
180 m_input_queue[index].enqueue(data);
181
182 // check: if there is one empty queue we are not yet ready for mixing
183 bool ready = true;
184 foreach (const QQueue<Kwave::SampleArray> &queue, m_input_queue) {
185 if (queue.isEmpty()) {
186 ready = false;
187 break;
188 }
189 }
190
191 // mix if we are ready
192 if (ready && m_matrix) mix();
193 }
194
195 //***************************************************************************
mix()196 void Kwave::ChannelMixer::mix()
197 {
198 Q_ASSERT(m_matrix);
199
200 // all inputs should contain a buffer, dequeue them into a vector
201 // and form an array of pointers to the raw data, for speeding up
202 QVector<Kwave::SampleArray> v_input(m_inputs);
203 QVarLengthArray<const sample_t *> input(m_inputs);
204 unsigned int min_len = std::numeric_limits<unsigned int>::max();
205 for (unsigned int track = 0; track < m_inputs; track++) {
206 // dequeue the buffer with input data
207 QQueue<Kwave::SampleArray> &queue = m_input_queue[track];
208 Q_ASSERT(!queue.isEmpty());
209 Kwave::SampleArray buffer = queue.dequeue();
210 v_input[track] = buffer;
211
212 // get a pointer for quick access
213 const sample_t *raw_data = v_input[track].constData();
214 input[track] = raw_data;
215
216 // detect minimum input length
217 min_len = qMin(min_len, buffer.size());
218 }
219 Q_ASSERT(min_len);
220 if (!min_len) return; // zero length buffer in the queue, data underrun?
221
222 // make sure all output buffers are large enough
223 // and build an array of pointers to the raw data, for speeding up
224 QVarLengthArray<sample_t *> output(m_outputs);
225 for (unsigned int track = 0; track < m_outputs; track++) {
226 Kwave::SampleBuffer *buffer = m_output_buffer[track];
227 Q_ASSERT(buffer);
228 if (!buffer) return;
229 bool ok = true;
230 if (buffer->constData().size() < min_len)
231 ok &= buffer->data().resize(min_len);
232 if (!ok) {
233 qWarning("ChannelMixer: failed to increase buffer size to %u",
234 min_len);
235 return; // OOM ?
236 }
237 output[track] = buffer->data().data();
238 }
239
240 // mix all channels together, using the mixer matrix
241 for (unsigned int y = 0; y < m_outputs; y++) {
242 sample_t *out = output[y];
243 for (unsigned int pos = 0; pos < min_len; pos++) {
244 double sum = 0.0;
245 for (unsigned int x = 0; x < m_inputs; x++) {
246 const double f = (*m_matrix)[x][y];
247 const double i = static_cast<double>(input[x][pos]);
248 sum += (f * i);
249 }
250 out[pos] = static_cast<sample_t>(sum);
251 }
252
253 // emit the output
254 Kwave::SampleBuffer *out_buf = m_output_buffer[y];
255 if (Q_UNLIKELY(out_buf->constData().size() > min_len)) {
256 bool ok = out_buf->data().resize(min_len);
257 Q_ASSERT(ok);
258 Q_UNUSED(ok)
259 }
260 out_buf->finished();
261 }
262
263 }
264
265 //***************************************************************************
266 //***************************************************************************
267