kwave  18.07.70
DebugPlugin.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  DebugPlugin.cpp - various debug aids
3  -------------------
4  begin : Mon Feb 02 2009
5  copyright : (C) 2009 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 <errno.h>
21 #include <math.h>
22 #include <string.h>
23 
24 #include <QByteArray>
25 #include <QCoreApplication>
26 #include <QDir>
27 #include <QFile>
28 #include <QList>
29 #include <QPoint>
30 #include <QRect>
31 #include <QScreen>
32 #include <QStringList>
33 #include <QWindow>
34 
35 #include <QApplication>
36 #include <QDesktopWidget>
37 #include <QKeyEvent>
38 #include <QKeySequence>
39 #include <QMouseEvent>
40 #include <QPixmap>
41 #include <QtEvents>
42 
43 #include <KLocalizedString> // for the i18n macro
44 
45 #include "libkwave/Logger.h"
48 #include "libkwave/PluginManager.h"
49 #include "libkwave/SignalManager.h"
50 #include "libkwave/String.h"
51 #include "libkwave/Utils.h"
52 #include "libkwave/Writer.h"
54 
55 #include "libgui/SelectTimeWidget.h" // for selection mode
56 
57 #include "DebugPlugin.h"
58 
59 KWAVE_PLUGIN(debug, DebugPlugin)
60 
61 
62 #define BUFFER_SIZE (64 * 1024)
63 
65 #define MENU_ENTRY(cmd,txt) \
66  emitCommand(entry.arg(_(cmd)).arg(txt));
67 
68 //***************************************************************************
70  const QVariantList &args)
71  :Kwave::Plugin(parent, args), m_buffer()
72 {
73 }
74 
75 //***************************************************************************
77 {
78 }
79 
80 //***************************************************************************
81 void Kwave::DebugPlugin::load(QStringList &params)
82 {
83  Q_UNUSED(params);
84 #ifdef HAVE_DEBUG_PLUGIN
85  QString entry = _("menu(plugin:execute(debug,%1),Calculate/Debug/%2)");
86 
87  MENU_ENTRY("dc_50", _(I18N_NOOP("Generate 50% DC Level")));
88  MENU_ENTRY("dc_100", _(I18N_NOOP("Generate 100% DC Level")));
89  MENU_ENTRY("min_max", _(I18N_NOOP("MinMax Pattern")));
90  MENU_ENTRY("sawtooth", _(I18N_NOOP("Generate Sawtooth Pattern")));
91  MENU_ENTRY("sawtooth_verify", _(I18N_NOOP("Verify Sawtooth Pattern")));
92  MENU_ENTRY("fm_sweep", _(I18N_NOOP("FM Sweep")));
93 // MENU_ENTRY("stripe_index", _(I18N_NOOP("Stripe Index")));
94 // MENU_ENTRY("hull_curve", _(I18N_NOOP("Hull Curve")));
95 // MENU_ENTRY("offset_in_stripe", _(I18N_NOOP("Offset in Stripe")));
96 // MENU_ENTRY("stripe_borders", _(I18N_NOOP("Show Stripe Borders")));
97  MENU_ENTRY("labels_at_stripes", _(I18N_NOOP("Labels at Stripe borders")));
98 
99  entry = _("menu(plugin:setup(debug,%1),Help/%2)");
100  MENU_ENTRY("dump_windows", _(I18N_NOOP("Dump Window Hierarchy")));
101 
102  entry = _("menu(%1,Help/%2)");
103  MENU_ENTRY("dump_metadata()", _(I18N_NOOP("Dump Meta Data")));
104 #endif /* HAVE_DEBUG_PLUGIN */
105 }
106 
107 //***************************************************************************
108 QStringList *Kwave::DebugPlugin::setup(QStringList &params)
109 {
110  if (params.count() < 1) return Q_NULLPTR;
111 
112  QString command = params.first();
113  QString action = i18n("Debug (%1)", command);
114  Kwave::UndoTransactionGuard undo_guard(*this, action);
115 
116 // for (int i = 0; i < params.count()d; ++i)
117 // qDebug("param[%d] = '%s'", i, DBG(params[i]));
118 
119  if (command == _("dump_windows")) {
120  dump_children(parentWidget(), _(""));
121  } else if (command == _("window:click")) {
122  if (params.count() != 4) return Q_NULLPTR;
123  QString class_name = params[1];
124  QWidget *widget = findWidget(class_name.toUtf8().constData());
125  unsigned int x = params[2].toUInt();
126  unsigned int y = params[3].toUInt();
127  if (!widget) return Q_NULLPTR;
128 
129  QMouseEvent *press_event =
130  new QMouseEvent(QEvent::MouseButtonPress,
131  QPoint(x, y),
132  widget->mapToGlobal(QPoint(x,y)),
133  Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
134  QCoreApplication::postEvent(widget, press_event);
135 
136  QMouseEvent *release_event =
137  new QMouseEvent(QEvent::MouseButtonRelease,
138  QPoint(x, y),
139  widget->mapToGlobal(QPoint(x,y)),
140  Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
141  QCoreApplication::postEvent(widget, release_event);
142  } else if (command == _("window:close")) {
143  if (params.count() != 2) return Q_NULLPTR;
144  QString class_name = params[1];
145  QWidget *widget = findWidget(class_name.toUtf8().constData());
146 
147  qDebug("close window '%s' [%p]",
148  DBG(class_name), static_cast<void *>(widget));
149  if (!widget) return Q_NULLPTR;
150  widget->close();
151  } else if (command == _("window:mousemove")) {
152  if (params.count() != 4) return Q_NULLPTR;
153  QString class_name = params[1];
154  QWidget *widget = findWidget(class_name.toUtf8().constData());
155  unsigned int x = params[2].toUInt();
156  unsigned int y = params[3].toUInt();
157  if (!widget) return Q_NULLPTR;
158 
159  QMouseEvent *move_event =
160  new QMouseEvent(QEvent::MouseMove,
161  QPoint(x, y),
162  widget->mapToGlobal(QPoint(x,y)),
163  Qt::NoButton, Qt::NoButton, Qt::NoModifier);
164  QCoreApplication::postEvent(widget, move_event);
165  } else if (command == _("window:resize")) {
166  if (params.count() != 4) return Q_NULLPTR;
167  QString class_name = params[1];
168  QWidget *widget = findWidget(class_name.toUtf8().constData());
169  unsigned int width = params[2].toUInt();
170  unsigned int height = params[3].toUInt();
171  if (!widget) return Q_NULLPTR;
172  widget->resize(width, height);
173  } else if (command == _("window:screenshot")) {
174  if (params.count() != 3) return Q_NULLPTR;
175  screenshot(params[1].toUtf8(), params[2]);
176  } else if (command == _("window:sendkey")) {
177  if (params.count() != 3) return Q_NULLPTR;
178  QString class_name = params[1];
179  QString key_name = params[2];
180  QWidget *widget = findWidget(class_name.toUtf8().constData());
181 
182  unsigned int shortcut = QKeySequence::fromString(key_name)[0];
183  int key_code = shortcut & Qt::Key_unknown;
184  Qt::KeyboardModifiers key_modifiers(shortcut & ~Qt::Key_unknown);
185 
186  qDebug("send key '%s' [0x%08X:0x%08X] to '%s' [%p]",
187  DBG(key_name), static_cast<int>(key_modifiers), key_code,
188  DBG(class_name), static_cast<void *>(widget));
189  if (!widget) return Q_NULLPTR;
190 
191  // make sure that the widget gets the focus
192  widget->activateWindow();
193  widget->raise();
194  widget->setFocus(Qt::OtherFocusReason);
195 
196  QKeyEvent *press_event =
197  new QKeyEvent(QEvent::KeyPress, key_code, key_modifiers);
198  QCoreApplication::postEvent(widget, press_event);
199 
200  QKeyEvent *release_event =
201  new QKeyEvent(QEvent::KeyRelease, key_code, key_modifiers);
202  QCoreApplication::postEvent(widget, release_event);
203 
204  }
205 
206  return new QStringList;
207 }
208 
209 //***************************************************************************
210 void Kwave::DebugPlugin::run(QStringList params)
211 {
212  sample_index_t first = 0;
213  sample_index_t last = 0;
215  const double rate = signalRate();
216 
217  if (params.count() < 1) return;
218 
219  QString command = params.first();
220  QString action = i18n("Debug (%1)", command);
221  Kwave::UndoTransactionGuard undo_guard(*this, action);
222 
223  // get the buffer for faster processing
224  {
225  bool ok = m_buffer.resize(BUFFER_SIZE);
226  Q_ASSERT(ok);
227  if (!ok) return;
228  m_buffer.fill(0);
229  }
230 
231  bool make_new_track = (
232  (command == _("stripe_index")) ||
233  (command == _("offset_in_stripe")) ||
234  (command == _("stripe_borders"))
235  );
236 
237  if (command == _("min_max")) {
238  // toggle between minimum and maximum possible sample value
239  for (unsigned int i = 0; i < BUFFER_SIZE; i++)
240  m_buffer[i] = (i & 1) ? SAMPLE_MIN : SAMPLE_MAX;
241  }
242 
243  if (command == _("labels_at_stripes")) {
244  QList<Kwave::Stripe::List> all_stripes = sig.stripes(sig.allTracks());
245  if (all_stripes.isEmpty()) return;
246 
247  const Kwave::Stripe::List &stripes = all_stripes.first();
248  unsigned int index = 0;
249  foreach (const Kwave::Stripe &stripe, stripes) {
250  QString text;
251  text = text.sprintf("stripe #%d [%llu .. %llu]",
252  index++, stripe.start(), stripe.end());
253  sig.addLabel(stripe.start(), text);
254  }
255  return;
256  } else if (command == _("dump_metadata")) {
257  sig.metaData().dump();
258  return;
259  } else if (command == _("sawtooth_verify")) {
261  Kwave::SinglePassForward, sig, sig.selectedTracks(), first, last);
262  Q_ASSERT(readers);
263  if (!readers) return;
264 
265  sample_index_t pos = first;
266  bool ok = true;
267  while (ok && (first <= last) && (!shouldStop())) {
268  sample_index_t rest = last - first + 1;
269  if (rest < m_buffer.size()) {
270  ok = m_buffer.resize(Kwave::toUint(rest));
271  Q_ASSERT(ok);
272  if (!ok) break;
273  }
274 
275  unsigned int count = readers->tracks();
276  for (unsigned int r = 0; ok && (r < count); ++r) {
277  *((*readers)[r]) >> m_buffer;
278  for (unsigned int ofs = 0; ofs < m_buffer.size(); ++ofs) {
279  sample_t value_is = m_buffer[ofs];
280  sample_t value_should = SAMPLE_MIN + static_cast<sample_t>(
281  ((pos + ofs) % (SAMPLE_MAX - SAMPLE_MIN)));
282  if (value_is != value_should) {
283  qWarning("ERROR: mismatch detected at offset %llu: "
284  "value=%d, expected=%d", pos + ofs,
285  value_is, value_should);
286  ok = false;
287  break;
288  }
289  }
290  if (!ok) break;
291  }
292 
293  pos += m_buffer.size();
294  first += m_buffer.size();
295  }
296  delete readers;
297  if (ok) {
298  qDebug("test pattern successfully detected, no errors :-)");
299  }
300  return;
301  }
302 
303  Kwave::MultiTrackWriter *writers = Q_NULLPTR;
304 
305  if (make_new_track) {
306  // append a new track
307  sig.appendTrack();
308 
309  // and use only the new track as target
310  last = signalLength() - 1;
311  QList<unsigned int> track_list;
312  track_list.append(sig.tracks() - 1);
313  writers = new Kwave::MultiTrackWriter(sig, track_list,
314  Kwave::Overwrite, 0, last);
315  } else {
316  // use all currently selected tracks
317  writers = new Kwave::MultiTrackWriter(sig, Kwave::Overwrite);
318  }
319 
320  Q_ASSERT(writers);
321  if (!writers) return; // out-of-memory
322 
323  // break if aborted
324  if (!writers->tracks()) return;
325 
326  first = (*writers)[0]->first();
327  last = (*writers)[0]->last();
328  unsigned int count = writers->tracks();
329 
330  // connect the progress dialog
331  connect(writers, SIGNAL(progress(qreal)),
332  this, SLOT(updateProgress(qreal)),
333  Qt::BlockingQueuedConnection);
334 
335  // loop over the sample range
336  const sample_index_t left = first;
337  const sample_index_t right = last;
338  const sample_index_t length = right - left + 1;
339  sample_index_t pos = first;
340  while ((first <= last) && (!shouldStop())) {
341  sample_index_t rest = last - first + 1;
342  if (rest < m_buffer.size()) {
343  bool ok = m_buffer.resize(Kwave::toUint(rest));
344  Q_ASSERT(ok);
345  if (!ok) break;
346  }
347 
348  // sawtooth pattern from min to max
349  if (command == _("fm_sweep")) {
350  const double f_max = rate / 2.0;
351  const double f_min = 1;
352  for (unsigned int i = 0; i < m_buffer.size(); ++i, ++pos) {
353  double t = static_cast<double>((pos - left) / rate);
354  double f = f_min + (((f_max - f_min) *
355  static_cast<double>(pos - left)) /
356  static_cast<double>(length));
357  double y = 0.707 * sin(M_PI * f * t);
358  m_buffer[i] = double2sample(y);
359  }
360  } else if (command == _("sawtooth")) {
361  for (unsigned int i = 0; i < m_buffer.size(); ++i, ++pos) {
362  m_buffer[i] = SAMPLE_MIN +
363  static_cast<sample_t>(pos % (SAMPLE_MAX - SAMPLE_MIN));
364  }
365  } else if (command == _("dc_50")) {
366  const sample_t s = float2sample(0.5);
367  for (unsigned int i = 0; i < m_buffer.size(); ++i) {
368  m_buffer[i] = s;
369  }
370  } else if (command == _("dc_100")) {
371  const sample_t s = SAMPLE_MAX;
372  for (unsigned int i = 0; i < m_buffer.size(); ++i) {
373  m_buffer[i] = s;
374  }
375  }
376 
377  // loop over all writers
378  for (unsigned int w = 0; w < count; ++w) {
379  *((*writers)[w]) << m_buffer;
380  }
381 
382  first += m_buffer.size();
383  }
384 
385  delete writers;
386 }
387 
388 //***************************************************************************
389 void Kwave::DebugPlugin::dump_children(const QObject *obj,
390  const QString &indent) const
391 {
392  if (!obj) return;
393  const char *classname = obj->metaObject()->className();
394  qDebug("%s - %p [%s]",
395  DBG(indent),
396  static_cast<const void *>(obj),
397  classname
398  );
399 
400  foreach (QObject *o, obj->children()) {
401  dump_children(o, indent + _("| "));
402  }
403 }
404 
405 //***************************************************************************
406 QWidget *Kwave::DebugPlugin::findWidget(const char *class_name) const
407 {
408  QObject *obj = findObject(parentWidget(), class_name);
409  if (!obj) return Q_NULLPTR;
410  return qobject_cast<QWidget *>(obj);
411 }
412 
413 //***************************************************************************
414 QObject *Kwave::DebugPlugin::findObject(QObject *obj,
415  const char *class_name) const
416 {
417  if (!obj) return Q_NULLPTR;
418  const char *obj_class_name = obj->metaObject()->className();
419  if (strcmp(class_name, obj_class_name) == 0)
420  return obj;
421 
422  foreach (QObject *o, obj->children()) {
423  QObject *result = findObject(o, class_name);
424  if (result) return result; // first match -> found
425  }
426 
427  return Q_NULLPTR; // nothing found
428 }
429 
430 //***************************************************************************
431 void Kwave::DebugPlugin::screenshot(const QByteArray &class_name,
432  const QString &filename)
433 {
434  // find the first widget/window with the given class name
435  QWidget *widget = findWidget(class_name.constData());
436  qDebug("screenshot of '%s' [%p] -> '%s'",
437  class_name.constData(),
438  static_cast<void *>(widget),
439  DBG(filename)
440  );
441  if (!widget) return;
442 
443  // get the outer frame geometry, absolute coordinates
444  const QRect rect = widget->windowHandle()->frameGeometry();
445  QScreen *screen = QGuiApplication::primaryScreen();
446  Q_ASSERT(screen);
447  if (!screen) return;
448  QPixmap pixmap = screen->grabWindow(
449  QApplication::desktop()->winId(),
450  rect.x(), rect.y(),
451  rect.width(), rect.height()
452  );
453 
454  QString str;
455  str = str.sprintf("screenshot of %s - [%p] %d/%d %dx%d",
456  DBG(filename), static_cast<void*>(widget),
457  rect.x(), rect.y(), rect.width(), rect.height()
458  );
459  Kwave::Logger::log(this, Logger::Info, str);
460 
461  // make sure the directory exists
462  QFileInfo file(filename);
463  QDir dir = file.absoluteDir();
464  if (!dir.exists()) dir.mkpath(dir.absolutePath());
465 
466  // save the file
467  pixmap.save(filename, "PNG", 90);
468 }
469 
470 //***************************************************************************
471 #include "DebugPlugin.moc"
472 //***************************************************************************
473 //***************************************************************************
virtual unsigned int tracks() const Q_DECL_OVERRIDE
Kwave::SampleArray m_buffer
Definition: DebugPlugin.h:111
Definition: App.h:33
void dump_children(const QObject *obj, const QString &indent) const
QWidget * parentWidget() const
Definition: Plugin.cpp:450
virtual unsigned int tracks() const Q_DECL_OVERRIDE
Kwave::MetaDataList & metaData()
Kwave::SignalManager & signalManager()
Definition: Plugin.cpp:444
#define SAMPLE_MIN
Definition: Sample.h:49
virtual void dump() const
unsigned int tracks()
quint64 sample_index_t
Definition: Sample.h:28
DebugPlugin(QObject *parent, const QVariantList &args)
Definition: DebugPlugin.cpp:69
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
sample_index_t start() const
Definition: Stripe.cpp:262
virtual void run(QStringList params) Q_DECL_OVERRIDE
virtual QStringList * setup(QStringList &params) Q_DECL_OVERRIDE
sample_index_t end() const
Definition: Stripe.cpp:282
#define BUFFER_SIZE
Definition: DebugPlugin.cpp:62
virtual sample_index_t signalLength()
Definition: Plugin.cpp:462
const QList< unsigned int > allTracks()
QWidget * findWidget(const char *class_name) const
Kwave::Label addLabel(sample_index_t pos, const QString &name)
virtual double signalRate()
Definition: Plugin.cpp:468
static sample_t double2sample(const double f)
Definition: Sample.h:81
#define KWAVE_PLUGIN(name, class)
Definition: Plugin.h:54
void screenshot(const QByteArray &class_name, const QString &filename)
void fill(sample_t value)
Definition: SampleArray.cpp:68
#define _(m)
Definition: memcpy.c:66
#define SAMPLE_MAX
Definition: Sample.h:52
#define MENU_ENTRY(cmd, txt)
Definition: DebugPlugin.cpp:65
virtual void load(QStringList &params) Q_DECL_OVERRIDE
Definition: DebugPlugin.cpp:81
unsigned int size() const
#define DBG(qs)
Definition: String.h:55
virtual void updateProgress(qreal progress)
Definition: Plugin.cpp:260
const QList< unsigned int > selectedTracks()
unsigned int toUint(T x)
Definition: Utils.h:109
static void Q_DECL_EXPORT log(const QObject *sender, LogLevel level, const QString &msg)
Definition: Logger.cpp:103
QObject * findObject(QObject *obj, const char *class_name) const
static sample_t float2sample(const float f)
Definition: Sample.h:57
virtual ~DebugPlugin() Q_DECL_OVERRIDE
Definition: DebugPlugin.cpp:76
bool resize(unsigned int size) Q_REQUIRED_RESULT
static double rect(double param)
Definition: Functions.cpp:29
bool shouldStop() const
Definition: Plugin.h:120
QList< Kwave::Stripe::List > stripes(const QList< unsigned int > &track_list, sample_index_t left=0, sample_index_t right=SAMPLE_INDEX_MAX)
qint32 sample_t
Definition: Sample.h:37