automated audio cut working
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "silencedialog.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include "timeline/sequence.h"
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/timeline.h"
|
||||
|
||||
SilenceDialog::SilenceDialog(QWidget *parent, QVector<Clip*> clips) : QDialog(parent) {
|
||||
setWindowTitle(tr("Cut silence"));
|
||||
|
||||
clips_ = clips;
|
||||
|
||||
QVBoxLayout* main_layout = new QVBoxLayout(this);
|
||||
QGridLayout* grid = new QGridLayout();
|
||||
grid->setSpacing(6);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Attack Threshold:"), this), 0, 0);
|
||||
attack_threshold = new LabelSlider(this);
|
||||
attack_threshold->SetDecimalPlaces(0);
|
||||
grid->addWidget(attack_threshold, 0, 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Attack Time:"), this), 1, 0);
|
||||
attack_time = new LabelSlider(this);
|
||||
attack_time->SetDecimalPlaces(0);
|
||||
grid->addWidget(attack_time, 1, 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Release Threshold:"), this), 2, 0);
|
||||
release_threshold = new LabelSlider(this);
|
||||
release_threshold->SetDecimalPlaces(0);
|
||||
grid->addWidget(release_threshold, 2, 1);
|
||||
|
||||
grid->addWidget(new QLabel(tr("Release Time:"), this), 3, 0);
|
||||
release_time = new LabelSlider(this);
|
||||
release_time->SetDecimalPlaces(0);
|
||||
grid->addWidget(release_time, 3, 1);
|
||||
|
||||
main_layout->addLayout(grid);
|
||||
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
buttonBox->setCenterButtons(true);
|
||||
main_layout->addWidget(buttonBox);
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
|
||||
run(); // is it ok to call it here?
|
||||
}
|
||||
|
||||
void SilenceDialog::run() {
|
||||
|
||||
default_attack_threshold = 5;
|
||||
current_attack_threshold = 5;
|
||||
default_attack_time = 2;
|
||||
current_attack_time = 2;
|
||||
default_release_threshold = 2;
|
||||
current_release_threshold = 2;
|
||||
default_release_time = 5;
|
||||
current_release_time = 5;
|
||||
|
||||
attack_threshold->SetMinimum(1);
|
||||
attack_threshold->setEnabled(true);
|
||||
attack_threshold->SetDefault(default_attack_threshold);
|
||||
attack_threshold->SetValue(current_attack_threshold);
|
||||
|
||||
attack_time->SetMinimum(1);
|
||||
attack_time->setEnabled(true);
|
||||
attack_time->SetDefault(default_attack_time);
|
||||
attack_time->SetValue(current_attack_time);
|
||||
|
||||
release_threshold->SetMinimum(1);
|
||||
release_threshold->setEnabled(true);
|
||||
release_threshold->SetDefault(default_release_threshold);
|
||||
release_threshold->SetValue(current_release_threshold);
|
||||
|
||||
release_time->SetMinimum(1);
|
||||
release_time->setEnabled(true);
|
||||
release_time->SetDefault(default_release_time);
|
||||
release_time->SetValue(current_release_time);
|
||||
|
||||
}
|
||||
|
||||
void SilenceDialog::accept() {
|
||||
|
||||
current_attack_threshold = attack_threshold->value();
|
||||
current_attack_time = attack_time->value();
|
||||
current_release_threshold = release_threshold->value();
|
||||
current_release_time = release_time->value();
|
||||
|
||||
cut_silence();
|
||||
|
||||
update_ui(true);
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void SilenceDialog::cut_silence() {
|
||||
|
||||
Clip* clip = clips_[0];
|
||||
int clip_start = clip->timeline_in();
|
||||
const FootageStream* ms = clip->media_stream();
|
||||
long media_length = clip->media_length();
|
||||
int preview_size = ms->audio_preview.length();
|
||||
float chunk_size = (float)preview_size/media_length; // how many audio samples to read for each fotogram
|
||||
|
||||
int sample_size = qMax(current_attack_time, current_release_time)+1;
|
||||
|
||||
bool attack = false; // status flags
|
||||
bool release = false;
|
||||
|
||||
qint8 * vols = NULL;
|
||||
vols = new qint8[sample_size];
|
||||
for(int i=0; i<sample_size; i++)
|
||||
vols[i] = 0;
|
||||
|
||||
// loop through the entire sequence
|
||||
for (int i=clip_start;i<media_length+clip_start;i++) {
|
||||
int start = ((i-clip_start)*chunk_size); //audio samples are read relative to the clip, not absolute to the timeline
|
||||
int circular_index = i%sample_size;
|
||||
|
||||
// read the current sample into the circular array
|
||||
qint8 tmp = 0;
|
||||
for (int k=start; k<start+chunk_size; k++){
|
||||
tmp = qMax(tmp, qint8(qRound(double(ms->audio_preview.at(k)))));
|
||||
}
|
||||
vols[circular_index] = tmp;
|
||||
|
||||
//for debug:
|
||||
//qInfo() << "i:" << i <<" - "<< i/30 <<":"<< i%30 << " - volume:" << vols[circular_index] <<"\n";
|
||||
|
||||
int overthreshold = 0;
|
||||
int cut_idx = 0; //how much to cut (backwards)
|
||||
|
||||
// if current volume value is above threshold
|
||||
if (vols[circular_index] >= current_attack_threshold && !attack){ // if we get one sample over the threshold
|
||||
for(int k=0; k<sample_size; k++){ // count how many times this happened before within sample_size range (avoid false activations)
|
||||
int back_idx = (((circular_index-k)%sample_size)+sample_size)%sample_size; // positive modulus
|
||||
if(vols[back_idx] > current_attack_threshold){
|
||||
overthreshold++;
|
||||
cut_idx = k+1;
|
||||
}
|
||||
}
|
||||
// if we reached threshold over the set tolerance
|
||||
if(overthreshold >= current_attack_time){
|
||||
panel_timeline->split_at_position(i-cut_idx); // cut at the first occurence
|
||||
attack = true;
|
||||
release = false;
|
||||
//qInfo() << "\n\n Current vol: "<<vols[circular_index]<<" attack at " << i-cut_idx << "\n\n";
|
||||
}
|
||||
overthreshold = 0;
|
||||
cut_idx = 0;
|
||||
}else if (vols[circular_index] < current_release_threshold && !release){ // if we get one sample under the threshold
|
||||
for(int k=0; k<sample_size; k++){ // count how many times this happened before within sample_size range
|
||||
int back_idx = (((circular_index-k)%sample_size)+sample_size)%sample_size; // positive modulus
|
||||
if(vols[back_idx] < current_release_threshold)
|
||||
overthreshold++;
|
||||
}
|
||||
// if we reached threshold over the set tolerance
|
||||
if(overthreshold >= current_release_time){ // must be <= sample_size
|
||||
attack = false;
|
||||
release = true;
|
||||
panel_timeline->split_at_position(i);
|
||||
//qInfo() << "\n\n Current vol: "<<vols[circular_index]<<" release at " << i << "\n\n";
|
||||
}
|
||||
overthreshold = 0;
|
||||
}
|
||||
}
|
||||
delete [] vols;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SILENCEDIALOG_H
|
||||
#define SILENCEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "timeline/clip.h"
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
class SilenceDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SilenceDialog(QWidget* parent, QVector<Clip*> clips);
|
||||
|
||||
void run();
|
||||
private slots:
|
||||
void cut_silence();
|
||||
void accept();
|
||||
private:
|
||||
QVector<Clip*> clips_;
|
||||
|
||||
LabelSlider* attack_threshold;
|
||||
LabelSlider* release_threshold;
|
||||
LabelSlider* attack_time;
|
||||
LabelSlider* release_time;
|
||||
|
||||
int default_attack_threshold;
|
||||
int current_attack_threshold;
|
||||
int default_release_threshold;
|
||||
int current_release_threshold;
|
||||
int default_attack_time;
|
||||
int current_attack_time;
|
||||
int default_release_time;
|
||||
int current_release_time;
|
||||
};
|
||||
|
||||
#endif // SILENCEDIALOG_H
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "dialogs/aboutdialog.h"
|
||||
#include "dialogs/speeddialog.h"
|
||||
#include "dialogs/actionsearch.h"
|
||||
#include "dialogs/silencedialog.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "ui/mediaiconservice.h"
|
||||
#include "ui/mainwindow.h"
|
||||
@@ -362,6 +363,18 @@ void OliveGlobal::open_speed_dialog() {
|
||||
}
|
||||
}
|
||||
|
||||
void OliveGlobal::open_cut_silence_dialog() {
|
||||
if (olive::ActiveSequence != nullptr) {
|
||||
|
||||
QVector<Clip*> selected_clips = olive::ActiveSequence->SelectedClips();
|
||||
|
||||
if (!selected_clips.isEmpty()) {
|
||||
SilenceDialog s(olive::MainWindow, selected_clips);
|
||||
s.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OliveGlobal::clear_undo_stack() {
|
||||
olive::UndoStack.clear();
|
||||
}
|
||||
|
||||
@@ -239,6 +239,11 @@ public slots:
|
||||
*/
|
||||
void open_speed_dialog();
|
||||
|
||||
/**
|
||||
* @brief Open the cut silence dialog.
|
||||
*/
|
||||
void open_cut_silence_dialog();
|
||||
|
||||
/**
|
||||
* @brief Open the Action Search overlay.
|
||||
*/
|
||||
|
||||
@@ -91,6 +91,7 @@ SOURCES += \
|
||||
dialogs/demonotice.cpp \
|
||||
timeline/marker.cpp \
|
||||
dialogs/speeddialog.cpp \
|
||||
dialogs/silencedialog.cpp \
|
||||
dialogs/mediapropertiesdialog.cpp \
|
||||
project/projectmodel.cpp \
|
||||
project/loadthread.cpp \
|
||||
@@ -221,6 +222,7 @@ HEADERS += \
|
||||
project/projectmodel.h \
|
||||
project/loadthread.h \
|
||||
dialogs/loaddialog.h \
|
||||
dialogs/silencedialog.h \
|
||||
global/debug.h \
|
||||
global/path.h \
|
||||
effects/internal/transformeffect.h \
|
||||
|
||||
@@ -138,6 +138,25 @@ void Timeline::Retranslate() {
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
void Timeline::split_at_position(int pos) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
split_cache.clear();
|
||||
|
||||
if (olive::ActiveSequence->selections.size() > 0) {
|
||||
// see if whole clips are selected
|
||||
QVector<int> pre_clips;
|
||||
QVector<ClipPtr> post_clips;
|
||||
for (int j=0;j<olive::ActiveSequence->clips.size();j++) {
|
||||
Clip* clip = olive::ActiveSequence->clips.at(j).get();
|
||||
if (clip != nullptr && olive::ActiveSequence->IsClipSelected(clip, true)) {
|
||||
split_clip_and_relink(ca,j,pos,true);
|
||||
|
||||
}
|
||||
}
|
||||
olive::UndoStack.push(ca);
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::previous_cut() {
|
||||
if (olive::ActiveSequence != nullptr
|
||||
&& olive::ActiveSequence->playhead > 0) {
|
||||
|
||||
@@ -237,6 +237,7 @@ public slots:
|
||||
void deselect();
|
||||
void toggle_links();
|
||||
void split_at_playhead();
|
||||
void split_at_position(int pos);
|
||||
void ripple_delete();
|
||||
void ripple_delete_empty_space();
|
||||
void toggle_enable_on_selected_clips();
|
||||
|
||||
@@ -122,6 +122,7 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction(tr("&Speed/Duration"), olive::Global.get(), SLOT(open_speed_dialog()));
|
||||
menu.addAction(tr("&Cut Silence"), olive::Global.get(), SLOT(open_cut_silence_dialog()));
|
||||
|
||||
QAction* autoscaleAction = menu.addAction(tr("Auto-s&cale"), this, SLOT(toggle_autoscale()));
|
||||
autoscaleAction->setCheckable(true);
|
||||
|
||||
Reference in New Issue
Block a user