tracksconfigdialog.cpp
configtrackscommand.cpp
abstractscopewidget.cpp
+ abstractgfxscopewidget.cpp
audioscopes/abstractaudioscopewidget.cpp
audioscopes/audiospectrum.cpp
rebuildgroupcommand.cpp
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
+ * This file is part of kdenlive. See www.kdenlive.org. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+
+#include "qtconcurrentrun.h"
+
+#include "abstractgfxscopewidget.h"
+#include "renderer.h"
+#include "monitor.h"
+
+#include <QFuture>
+#include <QColor>
+#include <QMenu>
+#include <QMouseEvent>
+#include <QPainter>
+
+const int REALTIME_FPS = 30;
+
+const QColor light(250, 238, 226, 255);
+const QColor dark(40, 40, 39, 255);
+const QColor dark2(25, 25, 23, 255);
+
+const QPen AbstractGfxScopeWidget::penThick(QBrush(QColor(250, 250, 250)), 2, Qt::SolidLine);
+const QPen AbstractGfxScopeWidget::penThin(QBrush(QColor(250, 250, 250)), 1, Qt::SolidLine);
+const QPen AbstractGfxScopeWidget::penLight(QBrush(QColor(200, 200, 250, 150)), 1, Qt::SolidLine);
+const QPen AbstractGfxScopeWidget::penLightDots(QBrush(QColor(200, 200, 250, 150)), 1, Qt::DotLine);
+const QPen AbstractGfxScopeWidget::penDark(QBrush(QColor(0, 0, 20, 250)), 1, Qt::SolidLine);
+const QPen AbstractGfxScopeWidget::penDarkDots(QBrush(QColor(0, 0, 20, 250)), 1, Qt::DotLine);
+
+AbstractGfxScopeWidget::AbstractGfxScopeWidget(Monitor *projMonitor, Monitor *clipMonitor, bool trackMouse, QWidget *parent) :
+ QWidget(parent),
+ m_projMonitor(projMonitor),
+ m_clipMonitor(clipMonitor),
+ m_mousePos(0, 0),
+ m_mouseWithinWidget(false),
+ offset(5),
+ m_accelFactorHUD(1),
+ m_accelFactorScope(1),
+ m_accelFactorBackground(1),
+ m_semaphoreHUD(1),
+ m_semaphoreScope(1),
+ m_semaphoreBackground(1),
+ initialDimensionUpdateDone(false),
+ m_requestForcedUpdate(false)
+
+{
+ m_scopePalette = QPalette();
+ m_scopePalette.setBrush(QPalette::Window, QBrush(dark2));
+ m_scopePalette.setBrush(QPalette::Base, QBrush(dark));
+ m_scopePalette.setBrush(QPalette::Button, QBrush(dark));
+ m_scopePalette.setBrush(QPalette::Text, QBrush(light));
+ m_scopePalette.setBrush(QPalette::WindowText, QBrush(light));
+ m_scopePalette.setBrush(QPalette::ButtonText, QBrush(light));
+ this->setPalette(m_scopePalette);
+ this->setAutoFillBackground(true);
+
+ m_aAutoRefresh = new QAction(i18n("Auto Refresh"), this);
+ m_aAutoRefresh->setCheckable(true);
+ m_aRealtime = new QAction(i18n("Realtime (with precision loss)"), this);
+ m_aRealtime->setCheckable(true);
+
+ m_menu = new QMenu(this);
+ m_menu->setPalette(m_scopePalette);
+ m_menu->addAction(m_aAutoRefresh);
+ m_menu->addAction(m_aRealtime);
+
+ this->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ m_activeRender = (m_clipMonitor->isActive()) ? m_clipMonitor->render : m_projMonitor->render;
+
+ bool b = true;
+ b &= connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint)));
+
+ //b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
+ b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage)));
+
+ b &= connect(this, SIGNAL(signalHUDRenderingFinished(uint, uint)), this, SLOT(slotHUDRenderingFinished(uint, uint)));
+ b &= connect(this, SIGNAL(signalScopeRenderingFinished(uint, uint)), this, SLOT(slotScopeRenderingFinished(uint, uint)));
+ b &= connect(this, SIGNAL(signalBackgroundRenderingFinished(uint, uint)), this, SLOT(slotBackgroundRenderingFinished(uint, uint)));
+ b &= connect(m_aRealtime, SIGNAL(toggled(bool)), this, SLOT(slotResetRealtimeFactor(bool)));
+ b &= connect(m_aAutoRefresh, SIGNAL(toggled(bool)), this, SLOT(slotAutoRefreshToggled(bool)));
+ Q_ASSERT(b);
+
+ // Enable mouse tracking if desired.
+ // Causes the mouseMoved signal to be emitted when the mouse moves inside the
+ // widget, even when no mouse button is pressed.
+ this->setMouseTracking(trackMouse);
+}
+
+AbstractGfxScopeWidget::~AbstractGfxScopeWidget()
+{
+ writeConfig();
+
+ delete m_menu;
+ delete m_aAutoRefresh;
+ delete m_aRealtime;
+}
+
+void AbstractGfxScopeWidget::init()
+{
+ m_widgetName = widgetName();
+ readConfig();
+}
+
+void AbstractGfxScopeWidget::readConfig()
+{
+ KSharedConfigPtr config = KGlobal::config();
+ KConfigGroup scopeConfig(config, configName());
+ m_aAutoRefresh->setChecked(scopeConfig.readEntry("autoRefresh", true));
+ m_aRealtime->setChecked(scopeConfig.readEntry("realtime", false));
+ scopeConfig.sync();
+}
+
+void AbstractGfxScopeWidget::writeConfig()
+{
+ KSharedConfigPtr config = KGlobal::config();
+ KConfigGroup scopeConfig(config, configName());
+ scopeConfig.writeEntry("autoRefresh", m_aAutoRefresh->isChecked());
+ scopeConfig.writeEntry("realtime", m_aRealtime->isChecked());
+ scopeConfig.sync();
+}
+
+QString AbstractGfxScopeWidget::configName()
+{
+ return "Scope_" + m_widgetName;
+}
+
+void AbstractGfxScopeWidget::prodHUDThread()
+{
+ if (this->visibleRegion().isEmpty()) {
+// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating HUD.";
+ } else {
+ if (m_semaphoreHUD.tryAcquire(1)) {
+ Q_ASSERT(!m_threadHUD.isRunning());
+
+ m_newHUDFrames.fetchAndStoreRelaxed(0);
+ m_newHUDUpdates.fetchAndStoreRelaxed(0);
+ m_threadHUD = QtConcurrent::run(this, &AbstractGfxScopeWidget::renderHUD, m_accelFactorHUD);
+// qDebug() << "HUD thread started in " << m_widgetName;
+
+ } else {
+// qDebug() << "HUD semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadHUD.isRunning();
+ }
+ }
+}
+
+void AbstractGfxScopeWidget::prodScopeThread()
+{
+ // Only start a new thread if the scope is actually visible
+ // and not hidden by another widget on the stack and if user want the scope to update.
+ if (this->visibleRegion().isEmpty() || (!m_aAutoRefresh->isChecked() && !m_requestForcedUpdate)) {
+// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating scope.";
+ } else {
+ // Try to acquire the semaphore. This must only succeed if m_threadScope is not running
+ // anymore. Therefore the semaphore must NOT be released before m_threadScope ends.
+ // If acquiring the semaphore fails, the thread is still running.
+ if (m_semaphoreScope.tryAcquire(1)) {
+ Q_ASSERT(!m_threadScope.isRunning());
+
+ m_newScopeFrames.fetchAndStoreRelaxed(0);
+ m_newScopeUpdates.fetchAndStoreRelaxed(0);
+
+ Q_ASSERT(m_accelFactorScope > 0);
+
+ // See http://doc.qt.nokia.com/latest/qtconcurrentrun.html#run about
+ // running member functions in a thread
+ m_threadScope = QtConcurrent::run(this, &AbstractGfxScopeWidget::renderScope, m_accelFactorScope, m_scopeImage);
+ m_requestForcedUpdate = false;
+
+// qDebug() << "Scope thread started in " << m_widgetName;
+
+ } else {
+// qDebug() << "Scope semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadScope.isRunning();
+ }
+ }
+}
+void AbstractGfxScopeWidget::prodBackgroundThread()
+{
+ if (this->visibleRegion().isEmpty()) {
+// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating background.";
+ } else {
+ if (m_semaphoreBackground.tryAcquire(1)) {
+ Q_ASSERT(!m_threadBackground.isRunning());
+
+ m_newBackgroundFrames.fetchAndStoreRelaxed(0);
+ m_newBackgroundUpdates.fetchAndStoreRelaxed(0);
+ m_threadBackground = QtConcurrent::run(this, &AbstractGfxScopeWidget::renderBackground, m_accelFactorBackground);
+// qDebug() << "Background thread started in " << m_widgetName;
+
+ } else {
+// qDebug() << "Background semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadBackground.isRunning();
+ }
+ }
+}
+
+void AbstractGfxScopeWidget::forceUpdate(bool doUpdate)
+{
+// qDebug() << "Force update called in " << widgetName() << ". Arg: " << doUpdate;
+ if (!doUpdate) {
+ return;
+ }
+ m_requestForcedUpdate = true;
+ m_newHUDUpdates.fetchAndAddRelaxed(1);
+ m_newScopeUpdates.fetchAndAddRelaxed(1);
+ m_newBackgroundUpdates.fetchAndAddRelaxed(1);
+ prodHUDThread();
+ prodScopeThread();
+ prodBackgroundThread();
+}
+void AbstractGfxScopeWidget::forceUpdateHUD()
+{
+ m_newHUDUpdates.fetchAndAddRelaxed(1);
+ prodHUDThread();
+
+}
+void AbstractGfxScopeWidget::forceUpdateScope()
+{
+ m_newScopeUpdates.fetchAndAddRelaxed(1);
+ m_requestForcedUpdate = true;
+ prodScopeThread();
+
+}
+void AbstractGfxScopeWidget::forceUpdateBackground()
+{
+ m_newBackgroundUpdates.fetchAndAddRelaxed(1);
+ prodBackgroundThread();
+
+}
+
+
+///// Events /////
+
+void AbstractGfxScopeWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (!m_aAutoRefresh->isChecked()) {
+ m_requestForcedUpdate = true;
+ m_activeRender->sendFrameUpdate();
+ }
+ prodHUDThread();
+ prodScopeThread();
+ prodBackgroundThread();
+ QWidget::mouseReleaseEvent(event);
+}
+
+void AbstractGfxScopeWidget::resizeEvent(QResizeEvent *event)
+{
+ // Update the dimension of the available rect for painting
+ m_scopeRect = scopeRect();
+ forceUpdate();
+
+ QWidget::resizeEvent(event);
+}
+
+void AbstractGfxScopeWidget::showEvent(QShowEvent *event)
+{
+ QWidget::showEvent(event);
+ m_scopeRect = scopeRect();
+}
+
+void AbstractGfxScopeWidget::paintEvent(QPaintEvent *)
+{
+ QPainter davinci(this);
+ davinci.drawImage(m_scopeRect.topLeft(), m_imgBackground);
+ davinci.drawImage(m_scopeRect.topLeft(), m_imgScope);
+ davinci.drawImage(m_scopeRect.topLeft(), m_imgHUD);
+}
+
+void AbstractGfxScopeWidget::mouseMoveEvent(QMouseEvent *event)
+{
+ m_mousePos = event->pos();
+ m_mouseWithinWidget = true;
+ emit signalMousePositionChanged();
+}
+void AbstractGfxScopeWidget::leaveEvent(QEvent *)
+{
+ m_mouseWithinWidget = false;
+ emit signalMousePositionChanged();
+}
+
+void AbstractGfxScopeWidget::customContextMenuRequested(const QPoint &pos)
+{
+ m_menu->exec(this->mapToGlobal(pos));
+}
+
+uint AbstractGfxScopeWidget::calculateAccelFactorHUD(uint oldMseconds, uint)
+{
+ return ceil((float)oldMseconds*REALTIME_FPS / 1000);
+}
+uint AbstractGfxScopeWidget::calculateAccelFactorScope(uint oldMseconds, uint)
+{
+ return ceil((float)oldMseconds*REALTIME_FPS / 1000);
+}
+uint AbstractGfxScopeWidget::calculateAccelFactorBackground(uint oldMseconds, uint)
+{
+ return ceil((float)oldMseconds*REALTIME_FPS / 1000);
+}
+
+
+///// Slots /////
+
+void AbstractGfxScopeWidget::slotHUDRenderingFinished(uint mseconds, uint oldFactor)
+{
+// qDebug() << "HUD rendering has finished, waiting for termination in " << m_widgetName;
+ m_threadHUD.waitForFinished();
+ m_imgHUD = m_threadHUD.result();
+
+ m_semaphoreHUD.release(1);
+ this->update();
+
+ int accel;
+ if (m_aRealtime->isChecked()) {
+ accel = calculateAccelFactorHUD(mseconds, oldFactor);
+ if (m_accelFactorHUD < 1) {
+ accel = 1;
+ }
+ m_accelFactorHUD = accel;
+ }
+
+ if ((m_newHUDFrames > 0 && m_aAutoRefresh->isChecked()) || m_newHUDUpdates > 0) {
+// qDebug() << "Trying to start a new HUD thread for " << m_widgetName
+// << ". New frames/updates: " << m_newHUDFrames << "/" << m_newHUDUpdates;
+ prodHUDThread();;
+ }
+}
+
+void AbstractGfxScopeWidget::slotScopeRenderingFinished(uint mseconds, uint oldFactor)
+{
+ // The signal can be received before the thread has really finished. So we
+ // need to wait until it has really finished before starting a new thread.
+// qDebug() << "Scope rendering has finished, waiting for termination in " << m_widgetName;
+ m_threadScope.waitForFinished();
+ m_imgScope = m_threadScope.result();
+
+ // The scope thread has finished. Now we can release the semaphore, allowing a new thread.
+ // See prodScopeThread where the semaphore is acquired again.
+ m_semaphoreScope.release(1);
+ this->update();
+
+ // Calculate the acceleration factor hint to get «realtime» updates.
+ int accel;
+ if (m_aRealtime->isChecked()) {
+ accel = calculateAccelFactorScope(mseconds, oldFactor);
+ if (accel < 1) {
+ // If mseconds happens to be 0.
+ accel = 1;
+ }
+ // Don't directly calculate with m_accelFactorScope as we are dealing with concurrency.
+ // If m_accelFactorScope is set to 0 at the wrong moment, who knows what might happen
+ // then :) Therefore use a local variable.
+ m_accelFactorScope = accel;
+ }
+
+ if ((m_newScopeFrames > 0 && m_aAutoRefresh->isChecked()) || m_newScopeUpdates > 0) {
+// qDebug() << "Trying to start a new scope thread for " << m_widgetName
+// << ". New frames/updates: " << m_newScopeFrames << "/" << m_newScopeUpdates;
+ prodScopeThread();
+ }
+}
+
+void AbstractGfxScopeWidget::slotBackgroundRenderingFinished(uint mseconds, uint oldFactor)
+{
+// qDebug() << "Background rendering has finished, waiting for termination in " << m_widgetName;
+ m_threadBackground.waitForFinished();
+ m_imgBackground = m_threadBackground.result();
+
+ m_semaphoreBackground.release(1);
+ this->update();
+
+ int accel;
+ if (m_aRealtime->isChecked()) {
+ accel = calculateAccelFactorBackground(mseconds, oldFactor);
+ if (m_accelFactorBackground < 1) {
+ accel = 1;
+ }
+ m_accelFactorBackground = accel;
+ }
+
+ if ((m_newBackgroundFrames > 0 && m_aAutoRefresh->isChecked()) || m_newBackgroundUpdates > 0) {
+// qDebug() << "Trying to start a new background thread for " << m_widgetName
+// << ". New frames/updates: " << m_newBackgroundFrames << "/" << m_newBackgroundUpdates;
+ prodBackgroundThread();;
+ }
+}
+
+void AbstractGfxScopeWidget::slotActiveMonitorChanged(bool isClipMonitor)
+{
+// qDebug() << "Active monitor has changed in " << m_widgetName << ". Is the clip monitor active now? " << isClipMonitor;
+
+ bool b = m_activeRender->disconnect(this);
+ Q_ASSERT(b);
+
+ m_activeRender = (isClipMonitor) ? m_clipMonitor->render : m_projMonitor->render;
+
+ //b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
+ b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage)));
+ Q_ASSERT(b);
+
+ // Update the scope for the new monitor.
+ prodHUDThread();
+ prodScopeThread();
+ prodBackgroundThread();
+}
+
+void AbstractGfxScopeWidget::slotRenderZoneUpdated()
+{
+ m_newHUDFrames.fetchAndAddRelaxed(1);
+ m_newScopeFrames.fetchAndAddRelaxed(1);
+ m_newBackgroundFrames.fetchAndAddRelaxed(1);
+
+// qDebug() << "Monitor incoming. New frames total HUD/Scope/Background: " << m_newHUDFrames
+// << "/" << m_newScopeFrames << "/" << m_newBackgroundFrames;
+
+ if (this->visibleRegion().isEmpty()) {
+// qDebug() << "Scope of widget " << m_widgetName << " is not at the top, not rendering.";
+ } else {
+ if (m_aAutoRefresh->isChecked()) {
+ prodHUDThread();
+ prodScopeThread();
+ prodBackgroundThread();
+ }
+ }
+}
+
+void AbstractGfxScopeWidget::slotRenderZoneUpdated(QImage frame)
+{
+ m_scopeImage = frame;
+ slotRenderZoneUpdated();
+}
+
+void AbstractGfxScopeWidget::slotResetRealtimeFactor(bool realtimeChecked)
+{
+ if (!realtimeChecked) {
+ m_accelFactorHUD = 1;
+ m_accelFactorScope = 1;
+ m_accelFactorBackground = 1;
+ }
+}
+
+bool AbstractGfxScopeWidget::autoRefreshEnabled()
+{
+ return m_aAutoRefresh->isChecked();
+}
+
+void AbstractGfxScopeWidget::slotAutoRefreshToggled(bool autoRefresh)
+{
+ if (isVisible()) emit requestAutoRefresh(autoRefresh);
+ // TODO only if depends on input
+ if (autoRefresh) {
+ //forceUpdate();
+ m_requestForcedUpdate = true;
+ m_activeRender->sendFrameUpdate();
+ }
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
+ * This file is part of kdenlive. See www.kdenlive.org. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+
+/**
+ This abstract widget is a proof that abstract things sometimes *are* useful.
+
+ The widget expects three layers which
+ * Will be painted on top of each other on each update
+ * Are rendered in a separate thread so that the UI is not blocked
+ * Are rendered only if necessary (e.g., if a layer does not depend
+ on input images, it will not be re-rendered for incoming frames)
+
+ The layer order is as follows:
+ _____________________
+ / \
+ / HUD Layer \
+ / \
+ ---------------------------
+ _____________________
+ / \
+ / Scope Layer \
+ / \
+ ---------------------------
+ _____________________
+ / \
+ / Background Layer \
+ / \
+ ---------------------------
+
+ Colors of Scope Widgets are defined in here (and thus don't need to be
+ re-defined in the implementation of the widget's .ui file).
+
+ The custom context menu already contains entries, like for enabling auto-
+ refresh. It can certainly be extended in the implementation of the widget.
+
+ Note: Widgets deriving from this class should connect slotActiveMonitorChanged
+ to the appropriate signal.
+
+ If you intend to write an own widget inheriting from this one, please read
+ the comments on the unimplemented methods carefully. They are not only here
+ for optical amusement, but also contain important information.
+ */
+
+#ifndef ABSTRACTGFXSCOPEWIDGET_H
+#define ABSTRACTGFXSCOPEWIDGET_H
+
+
+#include <QtCore>
+#include <QWidget>
+
+class QMenu;
+
+class Monitor;
+class Render;
+
+class AbstractGfxScopeWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ AbstractGfxScopeWidget(Monitor *projMonitor, Monitor *clipMonitor, bool trackMouse = false, QWidget *parent = 0);
+ virtual ~AbstractGfxScopeWidget(); // Must be virtual because of inheritance, to avoid memory leaks
+ QPalette m_scopePalette;
+
+ /** Initializes widget settings (reads configuration).
+ Has to be called in the implementing object. */
+ void init();
+
+ /** Does this scope have auto-refresh enabled */
+ bool autoRefreshEnabled();
+
+ ///// Unimplemented /////
+
+ virtual QString widgetName() const = 0;
+
+ ///// Variables /////
+ static const QPen penThick;
+ static const QPen penThin;
+ static const QPen penLight;
+ static const QPen penLightDots;
+ static const QPen penDark;
+ static const QPen penDarkDots;
+
+protected:
+ ///// Variables /////
+
+ Monitor *m_projMonitor;
+ Monitor *m_clipMonitor;
+ Render *m_activeRender;
+
+
+ /** The context menu. Feel free to add new entries in your implementation. */
+ QMenu *m_menu;
+
+ /** Enables auto refreshing of the scope.
+ This is when a new frame is shown on the active monitor.
+ Resize events always force a recalculation. */
+ QAction *m_aAutoRefresh;
+
+ /** Realtime rendering. Should be disabled if it is not supported.
+ Use the accelerationFactor variable passed to the render functions as a hint of
+ how many times faster the scope should be calculated. */
+ QAction *m_aRealtime;
+
+ /** The mouse position; Updated when the mouse enters the widget
+ AND mouse tracking has been enabled. */
+ QPoint m_mousePos;
+ /** Knows whether the mouse currently lies within the widget or not.
+ Can e.g. be used for drawing a HUD only when the mouse is in the widget. */
+ bool m_mouseWithinWidget;
+
+ /** Offset from the widget's borders */
+ const uchar offset;
+
+ /** The rect on the widget we're painting in.
+ Can be used by the implementing widget, e.g. in the render methods.
+ Is updated when necessary (size changes). */
+ QRect m_scopeRect;
+
+ /** Images storing the calculated layers. Will be used on repaint events. */
+ QImage m_imgHUD;
+ QImage m_imgScope;
+ QImage m_imgBackground;
+
+ /** The acceleration factors can be accessed also by other renderer tasks,
+ e.g. to display the scope's acceleration factor in the HUD renderer. */
+ int m_accelFactorHUD;
+ int m_accelFactorScope;
+ int m_accelFactorBackground;
+
+ /** Reads the widget's configuration.
+ Can be extended in the implementing subclass (make sure to run readConfig as well). */
+ virtual void readConfig();
+ /** Writes the widget configuration.
+ Implementing widgets have to implement an own method and run it in their destructor. */
+ void writeConfig();
+ /** Identifier for the widget's configuration. */
+ QString configName();
+
+
+ ///// Unimplemented Methods /////
+
+ /** Where on the widget we can paint in.
+ May also update other variables that depend on the widget's size. */
+ virtual QRect scopeRect() = 0;
+
+ /** @brief HUD renderer. Must emit signalHUDRenderingFinished(). @see renderScope */
+ virtual QImage renderHUD(uint accelerationFactor) = 0;
+ /** @brief Scope renderer. Must emit signalScopeRenderingFinished()
+ when calculation has finished, to allow multi-threading.
+ accelerationFactor hints how much faster than usual the calculation should be accomplished, if possible. */
+ virtual QImage renderScope(uint accelerationFactor, const QImage) = 0;
+ /** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). @see renderScope */
+ virtual QImage renderBackground(uint accelerationFactor) = 0;
+
+ /** Must return true if the HUD layer depends on the input monitor.
+ If it does not, then it does not need to be re-calculated when
+ a new frame from the monitor is incoming. */
+ virtual bool isHUDDependingOnInput() const = 0;
+ /** @see isHUDDependingOnInput() */
+ virtual bool isScopeDependingOnInput() const = 0;
+ /** @see isHUDDependingOnInput() */
+ virtual bool isBackgroundDependingOnInput() const = 0;
+
+ ///// Can be reimplemented /////
+ /** Calculates the acceleration factor to be used by the render thread.
+ This method can be refined in the subclass if required. */
+ virtual uint calculateAccelFactorHUD(uint oldMseconds, uint oldFactor);
+ virtual uint calculateAccelFactorScope(uint oldMseconds, uint oldFactor);
+ virtual uint calculateAccelFactorBackground(uint oldMseconds, uint oldFactor);
+
+ ///// Reimplemented /////
+
+ void mouseMoveEvent(QMouseEvent *);
+ void leaveEvent(QEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void paintEvent(QPaintEvent *);
+ void resizeEvent(QResizeEvent *);
+ void showEvent(QShowEvent *); // Called when the widget is activated via the Menu entry
+ // void raise(); // Called only when manually calling the event -> useless
+
+
+protected slots:
+ /** Forces an update of all layers. */
+ void forceUpdate(bool doUpdate = true);
+ void forceUpdateHUD();
+ void forceUpdateScope();
+ void forceUpdateBackground();
+ void slotAutoRefreshToggled(bool);
+
+signals:
+ /** mseconds represent the time taken for the calculation,
+ accelerationFactor is the acceleration factor that has been used for this calculation. */
+ void signalHUDRenderingFinished(uint mseconds, uint accelerationFactor);
+ void signalScopeRenderingFinished(uint mseconds, uint accelerationFactor);
+ void signalBackgroundRenderingFinished(uint mseconds, uint accelerationFactor);
+
+ /** For the mouse position itself see m_mousePos.
+ To check whether the mouse has leaved the widget, see m_mouseWithinWidget. */
+ void signalMousePositionChanged();
+
+ /** Do we need the renderer to send its frames to us? */
+ void requestAutoRefresh(bool);
+
+private:
+
+ /** Counts the number of frames that have been rendered in the active monitor.
+ The frame number will be reset when the calculation starts for the current frame. */
+ QAtomicInt m_newHUDFrames;
+ QAtomicInt m_newScopeFrames;
+ QAtomicInt m_newBackgroundFrames;
+
+ /** Counts the number of updates that, unlike new frames, force a recalculation
+ of the scope, like for example a resize event. */
+ QAtomicInt m_newHUDUpdates;
+ QAtomicInt m_newScopeUpdates;
+ QAtomicInt m_newBackgroundUpdates;
+
+ /** The semaphores ensure that the QFutures for the HUD/Scope/Background threads cannot
+ be assigned a new thread while it is still running. (Could cause deadlocks and other
+ nasty things known from parallelism.) */
+ QSemaphore m_semaphoreHUD;
+ QSemaphore m_semaphoreScope;
+ QSemaphore m_semaphoreBackground;
+
+ QFuture<QImage> m_threadHUD;
+ QFuture<QImage> m_threadScope;
+ QFuture<QImage> m_threadBackground;
+
+ bool initialDimensionUpdateDone;
+ bool m_requestForcedUpdate;
+
+ QImage m_scopeImage;
+
+ QString m_widgetName;
+
+ void prodHUDThread();
+ void prodScopeThread();
+ void prodBackgroundThread();
+
+public slots:
+ /** @brief Must be called when the active monitor has shown a new frame.
+ This slot must be connected in the implementing class, it is *not*
+ done in this abstract class. */
+ void slotActiveMonitorChanged(bool isClipMonitor);
+
+private slots:
+ void customContextMenuRequested(const QPoint &pos);
+ /** To be called when a new frame has been received.
+ The scope then decides whether and when it wants to recalculate the scope, depending
+ on whether it is currently visible and whether a calculation thread is already running. */
+ void slotRenderZoneUpdated();
+ void slotRenderZoneUpdated(QImage);
+ /** The following slots are called when rendering of a component has finished. They e.g. update
+ the widget and decide whether to immediately restart the calculation thread. */
+ void slotHUDRenderingFinished(uint mseconds, uint accelerationFactor);
+ void slotScopeRenderingFinished(uint mseconds, uint accelerationFactor);
+ void slotBackgroundRenderingFinished(uint mseconds, uint accelerationFactor);
+
+ /** Resets the acceleration factors to 1 when realtime rendering is disabled. */
+ void slotResetRealtimeFactor(bool realtimeChecked);
+
+};
+
+#endif // ABSTRACTGFXSCOPEWIDGET_H
// See http://doc.qt.nokia.com/latest/qtconcurrentrun.html#run about
// running member functions in a thread
- m_threadScope = QtConcurrent::run(this, &AbstractScopeWidget::renderScope, m_accelFactorScope, m_scopeImage);
+ m_threadScope = QtConcurrent::run(this, &AbstractScopeWidget::renderScope, m_accelFactorScope);
m_requestForcedUpdate = false;
// qDebug() << "Scope thread started in " << m_widgetName;
/** @brief Scope renderer. Must emit signalScopeRenderingFinished()
when calculation has finished, to allow multi-threading.
accelerationFactor hints how much faster than usual the calculation should be accomplished, if possible. */
- virtual QImage renderScope(uint accelerationFactor, const QImage) = 0;
+ virtual QImage renderScope(uint accelerationFactor) = 0;
/** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). @see renderScope */
virtual QImage renderBackground(uint accelerationFactor) = 0;
done in this abstract class. */
void slotActiveMonitorChanged(bool isClipMonitor);
-private slots:
+protected slots:
void customContextMenuRequested(const QPoint &pos);
/** To be called when a new frame has been received.
The scope then decides whether and when it wants to recalculate the scope, depending
#include <QMouseEvent>
#include <QPainter>
-const int REALTIME_FPS = 30;
-
-const QColor light(250, 238, 226, 255);
-const QColor dark(40, 40, 39, 255);
-const QColor dark2(25, 25, 23, 255);
-
-const QPen AbstractAudioScopeWidget::penThick(QBrush(QColor(250, 250, 250)), 2, Qt::SolidLine);
-const QPen AbstractAudioScopeWidget::penThin(QBrush(QColor(250, 250, 250)), 1, Qt::SolidLine);
-const QPen AbstractAudioScopeWidget::penLight(QBrush(QColor(200, 200, 250, 150)), 1, Qt::SolidLine);
-const QPen AbstractAudioScopeWidget::penDark(QBrush(QColor(0, 0, 20, 250)), 1, Qt::SolidLine);
-
AbstractAudioScopeWidget::AbstractAudioScopeWidget(Monitor *projMonitor, Monitor *clipMonitor, bool trackMouse, QWidget *parent) :
- QWidget(parent),
- m_projMonitor(projMonitor),
- m_clipMonitor(clipMonitor),
- m_mousePos(0, 0),
- m_mouseWithinWidget(false),
- offset(5),
- m_accelFactorHUD(1),
- m_accelFactorScope(1),
- m_accelFactorBackground(1),
- m_semaphoreHUD(1),
- m_semaphoreScope(1),
- m_semaphoreBackground(1),
- initialDimensionUpdateDone(false),
- m_requestForcedUpdate(false)
-
-{
- m_scopePalette = QPalette();
- m_scopePalette.setBrush(QPalette::Window, QBrush(dark2));
- m_scopePalette.setBrush(QPalette::Base, QBrush(dark));
- m_scopePalette.setBrush(QPalette::Button, QBrush(dark));
- m_scopePalette.setBrush(QPalette::Text, QBrush(light));
- m_scopePalette.setBrush(QPalette::WindowText, QBrush(light));
- m_scopePalette.setBrush(QPalette::ButtonText, QBrush(light));
- this->setPalette(m_scopePalette);
- this->setAutoFillBackground(true);
-
- m_aAutoRefresh = new QAction(i18n("Auto Refresh"), this);
- m_aAutoRefresh->setCheckable(true);
- m_aRealtime = new QAction(i18n("Realtime (with precision loss)"), this);
- m_aRealtime->setCheckable(true);
-
- m_menu = new QMenu(this);
- m_menu->setPalette(m_scopePalette);
- m_menu->addAction(m_aAutoRefresh);
- m_menu->addAction(m_aRealtime);
-
- this->setContextMenuPolicy(Qt::CustomContextMenu);
-
- m_activeRender = (m_clipMonitor->isActive()) ? m_clipMonitor->render : m_projMonitor->render;
-
- bool b = true;
- b &= connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint)));
-
- //b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
-// b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage)));
-
- b &= connect(this, SIGNAL(signalHUDRenderingFinished(uint, uint)), this, SLOT(slotHUDRenderingFinished(uint, uint)));
- b &= connect(this, SIGNAL(signalScopeRenderingFinished(uint, uint)), this, SLOT(slotScopeRenderingFinished(uint, uint)));
- b &= connect(this, SIGNAL(signalBackgroundRenderingFinished(uint, uint)), this, SLOT(slotBackgroundRenderingFinished(uint, uint)));
- b &= connect(m_aRealtime, SIGNAL(toggled(bool)), this, SLOT(slotResetRealtimeFactor(bool)));
- b &= connect(m_aAutoRefresh, SIGNAL(toggled(bool)), this, SLOT(slotAutoRefreshToggled(bool)));
- Q_ASSERT(b);
-
- // Enable mouse tracking if desired.
- // Causes the mouseMoved signal to be emitted when the mouse moves inside the
- // widget, even when no mouse button is pressed.
- this->setMouseTracking(trackMouse);
-}
-
-AbstractAudioScopeWidget::~AbstractAudioScopeWidget()
-{
- writeConfig();
-
- delete m_menu;
- delete m_aAutoRefresh;
- delete m_aRealtime;
-}
-
-void AbstractAudioScopeWidget::init()
-{
- m_widgetName = widgetName();
- readConfig();
-}
-
-void AbstractAudioScopeWidget::readConfig()
-{
- KSharedConfigPtr config = KGlobal::config();
- KConfigGroup scopeConfig(config, configName());
- m_aAutoRefresh->setChecked(scopeConfig.readEntry("autoRefresh", true));
- m_aRealtime->setChecked(scopeConfig.readEntry("realtime", false));
- scopeConfig.sync();
-}
-
-void AbstractAudioScopeWidget::writeConfig()
-{
- KSharedConfigPtr config = KGlobal::config();
- KConfigGroup scopeConfig(config, configName());
- scopeConfig.writeEntry("autoRefresh", m_aAutoRefresh->isChecked());
- scopeConfig.writeEntry("realtime", m_aRealtime->isChecked());
- scopeConfig.sync();
-}
-
-QString AbstractAudioScopeWidget::configName()
-{
- return "Scope_" + m_widgetName;
-}
-
-void AbstractAudioScopeWidget::prodHUDThread()
-{
- if (this->visibleRegion().isEmpty()) {
-// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating HUD.";
- } else {
- if (m_semaphoreHUD.tryAcquire(1)) {
- Q_ASSERT(!m_threadHUD.isRunning());
-
- m_newHUDFrames.fetchAndStoreRelaxed(0);
- m_newHUDUpdates.fetchAndStoreRelaxed(0);
- m_threadHUD = QtConcurrent::run(this, &AbstractAudioScopeWidget::renderHUD, m_accelFactorHUD);
-// qDebug() << "HUD thread started in " << m_widgetName;
-
- } else {
-// qDebug() << "HUD semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadHUD.isRunning();
- }
- }
-}
-
-void AbstractAudioScopeWidget::prodScopeThread()
-{
- // Only start a new thread if the scope is actually visible
- // and not hidden by another widget on the stack and if user want the scope to update.
- if (this->visibleRegion().isEmpty() || (!m_aAutoRefresh->isChecked() && !m_requestForcedUpdate)) {
-// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating scope.";
- } else {
- // Try to acquire the semaphore. This must only succeed if m_threadScope is not running
- // anymore. Therefore the semaphore must NOT be released before m_threadScope ends.
- // If acquiring the semaphore fails, the thread is still running.
- if (m_semaphoreScope.tryAcquire(1)) {
- Q_ASSERT(!m_threadScope.isRunning());
-
- m_newScopeFrames.fetchAndStoreRelaxed(0);
- m_newScopeUpdates.fetchAndStoreRelaxed(0);
-
- Q_ASSERT(m_accelFactorScope > 0);
-
- // See http://doc.qt.nokia.com/latest/qtconcurrentrun.html#run about
- // running member functions in a thread
- m_threadScope = QtConcurrent::run(this, &AbstractAudioScopeWidget::renderScope,
- m_accelFactorScope, m_audioFrame, m_freq, m_nChannels, m_nSamples);
- m_requestForcedUpdate = false;
-
-// qDebug() << "Scope thread started in " << m_widgetName;
-
- } else {
-// qDebug() << "Scope semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadScope.isRunning();
- }
- }
-}
-void AbstractAudioScopeWidget::prodBackgroundThread()
-{
- if (this->visibleRegion().isEmpty()) {
-// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating background.";
- } else {
- if (m_semaphoreBackground.tryAcquire(1)) {
- Q_ASSERT(!m_threadBackground.isRunning());
-
- m_newBackgroundFrames.fetchAndStoreRelaxed(0);
- m_newBackgroundUpdates.fetchAndStoreRelaxed(0);
- m_threadBackground = QtConcurrent::run(this, &AbstractAudioScopeWidget::renderBackground, m_accelFactorBackground);
-// qDebug() << "Background thread started in " << m_widgetName;
-
- } else {
-// qDebug() << "Background semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadBackground.isRunning();
- }
- }
-}
-
-void AbstractAudioScopeWidget::forceUpdate(bool doUpdate)
-{
-// qDebug() << "Force update called in " << widgetName() << ". Arg: " << doUpdate;
- if (!doUpdate) {
- return;
- }
- m_requestForcedUpdate = true;
- m_newHUDUpdates.fetchAndAddRelaxed(1);
- m_newScopeUpdates.fetchAndAddRelaxed(1);
- m_newBackgroundUpdates.fetchAndAddRelaxed(1);
- prodHUDThread();
- prodScopeThread();
- prodBackgroundThread();
-}
-void AbstractAudioScopeWidget::forceUpdateHUD()
-{
- m_newHUDUpdates.fetchAndAddRelaxed(1);
- prodHUDThread();
-
-}
-void AbstractAudioScopeWidget::forceUpdateScope()
-{
- m_newScopeUpdates.fetchAndAddRelaxed(1);
- m_requestForcedUpdate = true;
- prodScopeThread();
-
-}
-void AbstractAudioScopeWidget::forceUpdateBackground()
-{
- m_newBackgroundUpdates.fetchAndAddRelaxed(1);
- prodBackgroundThread();
-
-}
-
-
-///// Events /////
-
-void AbstractAudioScopeWidget::mouseReleaseEvent(QMouseEvent *event)
-{
- if (!m_aAutoRefresh->isChecked()) {
- m_requestForcedUpdate = true;
- m_activeRender->sendFrameUpdate();
- }
- prodHUDThread();
- prodScopeThread();
- prodBackgroundThread();
- QWidget::mouseReleaseEvent(event);
-}
-
-void AbstractAudioScopeWidget::resizeEvent(QResizeEvent *event)
-{
- // Update the dimension of the available rect for painting
- m_scopeRect = scopeRect();
- forceUpdate();
-
- QWidget::resizeEvent(event);
-}
-
-void AbstractAudioScopeWidget::showEvent(QShowEvent *event)
-{
- QWidget::showEvent(event);
- m_scopeRect = scopeRect();
-}
-
-void AbstractAudioScopeWidget::paintEvent(QPaintEvent *)
-{
- QPainter davinci(this);
- davinci.drawImage(m_scopeRect.topLeft(), m_imgBackground);
- davinci.drawImage(m_scopeRect.topLeft(), m_imgScope);
- davinci.drawImage(m_scopeRect.topLeft(), m_imgHUD);
-}
-
-void AbstractAudioScopeWidget::mouseMoveEvent(QMouseEvent *event)
-{
- m_mousePos = event->pos();
- m_mouseWithinWidget = true;
- emit signalMousePositionChanged();
-}
-void AbstractAudioScopeWidget::leaveEvent(QEvent *)
+ AbstractScopeWidget(projMonitor, clipMonitor, trackMouse, parent)
{
- m_mouseWithinWidget = false;
- emit signalMousePositionChanged();
-}
-
-void AbstractAudioScopeWidget::customContextMenuRequested(const QPoint &pos)
-{
- m_menu->exec(this->mapToGlobal(pos));
-}
-
-uint AbstractAudioScopeWidget::calculateAccelFactorHUD(uint oldMseconds, uint)
-{
- return ceil((float)oldMseconds*REALTIME_FPS / 1000);
-}
-uint AbstractAudioScopeWidget::calculateAccelFactorScope(uint oldMseconds, uint)
-{
- return ceil((float)oldMseconds*REALTIME_FPS / 1000);
-}
-uint AbstractAudioScopeWidget::calculateAccelFactorBackground(uint oldMseconds, uint)
-{
- return ceil((float)oldMseconds*REALTIME_FPS / 1000);
-}
-
-
-///// Slots /////
-
-void AbstractAudioScopeWidget::slotHUDRenderingFinished(uint mseconds, uint oldFactor)
-{
-// qDebug() << "HUD rendering has finished, waiting for termination in " << m_widgetName;
- m_threadHUD.waitForFinished();
- m_imgHUD = m_threadHUD.result();
-
- m_semaphoreHUD.release(1);
- this->update();
-
- int accel;
- if (m_aRealtime->isChecked()) {
- accel = calculateAccelFactorHUD(mseconds, oldFactor);
- if (m_accelFactorHUD < 1) {
- accel = 1;
- }
- m_accelFactorHUD = accel;
- }
-
- if ((m_newHUDFrames > 0 && m_aAutoRefresh->isChecked()) || m_newHUDUpdates > 0) {
-// qDebug() << "Trying to start a new HUD thread for " << m_widgetName
-// << ". New frames/updates: " << m_newHUDFrames << "/" << m_newHUDUpdates;
- prodHUDThread();;
- }
-}
-
-void AbstractAudioScopeWidget::slotScopeRenderingFinished(uint mseconds, uint oldFactor)
-{
- // The signal can be received before the thread has really finished. So we
- // need to wait until it has really finished before starting a new thread.
-// qDebug() << "Scope rendering has finished, waiting for termination in " << m_widgetName;
- m_threadScope.waitForFinished();
- m_imgScope = m_threadScope.result();
-
- // The scope thread has finished. Now we can release the semaphore, allowing a new thread.
- // See prodScopeThread where the semaphore is acquired again.
- m_semaphoreScope.release(1);
- this->update();
-
- // Calculate the acceleration factor hint to get «realtime» updates.
- int accel;
- if (m_aRealtime->isChecked()) {
- accel = calculateAccelFactorScope(mseconds, oldFactor);
- if (accel < 1) {
- // If mseconds happens to be 0.
- accel = 1;
- }
- // Don't directly calculate with m_accelFactorScope as we are dealing with concurrency.
- // If m_accelFactorScope is set to 0 at the wrong moment, who knows what might happen
- // then :) Therefore use a local variable.
- m_accelFactorScope = accel;
- }
-
- if ((m_newScopeFrames > 0 && m_aAutoRefresh->isChecked()) || m_newScopeUpdates > 0) {
-// qDebug() << "Trying to start a new scope thread for " << m_widgetName
-// << ". New frames/updates: " << m_newScopeFrames << "/" << m_newScopeUpdates;
- prodScopeThread();
- }
-}
-
-void AbstractAudioScopeWidget::slotBackgroundRenderingFinished(uint mseconds, uint oldFactor)
-{
-// qDebug() << "Background rendering has finished, waiting for termination in " << m_widgetName;
- m_threadBackground.waitForFinished();
- m_imgBackground = m_threadBackground.result();
-
- m_semaphoreBackground.release(1);
- this->update();
-
- int accel;
- if (m_aRealtime->isChecked()) {
- accel = calculateAccelFactorBackground(mseconds, oldFactor);
- if (m_accelFactorBackground < 1) {
- accel = 1;
- }
- m_accelFactorBackground = accel;
- }
-
- if ((m_newBackgroundFrames > 0 && m_aAutoRefresh->isChecked()) || m_newBackgroundUpdates > 0) {
-// qDebug() << "Trying to start a new background thread for " << m_widgetName
-// << ". New frames/updates: " << m_newBackgroundFrames << "/" << m_newBackgroundUpdates;
- prodBackgroundThread();;
- }
-}
-
-//void AbstractAudioScopeWidget::slotActiveMonitorChanged(bool isClipMonitor)
-//{
-//// qDebug() << "Active monitor has changed in " << m_widgetName << ". Is the clip monitor active now? " << isClipMonitor;
-
-// bool b = m_activeRender->disconnect(this);
-// Q_ASSERT(b);
-
-// m_activeRender = (isClipMonitor) ? m_clipMonitor->render : m_projMonitor->render;
-
-// //b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
-// b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage)));
-// Q_ASSERT(b);
-
-// // Update the scope for the new monitor.
-// prodHUDThread();
-// prodScopeThread();
-// prodBackgroundThread();
-//}
-
-void AbstractAudioScopeWidget::slotRenderZoneUpdated()
-{
- m_newHUDFrames.fetchAndAddRelaxed(1);
- m_newScopeFrames.fetchAndAddRelaxed(1);
- m_newBackgroundFrames.fetchAndAddRelaxed(1);
-
-// qDebug() << "Audio incoming. New frames total HUD/Scope/Background: " << m_newHUDFrames
-// << "/" << m_newScopeFrames << "/" << m_newBackgroundFrames;
-
- if (this->visibleRegion().isEmpty()) {
-// qDebug() << "Scope of widget " << m_widgetName << " is not at the top, not rendering.";
- } else {
- if (m_aAutoRefresh->isChecked()) {
- prodHUDThread();
- prodScopeThread();
- prodBackgroundThread();
- }
- }
}
void AbstractAudioScopeWidget::slotReceiveAudio(const QVector<int16_t>& sampleData, int freq, int num_channels, int num_samples)
{
- //qDebug() << "Received audio. Size is " << (int) sampleData.size() << ".";
- if (sampleData.size() > 0) {
- //qDebug() << "Received: " << sampleData.data()[0] << ", " << sampleData.data()[1] << ", " << sampleData.data()[2];
- }
m_audioFrame = sampleData;
m_freq = freq;
m_nChannels = num_channels;
m_nSamples = num_samples;
- slotRenderZoneUpdated();
+ AbstractScopeWidget::slotRenderZoneUpdated();
//TODO
}
-void AbstractAudioScopeWidget::slotResetRealtimeFactor(bool realtimeChecked)
-{
- if (!realtimeChecked) {
- m_accelFactorHUD = 1;
- m_accelFactorScope = 1;
- m_accelFactorBackground = 1;
- }
-}
-
-bool AbstractAudioScopeWidget::autoRefreshEnabled()
-{
- return m_aAutoRefresh->isChecked();
-}
+AbstractAudioScopeWidget::~AbstractAudioScopeWidget() {}
-void AbstractAudioScopeWidget::slotAutoRefreshToggled(bool autoRefresh)
+QImage AbstractAudioScopeWidget::renderScope(uint accelerationFactor)
{
- if (isVisible()) emit requestAutoRefresh(autoRefresh);
- // TODO only if depends on input
- if (autoRefresh) {
- //forceUpdate();
- m_requestForcedUpdate = true;
- m_activeRender->sendFrameUpdate();
- }
+ return renderAudioScope(accelerationFactor, m_audioFrame, m_freq, m_nChannels, m_nSamples);
}
#include <QtCore>
#include <QWidget>
+#include "abstractscopewidget.h"
+
class QMenu;
class Monitor;
class Render;
-class AbstractAudioScopeWidget : public QWidget
+class AbstractAudioScopeWidget : public AbstractScopeWidget
{
Q_OBJECT
public:
AbstractAudioScopeWidget(Monitor *projMonitor, Monitor *clipMonitor, bool trackMouse = false, QWidget *parent = 0);
- virtual ~AbstractAudioScopeWidget(); // Must be virtual because of inheritance, to avoid memory leaks
- QPalette m_scopePalette;
-
- /** Initializes widget settings (reads configuration).
- Has to be called in the implementing object. */
- void init();
-
- /** Does this scope have auto-refresh enabled */
- bool autoRefreshEnabled();
-
- ///// Unimplemented /////
-
- virtual QString widgetName() const = 0;
-
- ///// Variables /////
- static const QPen penThick;
- static const QPen penThin;
- static const QPen penLight;
- static const QPen penDark;
+ virtual ~AbstractAudioScopeWidget();
protected:
- ///// Variables /////
-
- Monitor *m_projMonitor;
- Monitor *m_clipMonitor;
- Render *m_activeRender;
-
-
- /** The context menu. Feel free to add new entries in your implementation. */
- QMenu *m_menu;
-
- /** Enables auto refreshing of the scope.
- This is when a new frame is shown on the active monitor.
- Resize events always force a recalculation. */
- QAction *m_aAutoRefresh;
-
- /** Realtime rendering. Should be disabled if it is not supported.
- Use the accelerationFactor variable passed to the render functions as a hint of
- how many times faster the scope should be calculated. */
- QAction *m_aRealtime;
-
- /** The mouse position; Updated when the mouse enters the widget
- AND mouse tracking has been enabled. */
- QPoint m_mousePos;
- /** Knows whether the mouse currently lies within the widget or not.
- Can e.g. be used for drawing a HUD only when the mouse is in the widget. */
- bool m_mouseWithinWidget;
-
- /** Offset from the widget's borders */
- const uchar offset;
-
- /** The rect on the widget we're painting in.
- Can be used by the implementing widget, e.g. in the render methods.
- Is updated when necessary (size changes). */
- QRect m_scopeRect;
-
- /** Images storing the calculated layers. Will be used on repaint events. */
- QImage m_imgHUD;
- QImage m_imgScope;
- QImage m_imgBackground;
-
- /** The acceleration factors can be accessed also by other renderer tasks,
- e.g. to display the scope's acceleration factor in the HUD renderer. */
- int m_accelFactorHUD;
- int m_accelFactorScope;
- int m_accelFactorBackground;
-
- /** Reads the widget's configuration.
- Can be extended in the implementing subclass (make sure to run readConfig as well). */
- virtual void readConfig();
- /** Writes the widget configuration.
- Implementing widgets have to implement an own method and run it in their destructor. */
- void writeConfig();
- /** Identifier for the widget's configuration. */
- QString configName();
-
+ virtual QImage renderScope(uint accelerationFactor);
///// Unimplemented Methods /////
-
- /** Where on the widget we can paint in.
- May also update other variables that depend on the widget's size. */
- virtual QRect scopeRect() = 0;
-
- /** @brief HUD renderer. Must emit signalHUDRenderingFinished(). @see renderScope */
- virtual QImage renderHUD(uint accelerationFactor) = 0;
/** @brief Scope renderer. Must emit signalScopeRenderingFinished()
when calculation has finished, to allow multi-threading.
accelerationFactor hints how much faster than usual the calculation should be accomplished, if possible. */
- virtual QImage renderScope(uint accelerationFactor,
+ virtual QImage renderAudioScope(uint accelerationFactor,
const QVector<int16_t> audioFrame, const int freq, const int num_channels, const int num_samples) = 0;
- /** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). @see renderScope */
- virtual QImage renderBackground(uint accelerationFactor) = 0;
-
- /** Must return true if the HUD layer depends on the input monitor.
- If it does not, then it does not need to be re-calculated when
- a new frame from the monitor is incoming. */
- virtual bool isHUDDependingOnInput() const = 0;
- /** @see isHUDDependingOnInput() */
- virtual bool isScopeDependingOnInput() const = 0;
- /** @see isHUDDependingOnInput() */
- virtual bool isBackgroundDependingOnInput() const = 0;
-
- ///// Can be reimplemented /////
- /** Calculates the acceleration factor to be used by the render thread.
- This method can be refined in the subclass if required. */
- virtual uint calculateAccelFactorHUD(uint oldMseconds, uint oldFactor);
- virtual uint calculateAccelFactorScope(uint oldMseconds, uint oldFactor);
- virtual uint calculateAccelFactorBackground(uint oldMseconds, uint oldFactor);
-
- ///// Reimplemented /////
-
- void mouseMoveEvent(QMouseEvent *);
- void leaveEvent(QEvent *);
- void mouseReleaseEvent(QMouseEvent *);
- void paintEvent(QPaintEvent *);
- void resizeEvent(QResizeEvent *);
- void showEvent(QShowEvent *); // Called when the widget is activated via the Menu entry
- // void raise(); // Called only when manually calling the event -> useless
-
-
-protected slots:
- /** Forces an update of all layers. */
- void forceUpdate(bool doUpdate = true);
- void forceUpdateHUD();
- void forceUpdateScope();
- void forceUpdateBackground();
- void slotAutoRefreshToggled(bool);
-
-signals:
- /** mseconds represent the time taken for the calculation,
- accelerationFactor is the acceleration factor that has been used. */
- void signalHUDRenderingFinished(uint mseconds, uint accelerationFactor);
- void signalScopeRenderingFinished(uint mseconds, uint accelerationFactor);
- void signalBackgroundRenderingFinished(uint mseconds, uint accelerationFactor);
-
- /** For the mouse position itself see m_mousePos.
- To check whether the mouse has leaved the widget, see m_mouseWithinWidget. */
- void signalMousePositionChanged();
-
- /** Do we need the renderer to send its frames to us? */
- void requestAutoRefresh(bool);
private:
-
- /** Counts the number of frames that have been rendered in the active monitor.
- The frame number will be reset when the calculation starts for the current frame. */
- QAtomicInt m_newHUDFrames;
- QAtomicInt m_newScopeFrames;
- QAtomicInt m_newBackgroundFrames;
-
- /** Counts the number of updates that, unlike new frames, force a recalculation
- of the scope, like for example a resize event. */
- QAtomicInt m_newHUDUpdates;
- QAtomicInt m_newScopeUpdates;
- QAtomicInt m_newBackgroundUpdates;
-
- /** The semaphores ensure that the QFutures for the HUD/Scope/Background threads cannot
- be assigned a new thread while it is still running. (Could cause deadlocks and other
- nasty things known from parallelism.) */
- QSemaphore m_semaphoreHUD;
- QSemaphore m_semaphoreScope;
- QSemaphore m_semaphoreBackground;
-
- QFuture<QImage> m_threadHUD;
- QFuture<QImage> m_threadScope;
- QFuture<QImage> m_threadBackground;
-
- bool initialDimensionUpdateDone;
- bool m_requestForcedUpdate;
-
-// QImage m_scopeImage;
- QVector<int16_t> m_audioFrame; //NEW
+ QVector<int16_t> m_audioFrame;
int m_freq;
int m_nChannels;
int m_nSamples;
- QString m_widgetName;
-
- void prodHUDThread();
- void prodScopeThread();
- void prodBackgroundThread();
-
-public slots:
- /** @brief Must be called when the active monitor has shown a new frame.
- This slot must be connected in the implementing class, it is *not*
- done in this abstract class. */
-// void slotActiveMonitorChanged(bool isClipMonitor);
-
private slots:
- void customContextMenuRequested(const QPoint &pos);
- /** To be called when a new frame has been received.
- The scope then decides whether and when it wants to recalculate the scope, depending
- on whether it is currently visible and whether a calculation thread is already running. */
- void slotRenderZoneUpdated();
-// void slotRenderZoneUpdated(QImage);//OLD
void slotReceiveAudio(const QVector<int16_t>& sampleData, int freq, int num_channels, int num_samples); // NEW, TODO comment
- /** The following slots are called when rendering of a component has finished. They e.g. update
- the widget and decide whether to immediately restart the calculation thread. */
- void slotHUDRenderingFinished(uint mseconds, uint accelerationFactor);
- void slotScopeRenderingFinished(uint mseconds, uint accelerationFactor);
- void slotBackgroundRenderingFinished(uint mseconds, uint accelerationFactor);
-
- /** Resets the acceleration factors to 1 when realtime rendering is disabled. */
- void slotResetRealtimeFactor(bool realtimeChecked);
};
+/***************************************************************************
+ * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
+ * This file is part of kdenlive. See www.kdenlive.org. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+
#include "audiospectrum.h"
#include "tools/kiss_fftr.h"
b &= connect(ui->windowSize, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCfg()));
Q_ASSERT(b);
- init();
+ AbstractScopeWidget::init();
}
AudioSpectrum::~AudioSpectrum()
{
void AudioSpectrum::readConfig()
{
- AbstractAudioScopeWidget::readConfig();
+ AbstractScopeWidget::readConfig();
KSharedConfigPtr config = KGlobal::config();
- KConfigGroup scopeConfig(config, configName());
+ KConfigGroup scopeConfig(config, AbstractScopeWidget::configName());
QString scale = scopeConfig.readEntry("scale");
if (scale == "lin") {
m_aLin->setChecked(true);
void AudioSpectrum::writeConfig()
{
KSharedConfigPtr config = KGlobal::config();
- KConfigGroup scopeConfig(config, configName());
+ KConfigGroup scopeConfig(config, AbstractScopeWidget::configName());
QString scale;
if (m_aLin->isChecked()) {
scale = "lin";
bool AudioSpectrum::isHUDDependingOnInput() const { return false; }
QImage AudioSpectrum::renderBackground(uint) { return QImage(); }
-QImage AudioSpectrum::renderScope(uint, const QVector<int16_t> audioFrame, const int freq, const int num_channels, const int num_samples)
+QImage AudioSpectrum::renderAudioScope(uint, const QVector<int16_t> audioFrame, const int freq, const int num_channels, const int num_samples)
{
if (audioFrame.size() > 63) {
m_freqMax = freq / 2;
val = pow(pow(fabs(freqData[i].r),2) + pow(fabs(freqData[i].i),2), .5);
}
freqSpectrum[i] = val;
- // qDebug() << val;
}
}
- qDebug() << "max freq: " << m_freqMax;
const uint hzDiff = ceil( ((float)minDistX)/rect.width() * m_freqMax / 1000 ) * 1000;
- qDebug() << hzDiff;
int x;
- for (int hz = hzDiff; hz < m_freqMax; hz += hzDiff) {
+ for (uint hz = hzDiff; hz < m_freqMax; hz += hzDiff) {
x = rect.width() * ((float)hz)/m_freqMax;
davinci.drawLine(x, 0, x, rect.height()+4);
davinci.drawText(x-4, rect.height() + 20, QVariant(hz/1000).toString());
+/***************************************************************************
+ * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
+ * This file is part of kdenlive. See www.kdenlive.org. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+
#ifndef AUDIOSPECTRUM_H
#define AUDIOSPECTRUM_H
///// Implemented methods /////
QRect scopeRect();
QImage renderHUD(uint accelerationFactor);
- QImage renderScope(uint accelerationFactor, const QVector<int16_t> audioFrame, const int freq, const int num_channels, const int num_samples);
+ QImage renderAudioScope(uint accelerationFactor, const QVector<int16_t> audioFrame, const int freq, const int num_channels, const int num_samples);
QImage renderBackground(uint accelerationFactor);
bool isHUDDependingOnInput() const;
bool isScopeDependingOnInput() const;
#include "renderer.h"
Histogram::Histogram(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) :
- AbstractScopeWidget(projMonitor, clipMonitor, false, parent)
+ AbstractGfxScopeWidget(projMonitor, clipMonitor, false, parent)
{
ui = new Ui::Histogram_UI();
ui->setupUi(this);
void Histogram::readConfig()
{
- AbstractScopeWidget::readConfig();
+ AbstractGfxScopeWidget::readConfig();
KSharedConfigPtr config = KGlobal::config();
KConfigGroup scopeConfig(config, configName());
#ifndef HISTOGRAM_H
#define HISTOGRAM_H
-#include "abstractscopewidget.h"
+#include "abstractgfxscopewidget.h"
#include "ui_histogram_ui.h"
class HistogramGenerator;
-class Histogram : public AbstractScopeWidget {
+class Histogram : public AbstractGfxScopeWidget {
Q_OBJECT
public:
return;
} else {
for (int i = 0; i < m_scopesList.count(); i++) {
- if (m_scopesList.at(i)->isVisible() && tabifiedDockWidgets(m_scopesList.at(i)).isEmpty() && static_cast<AbstractScopeWidget *>(m_scopesList.at(i)->widget())->autoRefreshEnabled()) {
+ if (m_scopesList.at(i)->isVisible() && tabifiedDockWidgets(m_scopesList.at(i)).isEmpty() && static_cast<AbstractGfxScopeWidget *>(m_scopesList.at(i)->widget())->autoRefreshEnabled()) {
request = true;
break;
}
// Check scopes
bool request = false;
for (int i = 0; i < m_scopesList.count(); i++) {
- if (!m_scopesList.at(i)->widget()->visibleRegion().isEmpty() && static_cast<AbstractScopeWidget *>(m_scopesList.at(i)->widget())->autoRefreshEnabled()) {
- kDebug() << "SCOPE VISIBLE: " << static_cast<AbstractScopeWidget *>(m_scopesList.at(i)->widget())->widgetName();
+ if (!m_scopesList.at(i)->widget()->visibleRegion().isEmpty() && static_cast<AbstractGfxScopeWidget *>(m_scopesList.at(i)->widget())->autoRefreshEnabled()) {
+ kDebug() << "SCOPE VISIBLE: " << static_cast<AbstractGfxScopeWidget *>(m_scopesList.at(i)->widget())->widgetName();
request = true;
break;
}
bool request = false;
for (int i = 0; i < m_scopesList.count(); i++) {
// Check if we need the renderer to send a new frame for update
- if (!m_scopesList.at(i)->widget()->visibleRegion().isEmpty() && !(static_cast<AbstractScopeWidget *>(m_scopesList.at(i)->widget())->autoRefreshEnabled())) request = true;
- static_cast<AbstractScopeWidget *>(m_scopesList.at(i)->widget())->slotActiveMonitorChanged(m_clipMonitor->isActive());
+ if (!m_scopesList.at(i)->widget()->visibleRegion().isEmpty() && !(static_cast<AbstractGfxScopeWidget *>(m_scopesList.at(i)->widget())->autoRefreshEnabled())) request = true;
+ static_cast<AbstractGfxScopeWidget *>(m_scopesList.at(i)->widget())->slotActiveMonitorChanged(m_clipMonitor->isActive());
}
if (request) {
if (m_clipMonitor->isActive()) m_clipMonitor->render->sendFrameUpdate();
for (int i = 0; i < m_scopesList.count(); i++) {
// Check if we need the renderer to send a new frame for update
/*if (!m_scopesList.at(i)->widget()->visibleRegion().isEmpty() && !(static_cast<AbstractScopeWidget *>(m_scopesList.at(i)->widget())->autoRefreshEnabled())) request = true;*/
- connect(m_stopmotion, SIGNAL(gotFrame(QImage)), static_cast<AbstractScopeWidget *>(m_scopesList.at(i)->widget()), SLOT(slotRenderZoneUpdated(QImage)));
+ connect(m_stopmotion, SIGNAL(gotFrame(QImage)), static_cast<AbstractGfxScopeWidget *>(m_scopesList.at(i)->widget()), SLOT(slotRenderZoneUpdated(QImage)));
//static_cast<AbstractScopeWidget *>(m_scopesList.at(i)->widget())->slotMonitorCapture();
}
}
#include "rgbparadegenerator.h"
RGBParade::RGBParade(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) :
- AbstractScopeWidget(projMonitor, clipMonitor, true, parent)
+ AbstractGfxScopeWidget(projMonitor, clipMonitor, true, parent)
{
ui = new Ui::RGBParade_UI();
ui->setupUi(this);
void RGBParade::readConfig()
{
- AbstractScopeWidget::readConfig();
+ AbstractGfxScopeWidget::readConfig();
KSharedConfigPtr config = KGlobal::config();
KConfigGroup scopeConfig(config, configName());
#define RGBPARADE_H
#include <QObject>
-#include "abstractscopewidget.h"
+#include "abstractgfxscopewidget.h"
#include "ui_rgbparade_ui.h"
class Monitor;
class RGBParade_UI;
class RGBParadeGenerator;
-class RGBParade : public AbstractScopeWidget
+class RGBParade : public AbstractGfxScopeWidget
{
public:
RGBParade(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent = 0);
Vectorscope::Vectorscope(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) :
- AbstractScopeWidget(projMonitor, clipMonitor, true, parent),
+ AbstractGfxScopeWidget(projMonitor, clipMonitor, true, parent),
m_gain(1)
{
ui = new Ui::Vectorscope_UI();
void Vectorscope::readConfig()
{
- AbstractScopeWidget::readConfig();
+ AbstractGfxScopeWidget::readConfig();
KSharedConfigPtr config = KGlobal::config();
KConfigGroup scopeConfig(config, configName());
#include <QtCore>
#include "ui_vectorscope_ui.h"
-#include "abstractscopewidget.h"
+#include "abstractgfxscopewidget.h"
class ColorPlaneExport;
class ColorTools;
enum BACKGROUND_MODE { BG_NONE = 0, BG_YUV = 1, BG_CHROMA = 2, BG_YPbPr = 3 };
-class Vectorscope : public AbstractScopeWidget {
+class Vectorscope : public AbstractGfxScopeWidget {
Q_OBJECT
public:
const int Waveform::m_paddingBottom(20);
Waveform::Waveform(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) :
- AbstractScopeWidget(projMonitor, clipMonitor, true, parent)
+ AbstractGfxScopeWidget(projMonitor, clipMonitor, true, parent)
{
ui = new Ui::Waveform_UI();
ui->setupUi(this);
void Waveform::readConfig()
{
- AbstractScopeWidget::readConfig();
+ AbstractGfxScopeWidget::readConfig();
KSharedConfigPtr config = KGlobal::config();
KConfigGroup scopeConfig(config, configName());
#ifndef WAVEFORM_H
#define WAVEFORM_H
-#include "abstractscopewidget.h"
+#include "abstractgfxscopewidget.h"
#include "ui_waveform_ui.h"
class Waveform_UI;
class WaveformGenerator;
-class Waveform : public AbstractScopeWidget {
+class Waveform : public AbstractGfxScopeWidget {
Q_OBJECT
public: