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