Commit 81ed3299 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Fix audiothumbnails crash. Now we process audio thumbs one by one instead of...

Fix audiothumbnails crash. Now we process audio thumbs one by one instead of creating dozens of threads, better for hard drives.
parent e5976e63
......@@ -141,7 +141,6 @@ int AbstractProjectItem::index() const
if (m_parent) {
return m_parent->indexOf(const_cast<AbstractProjectItem*>(this));
}
return 0;
}
......@@ -242,3 +241,9 @@ bool AbstractProjectItem::statusReady() const
return m_clipStatus == StatusReady;
}
AbstractProjectItem::CLIPSTATUS AbstractProjectItem::clipStatus() const
{
return m_clipStatus;
}
......@@ -137,10 +137,12 @@ public:
enum CLIPSTATUS {
StatusReady = 0,
StatusMissing,
StatusWaiting
StatusWaiting,
StatusDeleting
};
void setClipStatus(AbstractProjectItem::CLIPSTATUS status);
AbstractProjectItem::CLIPSTATUS clipStatus() const;
bool statusReady() const;
/** @brief Returns the data that describes this item.
......
......@@ -438,6 +438,7 @@ Bin::~Bin()
{
blockSignals(true);
setEnabled(false);
abortAudioThumbs();
foreach (QWidget * w, m_propertiesPanel->findChildren<ClipPropertiesController*>()) {
delete w;
}
......@@ -454,6 +455,62 @@ Bin::~Bin()
delete m_infoMessage;
}
void Bin::slotAbortAudioThumb(const QString &id)
{
if (!m_audioThumbsThread.isRunning()) return;
QMutexLocker aMutex(&m_audioThumbMutex);
m_audioThumbsList.removeAll(id);
}
void Bin::requestAudioThumbs(const QString &id)
{
if (!m_audioThumbsList.contains(id) && m_processingAudioThumb != id) {
m_audioThumbMutex.lock();
m_audioThumbsList.append(id);
m_audioThumbMutex.unlock();
processAudioThumbs();
}
}
void Bin::processAudioThumbs()
{
if (m_audioThumbsThread.isRunning()) return;
m_audioThumbsThread = QtConcurrent::run(this, &Bin::slotCreateAudioThumbs);
}
void Bin::abortAudioThumbs()
{
if (!m_audioThumbsThread.isRunning()) return;
if (!m_processingAudioThumb.isEmpty()) {
ProjectClip *clip = m_rootFolder->clip(m_processingAudioThumb);
if (clip) clip->abortAudioThumb = true;
}
m_audioThumbMutex.lock();
m_audioThumbsList.clear();
m_audioThumbMutex.unlock();
m_audioThumbsThread.waitForFinished();
}
void Bin::slotCreateAudioThumbs()
{
int max = m_audioThumbsList.count();
int count = 0;
while (!m_audioThumbsList.isEmpty()) {
m_audioThumbMutex.lock();
max = qMax(max, m_audioThumbsList.count());
m_processingAudioThumb = m_audioThumbsList.takeFirst();
count++;
m_audioThumbMutex.unlock();
emitMessage(i18n("Creating audio thumbnails") + QString(" (%1/%2)").arg(count).arg(max), ProcessingJobMessage);
ProjectClip *clip = m_rootFolder->clip(m_processingAudioThumb);
if (clip) clip->slotCreateAudioThumbs();
}
m_audioThumbMutex.lock();
m_processingAudioThumb.clear();
m_audioThumbMutex.unlock();
emitMessage(i18n("Audio thumbnails done"), OperationCompletedMessage);
}
bool Bin::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonRelease) {
......@@ -602,6 +659,7 @@ void Bin::deleteClip(const QString &id)
return;
}
m_jobManager->discardJobs(id);
clip->setClipStatus(AbstractProjectItem::StatusDeleting);
AbstractProjectItem *parent = clip->parent();
parent->removeChild(clip);
delete clip;
......@@ -2748,13 +2806,6 @@ void Bin::slotCreateAudioThumb(const QString &id)
clip->createAudioThumbs();
}
void Bin::slotAbortAudioThumb(const QString &id)
{
ProjectClip *clip = m_rootFolder->clip(id);
if (!clip) return;
clip->abortAudioThumbs();
}
void Bin::slotSetSorting()
{
QTreeView *view = qobject_cast<QTreeView*>(m_itemView);
......
......@@ -36,6 +36,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QPushButton>
#include <QUrl>
#include <QListView>
#include <QFuture>
#include <QMutex>
class KdenliveDoc;
class ClipController;
......@@ -320,7 +322,7 @@ class Bin : public QWidget
public:
explicit Bin(QWidget* parent = 0);
~Bin();
bool isLoading;
/** @brief Sets the document for the bin and initialize some stuff */
......@@ -460,6 +462,8 @@ public:
/** @brief Update status of disable effects action (when loading a document). */
void setBinEffectsDisabledStatus(bool disabled);
void requestAudioThumbs(const QString &id);
private slots:
void slotAddClip();
void slotReloadClip();
......@@ -505,6 +509,7 @@ private slots:
void slotDisableEffects(bool disable);
/** @brief Rename a Bin Folder. */
void slotRenameFolder();
void slotCreateAudioThumbs();
public slots:
void slotThumbnailReady(const QString &id, const QImage &img, bool fromFile = false);
......@@ -561,6 +566,7 @@ public slots:
/** @brief Request current frame from project monitor. */
void slotGetCurrentProjectImage();
void slotExpandUrl(ItemInfo info, QUrl url, QUndoCommand *command);
void abortAudioThumbs();
protected:
void contextMenuEvent(QContextMenuEvent *event);
......@@ -619,6 +625,12 @@ private:
InvalidDialog *m_invalidClipDialog;
/** @brief Set to true if widget just gained focus (means we have to update effect stack . */
bool m_gainedFocus;
/** @brief List of Clip Ids that want an audio thumb. */
QStringList m_audioThumbsList;
QString m_processingAudioThumb;
QMutex m_audioThumbMutex;
/** @brief Indicates whether audio thumbnail creation is running. */
QFuture<void> m_audioThumbsThread;
void showClipProperties(ProjectClip *clip, bool openExternalDialog = true);
const QStringList getFolderInfo(QModelIndex selectedIx = QModelIndex());
/** @brief Get the QModelIndex value for an item in the Bin. */
......@@ -628,6 +640,7 @@ private:
ProjectClip *getFirstSelectedClip();
void showTitleWidget(ProjectClip *clip);
void showSlideshowWidget(ProjectClip *clip);
void processAudioThumbs();
signals:
void itemUpdated(AbstractProjectItem*);
......@@ -653,7 +666,7 @@ signals:
/** @brief Fill context menu with occurences of this clip in timeline. */
void findInTimeline(const QString &);
void clipNameChanged(const QString &);
};
#endif
......@@ -45,9 +45,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
ProjectClip::ProjectClip(const QString &id, QIcon thumb, ClipController *controller, ProjectFolder* parent) :
AbstractProjectItem(AbstractProjectItem::ClipItem, id, parent)
, audioFrameCache()
, abortAudioThumb(false)
, m_controller(controller)
, m_abortAudioThumb(false)
, m_thumbsProducer(NULL)
{
m_clipStatus = StatusReady;
......@@ -60,17 +59,17 @@ ProjectClip::ProjectClip(const QString &id, QIcon thumb, ClipController *control
// Make sure we have a hash for this clip
hash();
setParent(parent);
connect(this, &ProjectClip::updateJobStatus, this, &ProjectClip::setJobStatus);
bin()->loadSubClips(id, m_controller->getPropertiesFromPrefix("kdenlive:clipzone."));
if (KdenliveSettings::audiothumbnails()) {
m_audioThumbsThread = QtConcurrent::run(this, &ProjectClip::slotCreateAudioThumbs);
bin()->requestAudioThumbs(id);
}
}
ProjectClip::ProjectClip(const QDomElement& description, QIcon thumb, ProjectFolder* parent) :
AbstractProjectItem(AbstractProjectItem::ClipItem, description, parent)
, audioFrameCache()
, abortAudioThumb(false)
, m_controller(NULL)
, m_abortAudioThumb(false)
, m_type(Unknown)
, m_thumbsProducer(NULL)
{
......@@ -89,6 +88,7 @@ ProjectClip::ProjectClip(const QDomElement& description, QIcon thumb, ProjectFol
m_name = m_temporaryUrl.fileName();
}
else m_name = i18n("Untitled");
connect(this, &ProjectClip::updateJobStatus, this, &ProjectClip::setJobStatus);
setParent(parent);
}
......@@ -96,11 +96,15 @@ ProjectClip::ProjectClip(const QDomElement& description, QIcon thumb, ProjectFol
ProjectClip::~ProjectClip()
{
// controller is deleted in bincontroller
abortAudioThumbs();
abortAudioThumb = true;
bin()->slotAbortAudioThumb(m_id);
QMutexLocker audioLock(&m_audioMutex);
m_thumbMutex.lock();
m_requestedThumbs.clear();
m_thumbMutex.unlock();
m_thumbThread.waitForFinished();
delete m_thumbsProducer;
delete audioFrameCache;
audioFrameCache.clear();
}
QString ProjectClip::getToolTip() const
......@@ -124,7 +128,7 @@ QString ProjectClip::getXmlProperty(const QDomElement &producer, const QString &
void ProjectClip::updateAudioThumbnail(QVariantList audioLevels)
{
////qDebug() << "CLIPBASE RECIEDVED AUDIO DATA*********************************************";
audioFrameCache = new QVariantList(audioLevels);
audioFrameCache = audioLevels;
m_controller->audioThumbCreated = true;
emit gotAudioData();
}
......@@ -310,16 +314,8 @@ bool ProjectClip::setProducer(ClipController *controller, bool replaceProducer)
void ProjectClip::createAudioThumbs()
{
if (KdenliveSettings::audiothumbnails() && !m_audioThumbsThread.isRunning()) {
m_audioThumbsThread = QtConcurrent::run(this, &ProjectClip::slotCreateAudioThumbs);
}
}
void ProjectClip::abortAudioThumbs()
{
if (m_audioThumbsThread.isRunning()) {
m_abortAudioThumb = true;
m_audioThumbsThread.waitForFinished();
if (KdenliveSettings::audiothumbnails()) {
bin()->requestAudioThumbs(m_id);
}
}
......@@ -586,16 +582,16 @@ void ProjectClip::setProperties(QMap <QString, QString> properties, bool refresh
}
}
void ProjectClip::setJobStatus(AbstractClipJob::JOBTYPE jobType, ClipJobStatus status, int progress, const QString &statusMessage)
void ProjectClip::setJobStatus(int jobType, int status, int progress, const QString &statusMessage)
{
m_jobType = jobType;
m_jobType = (AbstractClipJob::JOBTYPE) jobType;
if (progress > 0) {
if (m_jobProgress == progress) return;
m_jobProgress = progress;
}
else {
m_jobProgress = status;
if ((status == JobAborted || status == JobCrashed || status == JobDone) || !statusMessage.isEmpty()) {
m_jobProgress = (ClipJobStatus) status;
if ((status == JobAborted || status == JobCrashed || status == JobDone) && !statusMessage.isEmpty()) {
m_jobMessage = statusMessage;
bin()->emitMessage(statusMessage, OperationCompletedMessage);
}
......@@ -819,6 +815,7 @@ int ProjectClip::audioChannels() const
void ProjectClip::slotCreateAudioThumbs()
{
QMutexLocker lock(&m_audioMutex);
Mlt::Producer *prod = originalProducer();
if (!prod || !prod->is_valid()) return;
AudioStreamInfo *audioInfo = m_controller->audioInfo();
......@@ -867,20 +864,20 @@ void ProjectClip::slotCreateAudioThumbs()
audioProducer->set("video_index", "-1");
int last_val = 0;
setJobStatus(AbstractClipJob::THUMBJOB, JobWaiting, 0, i18n("Creating audio thumbnails"));
emit updateJobStatus(AbstractClipJob::THUMBJOB, JobWaiting, 0);//, i18n("Creating audio thumbnails"));
double framesPerSecond = audioProducer->get_fps();
mlt_audio_format audioFormat = mlt_audio_s16;
QStringList keys;
for (int i = 0; i < channels; i++) {
keys << "meta.media.audio_level." + QString::number(i);
}
for (int z = (int) frame; z < (int)(frame + lengthInFrames) && !m_abortAudioThumb; ++z) {
for (int z = (int) frame; z < (int)(frame + lengthInFrames) && !abortAudioThumb; ++z) {
int val = (int)((z - frame) / (frame + lengthInFrames) * 100.0);
if (last_val != val && val > 1) {
setJobStatus(AbstractClipJob::THUMBJOB, JobWorking, val);
emit updateJobStatus(AbstractClipJob::THUMBJOB, JobWorking, val);
last_val = val;
}
Mlt::Frame *mlt_frame = audioProducer->get_frame();
QScopedPointer<Mlt::Frame> mlt_frame(audioProducer->get_frame());
if (mlt_frame && mlt_frame->is_valid() && !mlt_frame->get_int("test_audio")) {
int samples = mlt_sample_calculator(framesPerSecond, frequency, z);
mlt_frame->get_audio(audioFormat, frequency, channels, samples);
......@@ -892,11 +889,10 @@ void ProjectClip::slotCreateAudioThumbs()
for (int channel = 0; channel < channels; channel++)
audioLevels << audioLevels.last();
}
delete mlt_frame;
if (m_abortAudioThumb) break;
if (abortAudioThumb) break;
}
if (!m_abortAudioThumb && audioLevels.size() > 0) {
if (!abortAudioThumb && audioLevels.size() > 0) {
// Put into an image for caching.
int count = audioLevels.size();
QImage image((count + 3) / 4, channels, QImage::Format_ARGB32);
......@@ -917,11 +913,11 @@ void ProjectClip::slotCreateAudioThumbs()
}
image.save(audioPath);
}
setJobStatus(AbstractClipJob::THUMBJOB, JobDone, 0, i18n("Audio thumbnails done"));
if (!m_abortAudioThumb) {
emit updateJobStatus(AbstractClipJob::THUMBJOB, JobDone, 0);//, i18n("Audio thumbnails done"));
if (!abortAudioThumb) {
updateAudioThumbnail(audioLevels);
}
m_abortAudioThumb = false;
abortAudioThumb = false;
}
bool ProjectClip::isTransparent() const
......
......@@ -66,6 +66,8 @@ public:
ProjectClip(const QDomElement &description, QIcon thumb, ProjectFolder *parent);
virtual ~ProjectClip();
bool abortAudioThumb;
void reloadProducer(bool thumbnailOnly = false);
/** @brief Returns a unique hash identifier used to store clip thumbnails. */
......@@ -118,13 +120,6 @@ public:
virtual QDomElement toXml(QDomDocument &document);
QVariant data(DataType type) const;
/** @brief Set the Job status on a clip.
* @param jobType The job type
* @param status The job status (see definitions.h)
* @param progress The job progress (in percents)
* @param statusMessage The job info message */
void setJobStatus(AbstractClipJob::JOBTYPE jobType, ClipJobStatus status, int progress = 0, const QString &statusMessage = QString());
/** @brief Sets thumbnail for this clip. */
void setThumbnail(QImage);
......@@ -179,7 +174,7 @@ public:
/** Cache for every audio Frame with 10 Bytes */
/** format is frame -> channel ->bytes */
QVariantList *audioFrameCache;
QVariantList audioFrameCache;
bool audioThumbCreated() const;
void updateParentInfo(const QString &folderid, const QString &foldername);
......@@ -199,8 +194,6 @@ public:
void removeEffect(int ix);
/** @brief Create audio thumbnail for this clip. */
void createAudioThumbs();
/** @brief Abort audio thumbnail for this clip. */
void abortAudioThumbs();
/** @brief Returns the number of audio channels. */
int audioChannels() const;
/** @brief get data analysis value. */
......@@ -214,6 +207,12 @@ public slots:
/** @brief Extract image thumbnails for clip's subclips. */
void slotExtractSubImage(QList <int> frames);
void slotCreateAudioThumbs();
/** @brief Set the Job status on a clip.
* @param jobType The job type
* @param status The job status (see definitions.h)
* @param progress The job progress (in percents)
* @param statusMessage The job info message */
void setJobStatus(int jobType, int status, int progress = 0, const QString &statusMessage = QString());
private:
//TODO: clip markers
......@@ -224,13 +223,11 @@ private:
const QString getFileHash() const;
/** @brief Store clip url temporarily while the clip controller has not been created. */
QUrl m_temporaryUrl;
bool m_abortAudioThumb;
/** @brief Indicates whether audio thumbnail creation is running. */
QFuture<void> m_audioThumbsThread;
ClipType m_type;
Mlt::Producer *m_thumbsProducer;
QMutex m_producerMutex;
QMutex m_thumbMutex;
QMutex m_audioMutex;
QFuture <void> m_thumbThread;
QList <int> m_requestedThumbs;
const QString geometryWithOffset(const QString &data, int offset);
......@@ -242,6 +239,7 @@ signals:
void refreshAnalysisPanel();
void refreshClipDisplay();
void thumbReady(int, QImage);
void updateJobStatus(int jobType, int status, int progress = 0, const QString &statusMessage = QString());
};
#endif
......@@ -324,6 +324,7 @@ void ProjectItemModel::onItemRemoved(AbstractProjectItem* item)
void ProjectItemModel::onItemUpdated(AbstractProjectItem* item)
{
if (!item || item->clipStatus() == AbstractProjectItem::StatusDeleting) return;
AbstractProjectItem *parentItem = item->parent();
if (parentItem == NULL) return;
QModelIndex parentIndex;
......
......@@ -59,7 +59,7 @@ void Core::initialize()
connect(m_binWidget, SIGNAL(storeFolder(QString,QString,QString,QString)), m_binController, SLOT(slotStoreFolder(QString,QString,QString,QString)));
connect(m_binController, SIGNAL(loadFolders(QMap<QString,QString>)), m_binWidget, SLOT(slotLoadFolders(QMap<QString,QString>)));
connect(m_binController, SIGNAL(requestAudioThumb(QString)), m_binWidget, SLOT(slotCreateAudioThumb(QString)));
connect(m_binController, SIGNAL(abortAudioThumb(QString)), m_binWidget, SLOT(slotAbortAudioThumb(QString)));
connect(m_binController, SIGNAL(abortAudioThumbs()), m_binWidget, SLOT(abortAudioThumbs()));
connect(m_binController, SIGNAL(loadThumb(QString,QImage,bool)), m_binWidget, SLOT(slotThumbnailReady(QString,QImage,bool)));
m_monitorManager = new MonitorManager(this);
emit coreIsReady();
......
......@@ -492,7 +492,8 @@ void BinController::checkAudioThumbs()
emit requestAudioThumb(ctrl->clipId());
} else {
// Abort all pending thumb creation
emit abortAudioThumb(ctrl->clipId());
emit abortAudioThumbs();
break;
}
}
}
......
......@@ -196,7 +196,7 @@ signals:
void loadThumb(QString,QImage,bool);
void createThumb(const QDomElement&,const QString&,int);
void requestAudioThumb(const QString&);
void abortAudioThumb(const QString&);
void abortAudioThumbs();
void replaceTimelineProducer(const QString &id);
void setDocumentNotes(const QString &);
void updateTimelineProducer(const QString &);
......
......@@ -220,6 +220,8 @@ bool ProjectManager::closeCurrentDocument(bool saveChanges, bool quit)
if (!quit && !qApp->isSavingSession()) {
m_autoSaveTimer.stop();
if (m_project) {
m_project->renderer()->abortOperations();
pCore->bin()->abortAudioThumbs();
pCore->window()->slotTimelineClipSelected(NULL, false);
pCore->monitorManager()->clipMonitor()->slotOpenClip(NULL);
delete m_project;
......
......@@ -107,10 +107,20 @@ Render::~Render()
closeMlt();
}
void Render::abortOperations()
{
m_infoMutex.lock();
m_requestList.clear();
m_infoMutex.unlock();
m_infoThread.waitForFinished();
}
void Render::closeMlt()
{
m_infoMutex.lock();
m_requestList.clear();
m_infoMutex.unlock();
m_infoThread.waitForFinished();
delete m_showFrameEvent;
delete m_pauseEvent;
......@@ -148,7 +158,9 @@ void Render::prepareProfileReset()
m_refreshTimer.stop();
if (m_isSplitView)
slotSplitView(false);
m_infoMutex.lock();
m_requestList.clear();
m_infoMutex.unlock();
m_infoThread.waitForFinished();
}
......
......@@ -307,6 +307,8 @@ class Render: public AbstractRender
void prepareProfileReset();
void updateSlowMotionProducers(const QString &id, QMap <QString, QString> passProperties);
static QMap<QString, QString> mltGetTransitionParamsFromXml(const QDomElement &xml);
/** @brief Make sure to close running threads before closing document */
void abortOperations();
private:
......
......@@ -80,12 +80,25 @@ void StatusBarMessageLabel::setMessage(const QString& text,
if (item.timeoutMillis < 2000) {
item.timeoutMillis = 2000;
}
if (item.type == ProcessingJobMessage) {
// This is a job progress info, discard previous ones
QList <StatusBarMessageItem> cleanList;
foreach (const StatusBarMessageItem msg, m_messageQueue) {
if (msg.type != ProcessingJobMessage) {
cleanList << msg;
}
}
m_messageQueue = cleanList;
}
m_messageQueue.push_front(item);
// In case we are already displaying an error message, add a little delay
int delay = 800 * (m_currentMessage.type == ErrorMessage || m_currentMessage.type == MltError);
m_queueTimer.start(delay);
} else {
// Message with normal priority
......@@ -113,7 +126,7 @@ bool StatusBarMessageLabel::slotMessageTimeout()
while (!m_messageQueue.isEmpty()) {
item = m_messageQueue.at(0);
m_messageQueue.removeFirst();
if (item.type == OperationCompletedMessage || item.type == ErrorMessage || item.type == MltError) {
if (item.type == OperationCompletedMessage || item.type == ErrorMessage || item.type == MltError || item.type == ProcessingJobMessage) {
m_currentMessage = item;
newMessage = true;
break;
......@@ -138,7 +151,6 @@ bool StatusBarMessageLabel::slotMessageTimeout()
if (!m_messageQueue.isEmpty()) {
if (!m_currentMessage.needsConfirmation()) {
// If we only have the default message left to show in the queue,
// keep the current one for a little longer.
m_queueTimer.start(m_currentMessage.timeoutMillis + 4000*(m_messageQueue.at(0).type == DefaultMessage));
......@@ -146,7 +158,6 @@ bool StatusBarMessageLabel::slotMessageTimeout()
}
}
m_illumination = -64;
m_state = Default;
m_timer.stop();
......
......@@ -752,16 +752,16 @@ void ClipItem::paint(QPainter *painter,
if (scale < 1) {
offset = (int) (1.0 / scale);
}
int audioLevelCount = m_binClip->audioFrameCache->count() - 1;
int audioLevelCount = m_binClip->audioFrameCache.count() - 1;
if (!KdenliveSettings::displayallchannels()) {
// simplified audio
int channelHeight = mappedRect.height();
QPainterPath positiveChannelPath;
positiveChannelPath.moveTo(startx, mappedRect.bottom());
for (int i = startpixel + cropLeft; i < endpixel + cropLeft + offset; i += offset) {
double value = m_binClip->audioFrameCache->at(qMin(i * channels, audioLevelCount)).toDouble() / 256;
double value = m_binClip->audioFrameCache.at(qMin(i * channels, audioLevelCount)).toDouble() / 256;
for (int channel = 1; channel < channels; channel ++) {
value = qMax(value, m_binClip->audioFrameCache->at(qMin(i * channels + channel, audioLevelCount)).toDouble() / 256);
value = qMax(value, m_binClip->audioFrameCache.at(qMin(i * channels + channel, audioLevelCount)).toDouble() / 256);
}
positiveChannelPath.lineTo(startx + (i - (startpixel + cropLeft)) * scale, mappedRect.bottom() - (value * channelHeight));
}
......@@ -778,7 +778,7 @@ void ClipItem::paint(QPainter *painter,
positiveChannelPaths[channel].moveTo(startx, mappedRect.bottom() - y);
negativeChannelPaths[channel].moveTo(startx, mappedRect.bottom() - y);
for (int i = startpixel + cropLeft; i < endpixel + cropLeft + offset; i += offset) {
double value = m_binClip->audioFrameCache->at(qMin(i * channels + channel, audioLevelCount)).toDouble() / 256;
double value = m_binClip->audioFrameCache.at(qMin(i * channels + channel, audioLevelCount)).toDouble() / 256;
positiveChannelPaths[channel].lineTo(startx + (i - (startpixel + cropLeft)) * scale, mappedRect.bottom() - (y + (value * channelHeight / 2)));
negativeChannelPaths[channel].lineTo(startx + (i - (startpixel + cropLeft)) * scale, mappedRect.bottom() - (y - (value * channelHeight / 2)));
}
......
......@@ -3673,7 +3673,6 @@ void CustomTrackView::deleteClip(const QString &clipId, QUndoCommand *deleteComm
}
}
}
qDebug()<<"+ + +Found timeline clips: "<<count;
if (count > 0) {
new RefreshMonitorCommand(this, true, false, deleteCommand);