File indexing completed on 2024-04-21 04:52:05
0001 /* 0002 SPDX-FileCopyrightText: 2011 Simon Andreas Eugster <simon.eu@gmail.com> 0003 This file is part of kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "scopemanager.h" 0009 #include "audioscopes/audiosignal.h" 0010 #include "audioscopes/audiospectrum.h" 0011 #include "audioscopes/spectrogram.h" 0012 #include "colorscopes/histogram.h" 0013 #include "colorscopes/rgbparade.h" 0014 #include "colorscopes/vectorscope.h" 0015 #include "colorscopes/waveform.h" 0016 #include "core.h" 0017 #include "definitions.h" 0018 #include "kdenlivesettings.h" 0019 #include "mainwindow.h" 0020 #include "monitor/monitormanager.h" 0021 0022 #include "klocalizedstring.h" 0023 #include <QDockWidget> 0024 #include <QSignalMapper> 0025 0026 //#define DEBUG_SM 0027 #ifdef DEBUG_SM 0028 #include <QDebug> 0029 #endif 0030 0031 ScopeManager::ScopeManager(QObject *parent) 0032 : QObject(parent) 0033 0034 { 0035 connect(pCore->monitorManager(), &MonitorManager::checkColorScopes, this, &ScopeManager::slotUpdateActiveRenderer); 0036 connect(pCore->monitorManager(), &MonitorManager::clearScopes, this, &ScopeManager::slotClearColorScopes); 0037 connect(pCore->monitorManager(), &MonitorManager::checkScopes, this, &ScopeManager::slotCheckActiveScopes); 0038 0039 slotUpdateActiveRenderer(); 0040 0041 createScopes(); 0042 } 0043 0044 bool ScopeManager::addScope(AbstractAudioScopeWidget *audioScope, QDockWidget *audioScopeWidget) 0045 { 0046 bool added = false; 0047 int exists = 0; 0048 // Only add the scope if it does not exist yet in the list 0049 for (auto &m_audioScope : m_audioScopes) { 0050 if (m_audioScope.scope == audioScope) { 0051 exists = 1; 0052 break; 0053 } 0054 } 0055 if (exists == 0) { 0056 // Add scope to the list, set up signal/slot connections 0057 #ifdef DEBUG_SM 0058 qCDebug(KDENLIVE_LOG) << "Adding scope to scope manager: " << audioScope->widgetName(); 0059 #endif 0060 0061 AudioScopeData asd; 0062 asd.scope = audioScope; 0063 m_audioScopes.append(asd); 0064 0065 connect(audioScope, &AbstractScopeWidget::requestAutoRefresh, this, &ScopeManager::slotCheckActiveScopes); 0066 if (audioScopeWidget != nullptr) { 0067 connect(audioScopeWidget, &QDockWidget::visibilityChanged, this, &ScopeManager::slotCheckActiveScopes); 0068 connect(audioScopeWidget, &QDockWidget::visibilityChanged, this, [this, audioScope]() { slotRequestFrame(QString(audioScope->widgetName())); }); 0069 } 0070 0071 added = true; 0072 } 0073 return added; 0074 } 0075 bool ScopeManager::addScope(AbstractGfxScopeWidget *colorScope, QDockWidget *colorScopeWidget) 0076 { 0077 bool added = false; 0078 int exists = 0; 0079 for (auto &m_colorScope : m_colorScopes) { 0080 if (m_colorScope.scope == colorScope) { 0081 exists = 1; 0082 break; 0083 } 0084 } 0085 if (exists == 0) { 0086 #ifdef DEBUG_SM 0087 qCDebug(KDENLIVE_LOG) << "Adding scope to scope manager: " << colorScope->widgetName(); 0088 #endif 0089 0090 GfxScopeData gsd; 0091 gsd.scope = colorScope; 0092 m_colorScopes.append(gsd); 0093 0094 connect(colorScope, &AbstractScopeWidget::requestAutoRefresh, this, &ScopeManager::slotCheckActiveScopes); 0095 connect(colorScope, &AbstractGfxScopeWidget::signalFrameRequest, this, &ScopeManager::slotRequestFrame); 0096 connect(colorScope, &AbstractScopeWidget::signalScopeRenderingFinished, this, &ScopeManager::slotScopeReady); 0097 if (colorScopeWidget != nullptr) { 0098 connect(colorScopeWidget, &QDockWidget::visibilityChanged, this, &ScopeManager::slotCheckActiveScopes); 0099 connect(colorScopeWidget, &QDockWidget::visibilityChanged, this, [this, colorScope]() { slotRequestFrame(QString(colorScope->widgetName())); }); 0100 } 0101 0102 added = true; 0103 } 0104 return added; 0105 } 0106 0107 void ScopeManager::slotDistributeAudio(const audioShortVector &sampleData, int freq, int num_channels, int num_samples) 0108 { 0109 #ifdef DEBUG_SM 0110 qCDebug(KDENLIVE_LOG) << "ScopeManager: Starting to distribute audio."; 0111 #endif 0112 for (auto &m_audioScope : m_audioScopes) { 0113 // Distribute audio to all scopes that are visible and want to be refreshed 0114 if (!m_audioScope.scope->visibleRegion().isEmpty()) { 0115 if (m_audioScope.scope->autoRefreshEnabled()) { 0116 m_audioScope.scope->slotReceiveAudio(sampleData, freq, num_channels, num_samples); 0117 #ifdef DEBUG_SM 0118 qCDebug(KDENLIVE_LOG) << "ScopeManager: Distributed audio to " << m_audioScopes[i].scope->widgetName(); 0119 #endif 0120 } 0121 } 0122 } 0123 } 0124 void ScopeManager::slotDistributeFrame(const QImage &image) 0125 { 0126 #ifdef DEBUG_SM 0127 qCDebug(KDENLIVE_LOG) << "ScopeManager: Starting to distribute frame."; 0128 #endif 0129 for (auto &m_colorScope : m_colorScopes) { 0130 if (!m_colorScope.scope->visibleRegion().isEmpty()) { 0131 if (m_colorScope.scope->autoRefreshEnabled()) { 0132 m_colorScope.scope->slotRenderZoneUpdated(image); 0133 #ifdef DEBUG_SM 0134 qCDebug(KDENLIVE_LOG) << "ScopeManager: Distributed frame to " << m_colorScopes[i].scope->widgetName(); 0135 #endif 0136 } else if (m_colorScope.singleFrameRequested) { 0137 // Special case: Auto refresh is disabled, but user requested an update (e.g. by clicking). 0138 // Force the scope to update. 0139 m_colorScope.singleFrameRequested = false; 0140 m_colorScope.scope->slotRenderZoneUpdated(image); 0141 m_colorScope.scope->forceUpdateScope(); 0142 #ifdef DEBUG_SM 0143 qCDebug(KDENLIVE_LOG) << "ScopeManager: Distributed forced frame to " << m_colorScopes[i].scope->widgetName(); 0144 #endif 0145 } 0146 } 0147 } 0148 // checkActiveColourScopes(); 0149 } 0150 0151 void ScopeManager::slotScopeReady() 0152 { 0153 if (m_lastConnectedRenderer) { 0154 Q_EMIT m_lastConnectedRenderer->scopesClear(); 0155 } 0156 } 0157 0158 void ScopeManager::slotRequestFrame(const QString &widgetName) 0159 { 0160 #ifdef DEBUG_SM 0161 qCDebug(KDENLIVE_LOG) << "ScopeManager: New frame was requested by " << widgetName; 0162 #endif 0163 0164 // Search for the scope in the lists and tag it to trigger a forced update 0165 // in the distribution slots 0166 for (auto &m_colorScope : m_colorScopes) { 0167 if (m_colorScope.scope->widgetName() == widgetName) { 0168 m_colorScope.singleFrameRequested = true; 0169 break; 0170 } 0171 } 0172 for (auto &m_audioScope : m_audioScopes) { 0173 if (m_audioScope.scope->widgetName() == widgetName) { 0174 m_audioScope.singleFrameRequested = true; 0175 break; 0176 } 0177 } 0178 if (m_lastConnectedRenderer) { 0179 // TODO: trigger refresh? 0180 m_lastConnectedRenderer->refreshMonitorIfActive(); 0181 // m_lastConnectedRenderer->sendFrameUpdate(); 0182 } 0183 } 0184 0185 void ScopeManager::slotClearColorScopes() 0186 { 0187 m_lastConnectedRenderer = nullptr; 0188 } 0189 0190 void ScopeManager::slotUpdateActiveRenderer() 0191 { 0192 // Disconnect old connections 0193 if (m_lastConnectedRenderer != nullptr) { 0194 #ifdef DEBUG_SM 0195 qCDebug(KDENLIVE_LOG) << "Disconnected previous renderer: " << m_lastConnectedRenderer->id(); 0196 #endif 0197 m_lastConnectedRenderer->disconnect(this); 0198 } 0199 0200 m_lastConnectedRenderer = pCore->monitorManager()->activeMonitor(); 0201 0202 // Connect new renderer 0203 if (m_lastConnectedRenderer != nullptr) { 0204 connect(m_lastConnectedRenderer, &Monitor::frameUpdated, this, &ScopeManager::slotDistributeFrame, Qt::UniqueConnection); 0205 connect(m_lastConnectedRenderer, &Monitor::audioSamplesSignal, this, &ScopeManager::slotDistributeAudio, Qt::UniqueConnection); 0206 0207 #ifdef DEBUG_SM 0208 qCDebug(KDENLIVE_LOG) << "Renderer connected to ScopeManager: " << m_lastConnectedRenderer->id(); 0209 #endif 0210 0211 if (imagesAcceptedByScopes()) { 0212 #ifdef DEBUG_SM 0213 qCDebug(KDENLIVE_LOG) << "Some scopes accept images, triggering frame update."; 0214 #endif 0215 m_lastConnectedRenderer->refreshMonitorIfActive(); 0216 } 0217 } 0218 } 0219 0220 void ScopeManager::slotCheckActiveScopes() 0221 { 0222 #ifdef DEBUG_SM 0223 qCDebug(KDENLIVE_LOG) << "Checking active scopes …"; 0224 #endif 0225 // Leave a small delay to make sure that scope widget has been shown or hidden 0226 QTimer::singleShot(500, this, &ScopeManager::checkActiveAudioScopes); 0227 QTimer::singleShot(500, this, &ScopeManager::checkActiveColourScopes); 0228 } 0229 0230 bool ScopeManager::audioAcceptedByScopes() const 0231 { 0232 bool accepted = pCore->audioMixerVisible; 0233 for (auto m_audioScope : m_audioScopes) { 0234 if (m_audioScope.scope->isVisible() && m_audioScope.scope->autoRefreshEnabled()) { 0235 accepted = true; 0236 break; 0237 } 0238 } 0239 #ifdef DEBUG_SM 0240 qCDebug(KDENLIVE_LOG) << "Any scope accepting audio? " << accepted; 0241 #endif 0242 return accepted; 0243 } 0244 bool ScopeManager::imagesAcceptedByScopes() const 0245 { 0246 bool accepted = false; 0247 for (auto m_colorScope : m_colorScopes) { 0248 if (!m_colorScope.scope->visibleRegion().isEmpty() && m_colorScope.scope->autoRefreshEnabled()) { 0249 accepted = true; 0250 break; 0251 } 0252 } 0253 #ifdef DEBUG_SM 0254 qCDebug(KDENLIVE_LOG) << "Any scope accepting images? " << accepted; 0255 #endif 0256 return accepted; 0257 } 0258 0259 void ScopeManager::checkActiveAudioScopes() 0260 { 0261 bool audioStillRequested = audioAcceptedByScopes(); 0262 0263 #ifdef DEBUG_SM 0264 qCDebug(KDENLIVE_LOG) << "ScopeManager: New audio data still requested? " << audioStillRequested; 0265 #endif 0266 KdenliveSettings::setMonitor_audio(audioStillRequested); 0267 pCore->monitorManager()->slotUpdateAudioMonitoring(); 0268 } 0269 0270 void ScopeManager::checkActiveColourScopes() 0271 { 0272 bool imageStillRequested = imagesAcceptedByScopes(); 0273 0274 #ifdef DEBUG_SM 0275 qCDebug(KDENLIVE_LOG) << "ScopeManager: New frames still requested? " << imageStillRequested; 0276 #endif 0277 0278 // Notify monitors whether frames are still required 0279 Monitor *monitor; 0280 monitor = static_cast<Monitor *>(pCore->monitorManager()->monitor(Kdenlive::ProjectMonitor)); 0281 if (monitor != nullptr) { 0282 monitor->sendFrameForAnalysis(imageStillRequested); 0283 } 0284 0285 monitor = static_cast<Monitor *>(pCore->monitorManager()->monitor(Kdenlive::ClipMonitor)); 0286 if (monitor != nullptr) { 0287 monitor->sendFrameForAnalysis(imageStillRequested); 0288 } 0289 } 0290 0291 void ScopeManager::createScopes() 0292 { 0293 createScopeDock(new Vectorscope(pCore->window()), i18n("Vectorscope"), QStringLiteral("vectorscope")); 0294 createScopeDock(new Waveform(pCore->window()), i18n("Waveform"), QStringLiteral("waveform")); 0295 createScopeDock(new RGBParade(pCore->window()), i18n("RGB Parade"), QStringLiteral("rgb_parade")); 0296 createScopeDock(new Histogram(pCore->window()), i18n("Histogram"), QStringLiteral("histogram")); 0297 // Deprecated scopes 0298 // createScopeDock(new Spectrogram(pCore->window()), i18n("Spectrogram")); 0299 // createScopeDock(new AudioSignal(pCore->window()), i18n("Audio Signal")); 0300 // createScopeDock(new AudioSpectrum(pCore->window()), i18n("AudioSpectrum")); 0301 } 0302 0303 template <class T> void ScopeManager::createScopeDock(T *scopeWidget, const QString &title, const QString &name) 0304 { 0305 QDockWidget *dock = pCore->window()->addDock(title, name, scopeWidget); 0306 addScope(scopeWidget, dock); 0307 0308 // close for initial layout 0309 // actual state will be restored by session management 0310 dock->close(); 0311 }