kwave  18.07.70
RecordPlugin.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  RecordPlugin.cpp - plugin for recording audio data
3  -------------------
4  begin : Wed Jul 09 2003
5  copyright : (C) 2003 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 <new>
23 #include <stdlib.h>
24 
25 #include <QApplication>
26 #include <QCursor>
27 #include <QDateTime>
28 #include <QList>
29 #include <QStringList>
30 #include <QVariant>
31 #include <QtGlobal>
32 
33 #include <KAboutData>
34 #include <KConfig>
35 #include <KConfigGroup>
36 #include <KSharedConfig>
37 #include <kxmlgui_version.h>
38 
39 #include "libkwave/Compression.h"
40 #include "libkwave/FileInfo.h"
41 #include "libkwave/InsertMode.h"
42 #include "libkwave/MessageBox.h"
43 #include "libkwave/PluginManager.h"
44 #include "libkwave/Sample.h"
45 #include "libkwave/SampleFIFO.h"
46 #include "libkwave/SampleFormat.h"
47 #include "libkwave/SignalManager.h"
48 #include "libkwave/String.h"
49 #include "libkwave/Utils.h"
50 #include "libkwave/Writer.h"
51 
52 #include "Record-ALSA.h"
53 #include "Record-OSS.h"
54 #include "Record-PulseAudio.h"
55 #include "Record-Qt.h"
56 #include "RecordDevice.h"
57 #include "RecordDialog.h"
58 #include "RecordPlugin.h"
59 #include "RecordThread.h"
60 #include "SampleDecoderLinear.h"
61 
62 KWAVE_PLUGIN(record, RecordPlugin)
63 
64 #define OPEN_RETRY_TIME 1000
66 //***************************************************************************
67 Kwave::RecordPlugin::RecordPlugin(QObject *parent, const QVariantList &args)
68  :Kwave::Plugin(parent, args),
69  m_method(Kwave::RECORD_NONE),
70  m_device_name(),
71  m_controller(),
72  m_state(Kwave::REC_EMPTY),
73  m_device(Q_NULLPTR),
74  m_dialog(Q_NULLPTR),
75  m_thread(Q_NULLPTR),
76  m_decoder(Q_NULLPTR),
77  m_prerecording_queue(),
78  m_writers(Q_NULLPTR),
79  m_buffers_recorded(0),
80  m_inhibit_count(0),
81  m_trigger_value(),
82  m_retry_timer()
83 {
84  m_retry_timer.setSingleShot(true);
85  connect(&m_retry_timer, SIGNAL(timeout()),
86  this, SLOT(retryOpen()),
87  Qt::QueuedConnection
88  );
89 }
90 
91 //***************************************************************************
93 {
94  Q_ASSERT(!m_dialog);
95  if (m_dialog) delete m_dialog;
96  m_dialog = Q_NULLPTR;
97 
98  Q_ASSERT(!m_thread);
99  if (m_thread) delete m_thread;
100  m_thread = Q_NULLPTR;
101 
102  Q_ASSERT(!m_decoder);
103  if (m_decoder) delete m_decoder;
104  m_decoder = Q_NULLPTR;
105 
106  if (m_device) delete m_device;
107  m_device = Q_NULLPTR;
108 }
109 
110 //***************************************************************************
111 QStringList *Kwave::RecordPlugin::setup(QStringList &previous_params)
112 {
114 
115  qDebug("RecordPlugin::setup(%s)", DBG(previous_params.join(_(","))));;
116 
117  // if we have only one parameter, then we got called with a specific
118  // mode, e.g. "show format settings only"
119  if (previous_params.count() == 1) {
120  const QString m = previous_params[0].toLower();
121 
122  if (m == _("format"))
124  else if (m == _("source"))
126  else if (m == _("start_now"))
128 
129  // get previous parameters for the setup dialog
130  previous_params = manager().defaultParams(name());
131  qDebug("RecordPlugin::setup(%s) - MODE=%d",
132  DBG(previous_params.join(_(","))), static_cast<int>(mode));
133  }
134 
135  // create the setup dialog
136  m_dialog = new Kwave::RecordDialog(parentWidget(), previous_params,
137  &m_controller, mode);
138  Q_ASSERT(m_dialog);
139  if (!m_dialog) return Q_NULLPTR;
140 
141  // create the lowlevel recording thread
143  Q_ASSERT(m_thread);
144  if (!m_thread) {
145  delete m_dialog;
146  m_dialog = Q_NULLPTR;
147  return Q_NULLPTR;
148  }
149 
150  // connect some signals of the setup dialog
151  connect(m_dialog, SIGNAL(sigMethodChanged(Kwave::record_method_t)),
152  this, SLOT(setMethod(Kwave::record_method_t)));
153  connect(m_dialog, SIGNAL(sigDeviceChanged(QString)),
154  this, SLOT(setDevice(QString)));
155 
156  connect(m_dialog, SIGNAL(sigTracksChanged(uint)),
157  this, SLOT(changeTracks(uint)));
158  connect(m_dialog, SIGNAL(sampleRateChanged(double)),
159  this, SLOT(changeSampleRate(double)));
160  connect(m_dialog, SIGNAL(sigCompressionChanged(Kwave::Compression::Type)),
162  connect(m_dialog, SIGNAL(sigBitsPerSampleChanged(uint)),
163  this, SLOT(changeBitsPerSample(uint)));
164  connect(m_dialog,
165  SIGNAL(sigSampleFormatChanged(Kwave::SampleFormat::Format)),
166  this,
168  connect(m_dialog, SIGNAL(sigBuffersChanged()),
169  this, SLOT(buffersChanged()));
171  m_dialog, SLOT(setRecordedSamples(sample_index_t)));
172 
173  connect(m_dialog, SIGNAL(sigTriggerChanged(bool)),
174  &m_controller, SLOT(enableTrigger(bool)));
176  m_dialog->params().record_trigger_enabled ||
177  m_dialog->params().start_time_enabled
178  );
179 
180  connect(m_dialog, SIGNAL(sigPreRecordingChanged(bool)),
181  &m_controller, SLOT(enablePrerecording(bool)));
182  connect(m_dialog, SIGNAL(sigPreRecordingChanged(bool)),
183  this, SLOT(prerecordingChanged(bool)));
184  m_controller.enablePrerecording(m_dialog->params().pre_record_enabled);
185 
186  // connect the record controller and this
187  connect(&m_controller, SIGNAL(sigReset(bool&)),
188  this, SLOT(resetRecording(bool&)));
189  connect(&m_controller, SIGNAL(sigStartRecord()),
190  this, SLOT(startRecording()));
191  connect(&m_controller, SIGNAL(sigStopRecord(int)),
192  &m_controller, SLOT(deviceRecordStopped(int)));
194  this, SLOT(stateChanged(Kwave::RecordState)));
195 
196  // connect record controller and record thread
197  connect(m_thread, SIGNAL(stopped(int)),
198  &m_controller, SLOT(deviceRecordStopped(int)));
199 
200  // connect us to the record thread
201  connect(m_thread, SIGNAL(stopped(int)),
202  this, SLOT(recordStopped(int)));
203  connect(m_thread, SIGNAL(bufferFull()),
204  this, SLOT(processBuffer()),
205  Qt::QueuedConnection);
206 
207  // dummy init -> disable format settings
208  m_dialog->setSupportedTracks(0, 0);
209 
210  // activate the recording method
211  setMethod(m_dialog->params().method);
212 
213  // directly start recording if requested
216 
217  QStringList *list = new QStringList();
218  Q_ASSERT(list);
219  if (list && (m_dialog->exec() == QDialog::Accepted)) {
220  // user has pressed "OK"
221  *list = m_dialog->params().toList();
222  } else {
223  // user pressed "Cancel"
224  if (list) delete list;
225  list = Q_NULLPTR;
226  }
227 
228  /* de-queue all buffers that are pending and remove the record thread */
229  if (m_thread) {
230  m_thread->stop();
231  while (m_thread->queuedBuffers())
232  processBuffer();
233  delete m_thread;
234  m_thread = Q_NULLPTR;
235  }
236 
237  if (m_decoder) delete m_decoder;
238  m_decoder = Q_NULLPTR;
239 
240  delete m_dialog;
241  m_dialog = Q_NULLPTR;
242 
243  // flush away all prerecording buffers
244  m_prerecording_queue.clear();
245 
246  // enable undo again if we recorded something
247  if (!signalManager().isEmpty())
249 
250  return list;
251 }
252 
253 //***************************************************************************
254 void Kwave::RecordPlugin::notice(QString message)
255 {
256  Q_ASSERT(m_dialog);
257  if (m_dialog) m_dialog->message(message);
258 }
259 
260 //***************************************************************************
262 {
263  if (m_retry_timer.isActive()) m_retry_timer.stop();
264 
265  if (m_device) {
266  m_device->close();
267  delete m_device;
268  m_device = Q_NULLPTR;
269  }
270 }
271 
272 //***************************************************************************
274 {
275  Q_ASSERT(m_dialog);
276  if (!m_dialog) return;
277 
278  InhibitRecordGuard _lock(*this); // don't record while settings change
279  qDebug("RecordPlugin::setMethod(%d)", static_cast<int>(method));
280 
281  // change the recording method (class RecordDevice)
282  if ((method != m_method) || !m_device) {
283  if (m_device) delete m_device;
284  m_device = Q_NULLPTR;
285  bool searching = false;
286 
287  // use the previous device
288  QString section = _("plugin ") + name();
289  KConfigGroup cfg = KSharedConfig::openConfig()->group(section);
290 
291  // restore the previous device
292  QString device = cfg.readEntry(
293  _("last_device_%1").arg(static_cast<int>(method)));
294 // qDebug("<<< %d -> '%s'", static_cast<int>(method), device.data());
295  m_device_name = device;
296 
297  do {
298  switch (method) {
299 #ifdef HAVE_OSS_SUPPORT
300  case Kwave::RECORD_OSS:
301  m_device = new Kwave::RecordOSS();
302  Q_ASSERT(m_device);
303  break;
304 #endif /* HAVE_OSS_SUPPORT */
305 
306 #ifdef HAVE_ALSA_SUPPORT
307  case Kwave::RECORD_ALSA:
308  m_device = new Kwave::RecordALSA();
309  Q_ASSERT(m_device);
310  break;
311 #endif /* HAVE_ALSA_SUPPORT */
312 
313 #ifdef HAVE_PULSEAUDIO_SUPPORT
316  Q_ASSERT(m_device);
317  break;
318 #endif /* HAVE_PULSEAUDIO_SUPPORT */
319 
320 #ifdef HAVE_QT_AUDIO_SUPPORT
321  case Kwave::RECORD_QT:
322  m_device = new Kwave::RecordQt();
323  Q_ASSERT(m_device);
324  break;
325 #endif /* HAVE_QT_AUDIO_SUPPORT */
326  default:
327  qDebug("unsupported recording method (%d)",
328  static_cast<int>(method));
329  if (!searching) {
330  // start trying all other methods
331  searching = true;
332  method = Kwave::RECORD_NONE;
333  ++method;
334  continue;
335  } else {
336  // try next method
337  ++method;
338  }
339  qDebug("unsupported recording method - trying next (%d)",
340  static_cast<int>(method));
341  if (method != Kwave::RECORD_INVALID) continue;
342  }
343  break;
344  } while (true);
345  }
346  Q_ASSERT(m_device);
347 
348  // if we found no recording method
349  if (method == Kwave::RECORD_INVALID) {
350  qWarning("found no valid recording method");
351  }
352 
353  // take the change in the method
354  m_method = method;
355 
356  // activate the cange in the dialog
357  m_dialog->setMethod(method);
358 
359  // set list of supported devices
360  QStringList supported_devices;
361  Q_ASSERT(m_device);
362  if (m_device) supported_devices = m_device->supportedDevices();
363  m_dialog->setSupportedDevices(supported_devices);
364 
365  // set current device (again), no matter if supported or not,
366  // the dialog will take care of this.
368 
369  // check the filter for the "select..." dialog. If it is
370  // empty, the "select" dialog will be disabled
371  QString file_filter;
372  if (m_device) file_filter = m_device->fileFilter();
373  m_dialog->setFileFilter(file_filter);
374 }
375 
376 //***************************************************************************
378 {
379  qDebug("RecordPlugin::retryOpen()");
381 }
382 
383 //***************************************************************************
384 void Kwave::RecordPlugin::setDevice(const QString &device)
385 {
386  Q_ASSERT(m_dialog);
387  Q_ASSERT(m_device);
388  if (!m_dialog || !m_device) return;
389 
390  InhibitRecordGuard _lock(*this); // don't record while settings change
391  qDebug("RecordPlugin::setDevice('%s')", DBG(device));
392 
393  if (m_retry_timer.isActive()) m_retry_timer.stop();
394 
395  // select the default device if this one is not supported
396  QString dev = device;
397  QStringList supported = m_device->supportedDevices();
398  if (!supported.isEmpty() && !supported.contains(device)) {
399  // use the first entry as default
400  dev = supported.first();
401  qDebug("RecordPlugin::setDevice(%s) -> fallback to '%s'",
402  DBG(device), DBG(dev));
403  }
404 
405  // if there was no valid device name, fall back to default device
406  if (dev.startsWith(_("#"))) {
407  dev = _("/dev/dsp");
408  qDebug("RecordPlugin::setDevice(%s) -> no valid device, using '%s'",
409  DBG(device), DBG(dev));
410  }
411 
412  // open and initialize the device
413  QString result = m_device->open(dev);
414 
415  // set the device in the dialog
416  m_device_name = dev;
417  m_dialog->setDevice(dev);
418 
419  // remember the device selection, just for the GUI
420  // for the next change in the method
421  QString section = _("plugin ") + name();
422  KConfigGroup cfg = KSharedConfig::openConfig()->group(section);
423  cfg.writeEntry(_("last_device_%1").arg(
424  static_cast<int>(m_method)), m_device_name);
425 // qDebug(">>> %d -> '%s'", static_cast<int>(m_method), DBG(m_device_name));
426  cfg.sync();
427 
428  if (!result.isNull()) {
429  bool shouldRetry = false;
430 
431  qWarning("RecordPlugin::setDevice('%s'): "
432  "opening the device failed. error message='%s'",
433  DBG(device), DBG(result));
434 
436 
437  if (m_device_name.length()) {
438  // build a short device name for showing to the user
439  QString short_device_name = m_device_name;
440  if (m_device_name.contains(_("|"))) {
441  // tree syntax: extract card + device
442  short_device_name = m_device_name.section(_("|"), 0, 0);
443  if (m_device_name.section(_("|"), 3, 3).length())
444  short_device_name += _(", ") +
445  m_device_name.section(_("|"), 3, 3);
446  }
447 
448  bool errIsNumeric = false;
449  int errNumber = result.toInt(&errIsNumeric);
450  if (errIsNumeric) {
451  if (errNumber == ENODEV) {
452  result = i18n(
453  "Maybe your system lacks support for the "\
454  "corresponding hardware or the hardware is not "\
455  "connected."
456  );
457  } else if (errNumber == EBUSY) {
458  result = i18n(
459  "The audio device seems to be occupied by another "\
460  "application. Retrying..."
461  );
462  shouldRetry = true;
463  } else {
464  result = i18n(
465  "Some unexpected error happened (%1). "\
466  "You may try an other recording method or "\
467  "recording device.",
468  QString::fromLocal8Bit(strerror(errNumber))
469  );
470  }
471  }
472 
473  if (result.length()) {
474  if (shouldRetry) {
475  notice(result);
476  } else {
479  result, i18nc("%1 = a device name",
480  "Unable to open the recording device (%1)",
481  short_device_name));
482  }
483  }
484  }
485 
486  if (shouldRetry) {
487  // retry later...
489  } else {
490  m_device_name = QString();
491  changeTracks(0);
492  }
493  } else {
495  }
496 
497  if (paramsValid()) {
499  } else {
500  qDebug("RecordPlugin::setDevice('%s') failed, "
501  "returning to 'UNINITIALIZED'", DBG(device));
503  }
504 
505 }
506 
507 //***************************************************************************
508 void Kwave::RecordPlugin::changeTracks(unsigned int new_tracks)
509 {
510  Q_ASSERT(m_dialog);
511  if (!m_dialog) return;
512 
513  InhibitRecordGuard _lock(*this); // don't record while settings change
514 // qDebug("RecordPlugin::changeTracks(%u)", new_tracks);
515 
516  if (!m_device || m_device_name.isNull()) {
517  // no device -> dummy/shortcut
519  m_dialog->setTracks(0);
520  changeSampleRate(0);
521  return;
522  }
523 
524  // check the supported tracks
525  unsigned int min = 0;
526  unsigned int max = 0;
527  if ((m_device->detectTracks(min, max) < 0) || (max < 1))
528  min = max = 0;
529  if (min > max) min = max;
530 
531  unsigned int channels = new_tracks;
532  if ((channels < min) || (channels > max)) {
533  // clip to the supported number of tracks
534  if (channels < min) channels = min;
535  if (channels > max) channels = max;
536  qDebug("RecordPlugin::changeTracks(%u) -> clipped to %u",
537  new_tracks, channels);
538 
539  if ((new_tracks && channels) && (new_tracks != channels)) {
540  QString s1;
541  switch (new_tracks) {
542  case 1: s1 = i18n("Mono"); break;
543  case 2: s1 = i18n("Stereo"); break;
544  case 4: s1 = i18n("Quadro"); break;
545  default:
546  s1 = i18n("%1 channels", new_tracks);
547  }
548  QString s2;
549  switch (channels) {
550  case 1: s2 = i18n("Mono"); break;
551  case 2: s2 = i18n("Stereo"); break;
552  case 4: s2 = i18n("Quadro"); break;
553  default:
554  s2 = i18n("%1 channels", channels);
555  }
556 
557  notice(i18n("%1 is not supported, using %2", s1, s2));
558  }
559  }
560  Q_ASSERT(channels >= min);
561  Q_ASSERT(channels <= max);
562  m_dialog->setSupportedTracks(min, max);
563 
564  // try to activate the new number of tracks
565  int err = m_device->setTracks(channels);
566  if (err < 0) {
567  // revert to the current device setting if failed
568  int t = m_device->tracks();
569  if (t > 0) {
570  // current device state seems to be valid
571  channels = t;
572  if (channels < min) channels = min;
573  if (channels > max) channels = max;
574  } else {
575  // current device state is invalid
576  channels = 0;
577  }
578 
579  if (new_tracks && (channels > 0)) notice(
580  i18n("Recording with %1 channel(s) failed, "\
581  "using %2 channel(s)", new_tracks, channels));
582  }
583  m_dialog->setTracks(channels);
584 
585  // activate the new sample rate
587 }
588 
589 //***************************************************************************
591 {
592  Q_ASSERT(m_dialog);
593  if (!m_dialog) return;
594 
595  InhibitRecordGuard _lock(*this); // don't record while settings change
596 // qDebug("RecordPlugin::changeSampleRate(%u)", Kwave::toInt(new_rate));
597 
598  if (!m_device || m_device_name.isNull()) {
599  // no device -> dummy/shortcut
602  return;
603  }
604 
605  // check the supported sample rates
606  QList<double> supported_rates = m_device->detectSampleRates();
607  bool is_supported = false;
608  foreach (const double &r, supported_rates)
609  if (qFuzzyCompare(new_rate, r)) { is_supported = true; break; }
610  double rate = new_rate;
611  if (!is_supported && !supported_rates.isEmpty()) {
612  // find the nearest sample rate
613  double nearest = supported_rates.last();
614  foreach (double r, supported_rates) {
615  if (fabs(r - rate) <= fabs(nearest - rate))
616  nearest = r;
617  }
618  rate = nearest;
619 
620  const QString sr1(m_dialog->rate2string(new_rate));
621  const QString sr2(m_dialog->rate2string(rate));
622  if ((Kwave::toInt(new_rate) > 0) &&
623  (Kwave::toInt(rate) > 0) &&
624  (Kwave::toInt(new_rate) != Kwave::toInt(rate)))
625  notice(i18n("%1 Hz is not supported, "\
626  "using %2 Hz", sr1, sr2));
627  }
628  m_dialog->setSupportedSampleRates(supported_rates);
629 
630  // try to activate the new sample rate
631  int err = m_device->setSampleRate(rate);
632  if (err < 0) {
633  // revert to the current device setting if failed
634  rate = m_device->sampleRate();
635  if (rate < 0) rate = 0;
636 
637  const QString sr1(m_dialog->rate2string(new_rate));
638  const QString sr2(m_dialog->rate2string(rate));
639  if ((Kwave::toInt(new_rate) > 0) &&
640  (Kwave::toInt(rate) > 0) &&
641  (Kwave::toInt(new_rate) != Kwave::toInt(rate)))
642  notice(i18n("%1 Hz failed, using %2 Hz", sr1, sr2));
643  }
644  m_dialog->setSampleRate(rate);
645 
646  // set the compression again
648 }
649 
650 //***************************************************************************
652  Kwave::Compression::Type new_compression
653 )
654 {
655  Q_ASSERT(m_dialog);
656  if (!m_dialog) return;
657 
658  InhibitRecordGuard _lock(*this); // don't record while settings change
659 // qDebug("RecordPlugin::changeCompression(%d)", new_compression);
660 
661  if (!m_device || m_device_name.isNull()) {
662  // no device -> dummy/shortcut
665  return;
666  }
667 
668  // check the supported compressions
669  QList<Kwave::Compression::Type> supported_comps =
671  Kwave::Compression::Type compression = new_compression;
672  if (!supported_comps.contains(compression) &&
673  (compression != Kwave::Compression::NONE))
674  {
675  // try to disable the compression (type 0)
676  compression = Kwave::Compression::NONE;
677  if (!supported_comps.isEmpty() &&
678  !supported_comps.contains(compression))
679  {
680  // what now, "None" is not supported
681  // -> take the first supported one
682  compression = supported_comps[0];
683  }
684 
685  if (compression != new_compression) {
686  const QString c1(Kwave::Compression(new_compression).name());
687  const QString c2(Kwave::Compression(compression).name());
688  notice(i18n("Compression '%1' not supported, using '%2'", c1, c2));
689  }
690  }
691  m_dialog->setSupportedCompressions(supported_comps);
692 
693  // try to activate the new compression
694  int err = m_device->setCompression(compression);
695  if (err < 0) {
696  // revert to the current device setting if failed
697  if (compression != m_device->compression()) {
698  const QString c1(Kwave::Compression(compression).name());
699  const QString c2(Kwave::Compression(m_device->compression()).name());
700  notice(i18n("Compression '%1' failed, using '%2'.", c1 ,c2));
701  }
702  compression = m_device->compression();
703  }
704  m_dialog->setCompression(compression);
705 
706  // set the resolution in bits per sample again
708 }
709 
710 //***************************************************************************
711 void Kwave::RecordPlugin::changeBitsPerSample(unsigned int new_bits)
712 {
713  Q_ASSERT(m_dialog);
714  if (!m_dialog) return;
715 
716  InhibitRecordGuard _lock(*this); // don't record while settings change
717 // qDebug("RecordPlugin::changeBitsPerSample(%d)", new_bits);
718 
719  if (!m_device || m_device_name.isNull()) {
720  // no device -> dummy/shortcut
723  return;
724  }
725 
726  // check the supported resolution in bits per sample
727  QList<unsigned int> supported_bits = m_device->supportedBits();
728  int bits = new_bits;
729  if (!supported_bits.contains(bits) && !supported_bits.isEmpty()) {
730  // find the nearest resolution
731  int nearest = supported_bits.last();
732  foreach (unsigned int b, supported_bits) {
733  if (qAbs(Kwave::toInt(b) - nearest) <= qAbs(bits - nearest))
734  nearest = Kwave::toInt(b);
735  }
736  bits = nearest;
737 
738  if ((Kwave::toInt(new_bits) > 0) && (bits > 0)) notice(
739  i18n("%1 bits per sample is not supported, "\
740  "using %2 bits per sample",
741  Kwave::toInt(new_bits), bits));
742  }
743  m_dialog->setSupportedBits(supported_bits);
744 
745  // try to activate the resolution
746  int err = m_device->setBitsPerSample(bits);
747  if (err < 0) {
748  // revert to the current device setting if failed
749  bits = m_device->bitsPerSample();
750  if (bits < 0) bits = 0;
751  if ((new_bits > 0) && (bits > 0)) notice(
752  i18n("%1 bits per sample failed, "
753  "using %2 bits per sample",
754  Kwave::toInt(new_bits), bits));
755  }
756  m_dialog->setBitsPerSample(bits);
757 
758  // set the sample format again
760 }
761 
762 //***************************************************************************
764  Kwave::SampleFormat::Format new_format)
765 {
766  Q_ASSERT(m_dialog);
767  if (!m_dialog) return;
768 
769  InhibitRecordGuard _lock(*this); // don't record while settings change
770 
771  if (!m_device || m_device_name.isNull()) {
772  // no device -> dummy/shortcut
774  return;
775  }
776 
777  // check the supported sample formats
778  QList<Kwave::SampleFormat::Format> supported_formats =
780  Kwave::SampleFormat::Format format = new_format;
781  if (!supported_formats.contains(format) && !supported_formats.isEmpty()) {
782  // use the device default instead
783  format = m_device->sampleFormat();
784 
785  // if this was also not supported -> stupid device !?
786  if (!supported_formats.contains(format)) {
787  format = supported_formats.first(); // just take the first one :-o
788  }
789 
791  const QString s1 = sf.description(sf.findFromData(new_format), true);
792  const QString s2 = sf.description(sf.findFromData(format), true);
793  if (!(new_format == -1) && !(new_format == format)) {
794  notice(i18n("Sample format '%1' is not supported, "\
795  "using '%2'", s1, s2));
796  }
797  }
798  m_dialog->setSupportedSampleFormats(supported_formats);
799 
800  // try to activate the new format
801  int err = m_device->setSampleFormat(format);
802  if (err < 0) {
803  // use the device default instead
804  format = m_device->sampleFormat();
805 
807  const QString s1 = sf.description(sf.findFromData(new_format), true);
808  const QString s2 = sf.description(sf.findFromData(format), true);
809  if (format > 0) notice(
810  i18n("Sample format '%1' failed, using '%2'", s1, s2));
811  }
812  m_dialog->setSampleFormat(format);
813 }
814 
815 //***************************************************************************
817 {
818  InhibitRecordGuard _lock(*this); // don't record while settings change
819  // this implicitly activates the new settings
820 }
821 
822 //***************************************************************************
824 {
825  m_inhibit_count++;
826  if ((m_inhibit_count == 1) && m_thread) {
827  // set hourglass cursor
828  QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
829 
830 // qDebug("RecordPlugin::enterInhibit() - STOPPING");
831  m_thread->stop();
832  Q_ASSERT(!m_thread->isRunning());
833 
834  // de-queue all buffers that are still in the queue
835  while (m_thread->queuedBuffers())
836  processBuffer();
837  }
838 }
839 
840 //***************************************************************************
842 {
843  Q_ASSERT(m_inhibit_count);
844  Q_ASSERT(m_dialog);
845 
847 
848  while (!m_inhibit_count && paramsValid()) {
849 // qDebug("RecordPlugin::leaveInhibit() - STARTING ("
850 // "%d channels, %d bits)",
851 // m_dialog->params().tracks,
852 // m_dialog->params().bits_per_sample);
853 
854  Q_ASSERT(!m_thread->isRunning());
855  if (m_thread->isRunning()) break;
856 
857  // set new parameters for the recorder
859 
860  // and let the thread run (again)
861  m_thread->start();
862  break;
863  }
864 
865  // take back the hourglass cursor
866  if (!m_inhibit_count) QApplication::restoreOverrideCursor();
867 }
868 
869 //***************************************************************************
871 {
872  if (!m_thread || !m_device || !m_dialog) return false;
873 
874  // check for a valid/usable record device
875  if (m_device_name.isNull()) return false;
878  return false;
879  if (m_device->bitsPerSample() < 1) return false;
880  if (m_device->endianness() == Kwave::UnknownEndian) return false;
881 
882  // check for valid parameters in the dialog
883  const Kwave::RecordParams &params = m_dialog->params();
884  if (params.tracks < 1) return false;
885  if ( (params.sample_format != Kwave::SampleFormat::Unsigned) &&
886  (params.sample_format != Kwave::SampleFormat::Signed) ) return false;
887 
888  return true;
889 }
890 
891 //***************************************************************************
893 {
894  InhibitRecordGuard _lock(*this);
895 
896  if (m_writers) m_writers->clear();
897 
898  emitCommand(_("nomacro:close()"));
899  QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
900  accepted = manager().signalManager().isEmpty();
901  if (!accepted) return;
902 
903  // the parent context might have changed, maybe we have to
904  // re-parent this plugin instance!
906 
907  m_buffers_recorded = 0;
908 
909  m_controller.setEmpty(true);
910  emit sigRecordedSamples(0);
911 }
912 
913 //***************************************************************************
915 {
916  Q_ASSERT(m_thread);
917  Q_ASSERT(m_dialog);
918  Q_ASSERT(m_device);
919  if (!paramsValid()) return;
920 
921  // stop the thread if necessary (should never happen)
922  Q_ASSERT(!m_thread->isRunning());
923  if (m_thread->isRunning()) m_thread->stop();
924  Q_ASSERT(!m_thread->isRunning());
925 
926  // delete the previous decoder
927  if (m_decoder) delete m_decoder;
928  m_decoder = Q_NULLPTR;
929 
930  // our own reference to the record parameters
931  const Kwave::RecordParams &params = m_dialog->params();
932  if (!paramsValid()) return;
933 
934  // create a decoder for the current sample format
935  switch (params.compression) {
937  switch (params.sample_format) {
938  case Kwave::SampleFormat::Unsigned: /* FALLTHROUGH */
940  // decoder for all linear formats
945  );
946  break;
947  default:
948  notice(
949  i18n("The current sample format is not supported!")
950  );
951  }
952  break;
953  default:
954  notice(
955  i18n("The current compression type is not supported!")
956  );
957  return;
958  }
959 
960  Q_ASSERT(m_decoder);
961  if (!m_decoder) {
962  Kwave::MessageBox::sorry(m_dialog, i18n("Out of memory"));
963  return;
964  }
965 
966  // set up the prerecording queues
967  m_prerecording_queue.clear();
968  if (params.pre_record_enabled) {
969  // prepare a queue for each track
970  const unsigned int prerecording_samples = Kwave::toUint(
971  rint(params.pre_record_time * params.sample_rate));
972  m_prerecording_queue.resize(params.tracks);
973  for (int i=0; i < m_prerecording_queue.size(); i++)
974  m_prerecording_queue[i].setSize(prerecording_samples);
975 
976  if (m_prerecording_queue.size() != Kwave::toInt(params.tracks)) {
977  m_prerecording_queue.clear();
978  Kwave::MessageBox::sorry(m_dialog, i18n("Out of memory"));
979  return;
980  }
981  }
982 
983  // set up the recording trigger values
984  m_trigger_value.resize(params.tracks);
985  m_trigger_value.fill(0.0);
986 
987  // set up the record thread
989  unsigned int buf_count = params.buffer_count;
990  unsigned int buf_size = params.tracks *
992  (1 << params.buffer_size);
993  m_thread->setBuffers(buf_count, buf_size);
994 }
995 
996 //***************************************************************************
998 {
999  Q_ASSERT(m_dialog);
1000  Q_ASSERT(m_thread);
1001  Q_ASSERT(m_device);
1002  if (!m_dialog || !m_thread || !m_device) return;
1003 
1004  InhibitRecordGuard _lock(*this); // don't record while settings change
1005 
1006  if ((m_state != Kwave::REC_PAUSED) || !m_decoder) {
1007  double rate = m_dialog->params().sample_rate;
1008  unsigned int tracks = m_dialog->params().tracks;
1009  unsigned int bits = m_dialog->params().bits_per_sample;
1010 
1011  if (!tracks) return;
1012 
1013  /*
1014  * if tracks or sample rate has changed
1015  * -> start over with a new signal and new settings
1016  */
1017  if ((!m_writers) ||
1018  (m_writers->tracks() != tracks) || !qFuzzyCompare(
1019  Kwave::FileInfo(signalManager().metaData()).rate(), rate))
1020  {
1021  // create a new and empty signal
1022  emitCommand(QString(_("newsignal(0,%1,%2,%3)")).arg(
1023  rate).arg(bits).arg(tracks));
1024  QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1025 
1026  // the parent context might have changed, maybe we have to
1027  // re-parent this plugin instance!
1029 
1031  if (!qFuzzyCompare(mgr.rate(), rate) || (mgr.bits() != bits) ||
1032  (mgr.tracks() != tracks))
1033  {
1034  emitCommand(_("close"));
1035  return;
1036  }
1037 
1038  // we do not need undo while recording, this would only waste undo
1039  // buffers with modified/inserted data
1041 
1042  // create a sink for our audio data
1043  if (m_writers) delete m_writers;
1044  m_writers = new(std::nothrow) Kwave::MultiTrackWriter(
1046  if ((!m_writers) || (m_writers->tracks() != tracks)) {
1047  Kwave::MessageBox::sorry(m_dialog, i18n("Out of memory"));
1048  return;
1049  }
1050  } else {
1051  // re-use the current signal and append to it
1052  }
1053 
1054  // initialize the file information
1055  Kwave::FileInfo fileInfo(signalManager().metaData());
1056  fileInfo.setRate(rate);
1057  fileInfo.setBits(bits);
1058  fileInfo.setTracks(tracks);
1059  fileInfo.set(Kwave::INF_MIMETYPE, _("audio/vnd.wave"));
1060  fileInfo.set(Kwave::INF_SAMPLE_FORMAT,
1063 
1064  // add our Kwave Software tag
1065  const KAboutData about_data = KAboutData::applicationData();
1066  QString software = about_data.componentName() + _("-") +
1067  about_data.version() + _(" ") +
1068  i18n("(built with KDE Frameworks %1)",
1069  _(KXMLGUI_VERSION_STRING));
1070  fileInfo.set(Kwave::INF_SOFTWARE, software);
1071 
1072  // add a date tag, ISO format
1073  QDate now(QDate::currentDate());
1074  QString date;
1075  date = date.sprintf("%04d-%02d-%02d",
1076  now.year(), now.month(), now.day());
1077  fileInfo.set(Kwave::INF_CREATION_DATE, date);
1078  signalManager().setFileInfo(fileInfo, false);
1079  }
1080 
1081  // now the recording can be considered to be started
1083 }
1084 
1085 //***************************************************************************
1087 {
1088  qDebug("RecordPlugin::recordStopped(%d)", reason);
1089  if (reason >= 0) return; // nothing to do
1090 
1091  // recording was aborted
1092  QString err_msg;
1093  switch (reason) {
1094  case -ENOBUFS:
1095  err_msg = i18n("Buffer overrun. Please increase the "\
1096  "number and/or size of the record buffers.");
1097  break;
1098  case -EBUSY:
1099  err_msg = i18n("The recording device seems to be busy.");
1100  break;
1101  default:
1102  err_msg = i18n("Reading from the recording device failed. "\
1103  "Error number = %1 (%2)", -reason,
1104  QString::fromLocal8Bit(strerror(-reason)));
1105  }
1107 
1108  if (m_writers) m_writers->flush();
1109  qDebug("RecordPlugin::recordStopped(): last=%lu",
1110  static_cast<unsigned long int>(
1111  (m_writers) ? m_writers->last() : 0));
1112 
1113  // flush away all prerecording buffers
1114  m_prerecording_queue.clear();
1115 
1116  // update the file info if we recorded something
1117  // NOTE: this implicitly sets the "modified" flag of the signal
1118  if (m_writers && m_writers->last()) {
1119  Kwave::FileInfo info(signalManager().metaData());
1120  info.setLength(signalLength());
1121  info.setTracks(m_dialog->params().tracks);
1122  signalManager().setFileInfo(info, false);
1123  }
1124 
1125 }
1126 
1127 //***************************************************************************
1129 {
1130  m_state = state;
1131  switch (m_state) {
1133  case Kwave::REC_EMPTY:
1134  case Kwave::REC_PAUSED:
1135  case Kwave::REC_DONE:
1136  // reset buffer status
1137  if (m_writers) {
1138  m_writers->flush();
1139  delete m_writers;
1140  m_writers = Q_NULLPTR;
1141  }
1142  m_buffers_recorded = 0;
1143  m_dialog->updateBufferState(0, 0);
1144  break;
1145  default:
1146  ;
1147  }
1148 }
1149 
1150 //***************************************************************************
1152 {
1153  Q_ASSERT(m_dialog);
1154  Q_ASSERT(m_thread);
1155  if (!m_dialog || !m_thread) return;
1156 
1157  unsigned int buffers_total = m_dialog->params().buffer_count;
1158 
1159  // if we are still recording: update the progress bar
1161  (m_state != Kwave::REC_DONE))
1162  {
1163  // count up the number of recorded buffers
1165 
1166  if (m_buffers_recorded <= buffers_total) {
1167  // buffers are just in progress of getting filled
1169  } else {
1170  // we have remaining+1 buffers (one is currently filled)
1171  unsigned int remaining = m_thread->remainingBuffers() + 1;
1172  if (remaining > buffers_total) remaining = buffers_total;
1173  m_dialog->updateBufferState(remaining, buffers_total);
1174  }
1175  } else {
1176  // no longer recording: count the buffer downwards
1177  unsigned int queued = m_thread->queuedBuffers();
1178  if (!queued) buffers_total = 0;
1179  m_dialog->updateBufferState(queued, buffers_total);
1180  }
1181 }
1182 
1183 //***************************************************************************
1184 void Kwave::RecordPlugin::split(QByteArray &raw_data, QByteArray &dest,
1185  unsigned int bytes_per_sample,
1186  unsigned int track,
1187  unsigned int tracks)
1188 {
1189  unsigned int samples = raw_data.size() / bytes_per_sample / tracks;
1190 
1191 #if 0
1192  // simple sawtooth generator, based on raw data
1193  // works for up to 16 channels
1194  static int saw[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1195  raw_data.fill(0x05);
1196  for (unsigned int s = 0; s < samples; s++) {
1197  int v = saw[track];
1198  for (unsigned int byte = 0; byte < bytes_per_sample; byte++) {
1199  quint8 x = (quint8)v;
1200  raw_data[(((s * tracks) + track) * bytes_per_sample) + byte] = x;
1201  v >>= 8;
1202  }
1203 
1204  const int max = (1 << ((bytes_per_sample * 8) - 1)) - 1;
1205  saw[track] += max / 64;
1206  if (saw[track] >= max) saw[track] = 0;
1207  }
1208 #endif
1209 
1210  if (tracks == 1) {
1211  // this would give a 1:1 memcpy
1212  dest = raw_data;
1213  } else {
1214  switch (bytes_per_sample) {
1215  case 1: {
1216  // 1...8 bits per sample, use 8 bit pointers
1217  const quint8 *src =
1218  reinterpret_cast<const quint8 *>(raw_data.constData());
1219  quint8 *dst =
1220  reinterpret_cast<quint8 *>(dest.data());
1221  src += track;
1222  while (samples) {
1223  *dst = *src;
1224  dst++;
1225  src += tracks;
1226  samples--;
1227  }
1228  break;
1229  }
1230  case 2: {
1231  // 9...16 bits per sample, use 16 bit pointers
1232  const quint16 *src =
1233  reinterpret_cast<const quint16 *>(raw_data.constData());
1234  quint16 *dst =
1235  reinterpret_cast<quint16 *>(dest.data());
1236  src += track;
1237  while (samples) {
1238  *dst = *src;
1239  dst++;
1240  src += tracks;
1241  samples--;
1242  }
1243  break;
1244  }
1245  case 3: {
1246  // 17...24 bits per sample, use 8 bit pointers, three times
1247  const quint8 *src =
1248  reinterpret_cast<const quint8 *>(raw_data.constData());
1249  quint8 *dst =
1250  reinterpret_cast<quint8 *>(dest.data());
1251  src += track * 3;
1252  while (samples) {
1253  *(dst++) = *(src++);
1254  *(dst++) = *(src++);
1255  *(dst++) = *(src++);
1256  src += (tracks - 1) * 3;
1257  samples--;
1258  }
1259  break;
1260  }
1261  case 4: {
1262  // 24...32 bits per sample, use 32 bit pointers
1263  const quint32 *src =
1264  reinterpret_cast<const quint32 *>(raw_data.constData());
1265  quint32 *dst =
1266  reinterpret_cast<quint32 *>(dest.data());
1267  src += track;
1268  while (samples) {
1269  *dst = *src;
1270  dst++;
1271  src += tracks;
1272  samples--;
1273  }
1274  break;
1275  }
1276  case 8: {
1277  // 64 bits per sample, use 64 bit pointers
1278  const quint64 *src =
1279  reinterpret_cast<const quint64 *>(raw_data.constData());
1280  quint64 *dst =
1281  reinterpret_cast<quint64 *>(dest.data());
1282  src += track;
1283  while (samples) {
1284  *dst = *src;
1285  dst++;
1286  src += tracks;
1287  samples--;
1288  }
1289  break;
1290  }
1291  default: {
1292  // default: byte wise operation
1293  const quint8 *src =
1294  reinterpret_cast<const quint8 *>(raw_data.constData());
1295  quint8 *dst =
1296  reinterpret_cast<quint8 *>(dest.data());
1297  src += (track * bytes_per_sample);
1298  unsigned int increment = (tracks - 1) * bytes_per_sample;
1299  while (samples) {
1300  for (unsigned int b = 0; b < bytes_per_sample; b++) {
1301  *dst = *src;
1302  dst++;
1303  src++;
1304  samples--;
1305  }
1306  src += increment;
1307  }
1308  }
1309  }
1310  }
1311 }
1312 
1313 //***************************************************************************
1314 bool Kwave::RecordPlugin::checkTrigger(unsigned int track,
1315  const Kwave::SampleArray &buffer)
1316 {
1317  Q_ASSERT(m_dialog);
1318  if (!m_dialog) return false;
1319 
1320  // check if the recording start time has been reached
1322  if (QDateTime::currentDateTime() < m_dialog->params().start_time)
1323  return false;
1324  }
1325 
1326  // shortcut if no trigger has been set
1327  if (!m_dialog->params().record_trigger_enabled) return true;
1328 
1329  // check the input parameters
1330  if (!buffer.size()) return false;
1331  if (!m_writers) return false;
1332  if (m_trigger_value.size() != Kwave::toInt(m_writers->tracks()))
1333  return false;
1334 
1335  // pass the buffer through a rectifier and a lowpass with
1336  // center frequency about 2Hz to get the amplitude
1337  float trigger = static_cast<float>(
1338  m_dialog->params().record_trigger / 100.0);
1339  const float rate = static_cast<const float>(
1341 
1342  /*
1343  * simple lowpass calculation:
1344  *
1345  * 1 + z
1346  * H(z) = a0 * ----------- | z = e ^ (j*2*pi*f)
1347  * z + b1
1348  *
1349  * 1 1 - n
1350  * a0 = ----- b1 = --------
1351  * 1 + n 1 + n
1352  *
1353  * Fg = fg / fa
1354  *
1355  * n = cot(Pi * Fg)
1356  *
1357  * y[t] = a0 * x[t] + a1 * x[t-1] - b1 * y[t-1]
1358  *
1359  */
1360 
1361  // rise coefficient: ~20Hz
1362  const float f_rise = 20.0f;
1363  float Fg = f_rise / rate;
1364  float n = 1.0f / tanf(float(M_PI) * Fg);
1365  const float a0_r = 1.0f / (1.0f + n);
1366  const float b1_r = (1.0f - n) / (1.0f + n);
1367 
1368  // fall coefficient: ~1.0Hz
1369  const float f_fall = 1.0f;
1370  Fg = f_fall / rate;
1371  n = 1.0f / tanf(float(M_PI) * Fg);
1372  const float a0_f = 1.0f / (1.0f + n);
1373  const float b1_f = (1.0f - n) / (1.0f + n);
1374 
1375  float y = m_trigger_value[track];
1376  float last_x = y;
1377  for (unsigned int t = 0; t < buffer.size(); ++t) {
1378  float x = fabsf(sample2float(buffer[t])); /* rectifier */
1379 
1380  if (x > y) { /* diode */
1381  // rise if amplitude is above average (serial R)
1382  y = (a0_r * x) + (a0_r * last_x) - (b1_r * y);
1383  }
1384 
1385  // fall (parallel R)
1386  y = (a0_f * x) + (a0_f * last_x) - (b1_f * y);
1387 
1388  // remember x[t-1]
1389  last_x = x;
1390 
1391 // nice for debugging:
1392 // buffer[t] = (int)((double)(1 << (SAMPLE_BITS-1)) * y);
1393  if (y > trigger) return true;
1394  }
1395  m_trigger_value[track] = y;
1396 
1397  qDebug(">> level=%5.3g, trigger=%5.3g", y, trigger);
1398 
1399  return false;
1400 }
1401 
1402 //***************************************************************************
1404  const Kwave::SampleArray &decoded)
1405 {
1406  Q_ASSERT(m_dialog);
1407  Q_ASSERT(Kwave::toInt(track) < m_prerecording_queue.size());
1408  if (!m_dialog) return;
1409  if (Kwave::toInt(track) >= m_prerecording_queue.size()) return;
1410 
1411  // append the array with decoded sample to the prerecording buffer
1412  m_prerecording_queue[track].put(decoded);
1413 }
1414 
1415 //***************************************************************************
1417 {
1418  if (!m_prerecording_queue.size()) return;
1419  Q_ASSERT(m_dialog);
1420  Q_ASSERT(m_thread);
1421  Q_ASSERT(m_decoder);
1422  if (!m_dialog || !m_thread || !m_decoder) return;
1423 
1424  const Kwave::RecordParams &params = m_dialog->params();
1425  const unsigned int tracks = params.tracks;
1426  Q_ASSERT(tracks);
1427  if (!tracks) return;
1428  Q_ASSERT(m_writers);
1429  if (!m_writers) return;
1430  Q_ASSERT(tracks == m_writers->tracks());
1431  if (!tracks || (tracks != m_writers->tracks())) return;
1432 
1433  for (unsigned int track=0; track < tracks; ++track) {
1434  Kwave::SampleFIFO &fifo = m_prerecording_queue[track];
1435  Q_ASSERT(fifo.length());
1436  if (!fifo.length()) continue;
1437  fifo.crop(); // enforce the correct size
1438 
1439  // push all buffers to the writer, starting at the tail
1440  Kwave::Writer *writer = (*m_writers)[track];
1441  Q_ASSERT(writer);
1442  if (writer) {
1443  Kwave::SampleArray buffer(writer->blockSize());
1444  unsigned int rest = fifo.length();
1445  while (rest) {
1446  unsigned int read = fifo.get(buffer);
1447  if (read < 1) break;
1448  writer->write(buffer, read);
1449  rest -= read;
1450  }
1451  } else {
1452  // fallback: discard the FIFO content
1453  fifo.flush();
1454  }
1455  Q_ASSERT(fifo.length() == 0);
1456  }
1457 
1458  // the queues are no longer needed
1459  m_prerecording_queue.clear();
1460 
1461  // we have transferred data to the writers, we are no longer empty
1462  m_controller.setEmpty(false);
1463 }
1464 
1465 //***************************************************************************
1467 {
1468  bool recording_done = false;
1469 
1470  // de-queue the buffer from the thread
1471  if (!m_thread) return;
1472  if (!m_thread->queuedBuffers()) return;
1473  QByteArray buffer = m_thread->dequeue();
1474 
1475  // abort here if we have no dialog or no decoder
1476  if (!m_dialog || !m_decoder) return;
1477 
1478  // we received a buffer -> update the progress bar
1480 
1481  const Kwave::RecordParams &params = m_dialog->params();
1482  const unsigned int tracks = params.tracks;
1483  Q_ASSERT(tracks);
1484  if (!tracks) return;
1485 
1486  const unsigned int bytes_per_sample = m_decoder->rawBytesPerSample();
1487  Q_ASSERT(bytes_per_sample);
1488  if (!bytes_per_sample) return;
1489 
1490  unsigned int samples = (buffer.size() / bytes_per_sample) / tracks;
1491  Q_ASSERT(samples);
1492  if (!samples) return;
1493 
1494  // check for reached recording time limit if enabled
1495  if (params.record_time_limited && m_writers) {
1496  const sample_index_t last = m_writers->last();
1497  const sample_index_t already_recorded = (last) ? (last + 1) : 0;
1498  const sample_index_t limit = static_cast<const sample_index_t>(rint(
1499  params.record_time * params.sample_rate));
1500  if (already_recorded + samples >= limit) {
1501  // reached end of recording time, we are full
1502  if (m_state == Kwave::REC_RECORDING) {
1503  samples = Kwave::toUint(
1504  (limit > already_recorded) ?
1505  (limit - already_recorded) : 0);
1506  buffer.resize(samples * tracks * bytes_per_sample);
1507  }
1508  recording_done = true;
1509  }
1510  }
1511 
1512  QByteArray buf;
1513  buf.resize(bytes_per_sample * samples);
1514  Q_ASSERT(buf.size() == Kwave::toInt(bytes_per_sample * samples));
1515  if (buf.size() != Kwave::toInt(bytes_per_sample * samples)) return;
1516 
1517  Kwave::SampleArray decoded(samples);
1518  Q_ASSERT(decoded.size() == samples);
1519  if (decoded.size() != samples) return;
1520 
1521  // check for trigger
1522  // note: this might change the state, which affects the
1523  // processing of all tracks !
1527  {
1528  for (unsigned int track=0; track < tracks; ++track) {
1529  // split off and decode buffer with current track
1530  split(buffer, buf, bytes_per_sample, track, tracks);
1531  m_decoder->decode(buf, decoded);
1532  if (checkTrigger(track, decoded)) {
1534  break;
1535  }
1536  }
1537  }
1538 
1539  if ((m_state == Kwave::REC_RECORDING) && !m_prerecording_queue.isEmpty()) {
1540  // flush all prerecorded buffers to the output
1542  }
1543 
1544  // use a copy of the state, in case it changes below ;-)
1545  Kwave::RecordState state = m_state;
1546  for (unsigned int track=0; track < tracks; ++track) {
1547  // decode and care for all special effects, meters and so on
1548  // split off and decode buffer with current track
1549  split(buffer, buf, bytes_per_sample, track, tracks);
1550  m_decoder->decode(buf, decoded);
1551 
1552  // update the level meter and other effects
1553  m_dialog->updateEffects(track, decoded);
1554 
1555  // if the first buffer is full -> leave REC_BUFFERING
1556  // limit state transitions to a point before the first track is
1557  // processed (avoid asymmetry)
1558  if ((track == 0) && (m_state == Kwave::REC_BUFFERING) &&
1559  (m_buffers_recorded > 1))
1560  {
1562  state = m_state; // might have changed!
1563  }
1564 
1565  switch (state) {
1567  case Kwave::REC_EMPTY:
1568  case Kwave::REC_PAUSED:
1569  case Kwave::REC_DONE:
1570  case Kwave::REC_BUFFERING:
1572  // already handled before or nothing to do...
1573  break;
1575  // enqueue the buffers into a FIFO
1576  enqueuePrerecording(track, decoded);
1577  break;
1578  case Kwave::REC_RECORDING: {
1579  // put the decoded track data into the buffer
1580  if (!m_writers) break; // (could happen due to queued signal)
1581  Q_ASSERT(tracks == m_writers->tracks());
1582  if (!tracks || (tracks != m_writers->tracks())) break;
1583 
1584  Kwave::Writer *writer = (*m_writers)[track];
1585  Q_ASSERT(writer);
1586  if (writer) (*writer) << decoded;
1587  m_controller.setEmpty(false);
1588 
1589  break;
1590  }
1591  }
1592  }
1593 
1594  // update the number of recorded samples
1595  if (m_writers) emit sigRecordedSamples(m_writers->last() + 1);
1596 
1597  // if this was the last received buffer, change state
1598  if (recording_done &&
1599  (m_state != Kwave::REC_DONE) &&
1601  {
1603  }
1604 
1605 }
1606 
1607 //***************************************************************************
1609 {
1610  (void)enable;
1611  InhibitRecordGuard _lock(*this); // activate the change
1612 }
1613 
1614 //***************************************************************************
1615 #include "RecordPlugin.moc"
1616 //***************************************************************************
1617 //***************************************************************************
void notice(QString message)
void split(QByteArray &raw_data, QByteArray &dest, unsigned int bytes_per_sample, unsigned int track, unsigned int tracks)
virtual QString open(const QString &dev)=0
void enablePrerecording(bool enable)
void sigRecordedSamples(sample_index_t samples_recorded)
void emitCommand(const QString &command)
Definition: Plugin.cpp:510
void setBitsPerSample(unsigned int bits)
virtual unsigned int tracks() const Q_DECL_OVERRIDE
unsigned int bits_per_sample
Definition: RecordParams.h:105
Kwave::SampleDecoder * m_decoder
Definition: RecordPlugin.h:258
void changeBitsPerSample(unsigned int new_bits)
Kwave::RecordThread * m_thread
Definition: RecordPlugin.h:255
void updateBufferState(unsigned int count, unsigned int total)
void setTracks(unsigned int tracks)
virtual bool write(const Kwave::SampleArray &buffer, unsigned int &count)=0
Definition: App.h:33
void prerecordingChanged(bool enable)
void recordStopped(int reason)
double rate() const
Definition: FileInfo.cpp:415
void enqueuePrerecording(unsigned int track, const Kwave::SampleArray &decoded)
virtual unsigned int rawBytesPerSample()=0
QString rate2string(double rate) const
QString description(IDX type, bool localized) const
Definition: TypesMap.h:128
virtual QString fileFilter()
Definition: RecordDevice.h:75
virtual QList< unsigned int > supportedBits()=0
virtual int setSampleRate(double &new_rate)=0
QWidget * parentWidget() const
Definition: Plugin.cpp:450
virtual int bitsPerSample()=0
bool checkTrigger(unsigned int track, const Kwave::SampleArray &buffer)
void setSupportedBits(const QList< unsigned int > &bits)
Kwave::RecordState m_state
Definition: RecordPlugin.h:246
Kwave::SignalManager & signalManager()
Definition: Plugin.cpp:444
void changeTracks(unsigned int new_tracks)
static int sorry(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:85
virtual QString name() const
Definition: Plugin.cpp:196
virtual int setTracks(unsigned int &tracks)=0
RecordPlugin(QObject *parent, const QVariantList &args)
Kwave::record_method_t m_method
Definition: RecordPlugin.h:237
virtual void migrateToActiveContext()
Definition: Plugin.cpp:516
unsigned int tracks()
unsigned int m_buffers_recorded
Definition: RecordPlugin.h:273
quint64 sample_index_t
Definition: Sample.h:28
int setBuffers(unsigned int count, unsigned int size)
virtual int setCompression(Kwave::Compression::Type new_compression)=0
unsigned int pre_record_time
Definition: RecordParams.h:78
Kwave::MultiTrackWriter * m_writers
Definition: RecordPlugin.h:267
virtual int detectTracks(unsigned int &min, unsigned int &max)=0
void resetRecording(bool &accepted)
Kwave::PluginManager & manager() const
Definition: Plugin.cpp:437
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
QVector< float > m_trigger_value
Definition: RecordPlugin.h:279
Kwave::SignalManager & signalManager()
void set(FileProperty key, const QVariant &value)
Definition: FileInfo.cpp:363
virtual QStringList * setup(QStringList &previous_params) Q_DECL_OVERRIDE
virtual sample_index_t last() const
Definition: MultiWriter.cpp:85
void setRate(double rate)
Definition: FileInfo.cpp:424
void setFileInfo(const Kwave::FileInfo &new_info, bool with_undo)
record_method_t
Definition: RecordParams.h:37
void setCompression(int compression)
unsigned int record_time
Definition: RecordParams.h:81
unsigned int record_trigger
Definition: RecordParams.h:87
virtual QList< Kwave::Compression::Type > detectCompressions()=0
void changeCompression(Kwave::Compression::Type new_compression)
virtual QStringList supportedDevices()=0
void setMethod(Kwave::record_method_t method)
virtual void flush()
static int error(QWidget *widget, QString message, QString caption=QString())
Definition: MessageBox.cpp:126
void setMethod(Kwave::record_method_t method)
virtual QList< double > detectSampleRates()=0
unsigned int m_inhibit_count
Definition: RecordPlugin.h:276
virtual int setBitsPerSample(unsigned int new_bits)=0
Kwave::RecordDialog * m_dialog
Definition: RecordPlugin.h:252
#define OPEN_RETRY_TIME
virtual ~RecordPlugin() Q_DECL_OVERRIDE
int toInt(T x)
Definition: Utils.h:127
void setLength(sample_index_t length)
Definition: FileInfo.cpp:409
double rate() const
void setTracks(unsigned int tracks)
Definition: FileInfo.cpp:454
virtual QList< Kwave::SampleFormat::Format > detectSampleFormats()=0
virtual unsigned int get(Kwave::SampleArray &buffer)
Definition: SampleFIFO.cpp:70
Kwave::RecordDevice * m_device
Definition: RecordPlugin.h:249
void stateChanged(Kwave::RecordState state)
virtual double sampleRate()=0
void setSupportedSampleRates(const QList< double > &rates)
unsigned int tracks
Definition: RecordParams.h:102
void changeSampleRate(double new_rate)
virtual sample_index_t signalLength()
Definition: Plugin.cpp:462
virtual Kwave::Compression::Type compression()=0
void enableTrigger(bool enable)
static float sample2float(const sample_t s)
Definition: Sample.h:65
void setSampleRate(double new_rate)
virtual void decode(QByteArray &raw_data, Kwave::SampleArray &decoded)=0
void setSupportedTracks(unsigned int min, unsigned int max)
QByteArray dequeue()
void setSampleFormat(Kwave::SampleFormat::Format sample_format)
virtual void crop()
Definition: SampleFIFO.cpp:136
void setBits(unsigned int bits)
Definition: FileInfo.cpp:439
virtual unsigned int length()
Definition: SampleFIFO.cpp:122
static double saw(double param)
Definition: Functions.cpp:52
Kwave::RecordController m_controller
Definition: RecordPlugin.h:243
virtual void flush()
Definition: SampleFIFO.cpp:45
unsigned int queuedBuffers()
virtual unsigned int blockSize() const
virtual Kwave::SampleFormat::Format sampleFormat()=0
Kwave::RecordParams & params()
QVector< Kwave::SampleFIFO > m_prerecording_queue
Definition: RecordPlugin.h:264
void changeSampleFormat(Kwave::SampleFormat::Format new_format)
Kwave::SampleFormat::Format sample_format
Definition: RecordParams.h:106
#define KWAVE_PLUGIN(name, class)
Definition: Plugin.h:54
#define _(m)
Definition: memcpy.c:66
unsigned int bits() const
void setDevice(const QString &device)
void setDevice(const QString &device)
unsigned int size() const
void setSupportedDevices(QStringList devices)
#define DBG(qs)
Definition: String.h:55
virtual int stop(unsigned int timeout=10000)
virtual int close()=0
virtual int tracks()=0
virtual void start()
unsigned int toUint(T x)
Definition: Utils.h:109
QDateTime start_time
Definition: RecordParams.h:84
void setInitialized(bool initialized)
virtual void clear() Q_DECL_OVERRIDE
Definition: MultiWriter.cpp:97
virtual int setSampleFormat(Kwave::SampleFormat::Format new_format)=0
void updateEffects(unsigned int track, Kwave::SampleArray &buffer)
Kwave::Compression::Type compression
Definition: RecordParams.h:104
unsigned int buffer_count
Definition: RecordParams.h:108
virtual Kwave::byte_order_t endianness()=0
void setFileFilter(const QString &filter)
void setSupportedSampleFormats(const QList< Kwave::SampleFormat::Format > &formats)
RecordState
Definition: RecordState.h:25
IDX findFromData(const DATA &data) const
Definition: TypesMap.h:89
void setRecordDevice(Kwave::RecordDevice *device)
unsigned int buffer_size
Definition: RecordParams.h:109
unsigned int remainingBuffers()
void setSupportedCompressions(const QList< Kwave::Compression::Type > &comps)
QStringList defaultParams(const QString &name)
void message(const QString &message)