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