kwave  18.07.70
NormalizePlugin.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  NormalizePlugin.cpp - plugin for level normalizing
3  -------------------
4  begin : Fri May 01 2009
5  copyright : (C) 2009 by Thomas Eschenbacher
6  email : Thomas.Eschenbacher@gmx.de
7 
8  original algorithms : (C) 1999-2005 Chris Vaill <chrisvaill at gmail>
9  taken from "normalize-0.7.7"
10  ***************************************************************************/
11 
12 /***************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  ***************************************************************************/
20 
21 #include "config.h"
22 
23 #include <math.h>
24 #include <new>
25 
26 #include <QFutureSynchronizer>
27 #include <QList>
28 #include <QStringList>
29 #include <QThread>
30 #include <QtConcurrentRun>
31 
32 #include <KLocalizedString> // for the i18n macro
33 
34 #include "libkwave/Connect.h"
35 #include "libkwave/FileInfo.h"
38 #include "libkwave/PluginManager.h"
39 #include "libkwave/SignalManager.h"
40 #include "libkwave/Utils.h"
41 #include "libkwave/Writer.h"
43 
44 #include "NormalizePlugin.h"
45 #include "Normalizer.h"
46 
48 #define SMOOTHLEN 100
49 
51 #define TARGET_LEVEL -12
52 
53 KWAVE_PLUGIN(normalize, NormalizePlugin)
54 
55 //***************************************************************************
57  const QVariantList &args)
58  :Kwave::Plugin(parent, args)
59 {
60 }
61 
62 //***************************************************************************
64 {
65 }
66 
67 //***************************************************************************
68 void Kwave::NormalizePlugin::run(QStringList params)
69 {
70  Q_UNUSED(params);
71  Kwave::UndoTransactionGuard undo_guard(*this, i18n("Normalize"));
72 
73  // get the current selection
74  QList<unsigned int> tracks;
75  sample_index_t first = 0;
76  sample_index_t last = 0;
77  sample_index_t length = selection(&tracks, &first, &last, true);
78  if (!length || tracks.isEmpty()) return;
79 
80  // get the current volume level
81  double level = 0.0;
82  {
84  signalManager(), tracks, first, last);
85 
86  // connect the progress dialog
87  connect(&src, SIGNAL(progress(qreal)),
88  this, SLOT(updateProgress(qreal)),
89  Qt::BlockingQueuedConnection);
90 
91  // detect the peak value
92  emit setProgressText(i18n("Analyzing volume level..."));
93 // qDebug("NormalizePlugin: getting peak...");
94  level = getMaxPower(src);
95 // qDebug("NormalizePlugin: level is %g", level);
96  }
97 
99  signalManager(), tracks, first, last);
101  first, last);
103  tracks.count(), this);
104 
105  // break if aborted
106  if (!sink.tracks()) return;
107 
108  // connect the progress dialog
109  connect(&source, SIGNAL(progress(qreal)),
110  this, SLOT(updateProgress(qreal)),
111  Qt::BlockingQueuedConnection);
112 
113  // connect them
114  bool ok = true;
115  if (ok) ok = Kwave::connect(
116  source, SIGNAL(output(Kwave::SampleArray)),
117  normalizer, SLOT(input(Kwave::SampleArray)));
118  if (ok) ok = Kwave::connect(
119  normalizer, SIGNAL(output(Kwave::SampleArray)),
120  sink, SLOT(input(Kwave::SampleArray)));
121  if (!ok) {
122  return;
123  }
124 
125  double target = pow(10.0, (TARGET_LEVEL / 20.0));
126  double gain = target / level;
127  qDebug("NormalizePlugin: gain=%g", gain);
128 
129  QString db;
130  emit setProgressText(i18n("Normalizing (%1 dB) ...",
131  db.sprintf("%+0.1f", 20 * log10(gain))));
132 
133  normalizer.setAttribute(SLOT(setGain(QVariant)), QVariant(gain));
134  while (!shouldStop() && !source.eof()) {
135  source.goOn();
136  }
137 
138  sink.flush();
139 }
140 
141 //***************************************************************************
143 {
144  double maxpow = 0.0;
145  const unsigned int tracks = source.tracks();
146  const double rate = Kwave::FileInfo(signalManager().metaData()).rate();
147  const unsigned int window_size = Kwave::toUint(rate / 100);
148  if (!window_size) return 0;
149 
150  // set up smoothing window buffer
151  QVector<Kwave::NormalizePlugin::Average> average(tracks);
152  for (unsigned int t = 0; t < tracks; t++) {
153  average[t].fifo = QVector<double>(SMOOTHLEN, double(0.0));
154  average[t].wp = 0;
155  average[t].n = 0;
156  average[t].sum = 0.0;
157  average[t].max = 0.0;
158  }
159 
160  while (!shouldStop() && !source.eof()) {
161  QFutureSynchronizer<void> synchronizer;
162 
163  for (unsigned int t = 0; t < tracks; t++) {
164  Kwave::SampleReader *reader = source[t];
165  if (!reader) continue;
166  if (reader->eof()) continue;
167 
168  synchronizer.addFuture(QtConcurrent::run(
169  this,
171  reader, &(average[t]), window_size
172  ));
173  }
174  synchronizer.waitForFinished();
175  }
176 
177  if (average[0].n < SMOOTHLEN) {
178  // if file was too short, calculate power out of what we have
179  for (unsigned int t = 0; t < tracks; t++) {
180  Kwave::NormalizePlugin::Average &avg = average[t];
181  double pow = avg.sum / static_cast<double>(avg.n);
182  if (pow > maxpow) maxpow = pow;
183  }
184  } else {
185  // get maximum among all tracks
186  for (unsigned int t = 0; t < tracks; t++) {
187  double p = average[t].max;
188  if (p > maxpow) maxpow = p;
189  }
190  }
191 
192  double level = sqrt(maxpow);
193  return level;
194 }
195 
196 //***************************************************************************
198  Kwave::SampleReader *reader,
200  unsigned int window_size)
201 {
202  Kwave::NormalizePlugin::Average &average = *p_average;
203  Kwave::SampleArray data(window_size);
204  unsigned int round = 0;
205  unsigned int loops = 5 * reader->blockSize() / window_size;
206  loops++;
207 
208  while ((round++ < loops) && !reader->eof()) {
209  unsigned int len = reader->read(data, 0, window_size);
210 
211  // calculate power of one block
212  double sum = 0;
213  const Kwave::SampleArray &in = data;
214  for (unsigned int i = 0; i < len; i++) {
215  sample_t s = in[i];
216  double d = sample2double(s);
217  sum += (d * d);
218  }
219  double pow = sum / static_cast<double>(len);
220 
221  // collect all power values in a FIFO
222  unsigned int wp = average.wp;
223  average.sum -= average.fifo[wp];
224  average.sum += pow;
225  average.fifo[wp] = pow;
226  if (++wp >= SMOOTHLEN) wp = 0;
227  average.wp = wp;
228  if (average.n == SMOOTHLEN) {
229  // detect power peak
230  double p = average.sum / static_cast<double>(SMOOTHLEN);
231  if (p > average.max) average.max = p;
232  } else {
233  average.n++;
234  }
235  }
236 // qDebug("%p -> pos=%llu, max=%g", this, reader->pos(), average.max);
237 }
238 
239 //***************************************************************************
240 #include "NormalizePlugin.moc"
241 //***************************************************************************
242 //***************************************************************************
virtual unsigned int tracks() const Q_DECL_OVERRIDE
Definition: App.h:33
double rate() const
Definition: FileInfo.cpp:415
virtual unsigned int tracks() const Q_DECL_OVERRIDE
Kwave::SignalManager & signalManager()
Definition: Plugin.cpp:444
quint64 sample_index_t
Definition: Sample.h:28
void getMaxPowerOfTrack(Kwave::SampleReader *reader, Kwave::NormalizePlugin::Average *average, unsigned int window_size)
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
bool eof() const
Definition: SampleReader.h:66
virtual void goOn() Q_DECL_OVERRIDE
virtual bool eof() const
virtual void flush()
virtual ~NormalizePlugin() Q_DECL_OVERRIDE
#define SMOOTHLEN
static double sample2double(const sample_t s)
Definition: Sample.h:73
#define TARGET_LEVEL
virtual void run(QStringList params) Q_DECL_OVERRIDE
double getMaxPower(Kwave::MultiTrackReader &source)
virtual unsigned int blockSize() const
#define KWAVE_PLUGIN(name, class)
Definition: Plugin.h:54
unsigned int read(Kwave::SampleArray &buffer, unsigned int dstoff, unsigned int length)
virtual void updateProgress(qreal progress)
Definition: Plugin.cpp:260
unsigned int toUint(T x)
Definition: Utils.h:109
void setProgressText(const QString &text)
bool shouldStop() const
Definition: Plugin.h:120
qint32 sample_t
Definition: Sample.h:37
virtual sample_index_t selection(QList< unsigned int > *tracks=Q_NULLPTR, sample_index_t *left=Q_NULLPTR, sample_index_t *right=Q_NULLPTR, bool expand_if_empty=false)
Definition: Plugin.cpp:480