diff --git a/io/config.cpp b/io/config.cpp
index d7d609497..0a1870091 100644
--- a/io/config.cpp
+++ b/io/config.cpp
@@ -26,7 +26,8 @@ Config::Config()
enable_drag_files_to_timeline(true),
autoscale_by_default(false),
recording_mode(2),
- enable_seek_to_import(false)
+ enable_seek_to_import(false),
+ enable_audio_scrubbing(true)
{}
void Config::load(QString path) {
@@ -91,7 +92,10 @@ void Config::load(QString path) {
} else if (stream.name() == "EnableSeekToImport") {
stream.readNext();
enable_seek_to_import = (stream.text() == "1");
- }
+ } else if (stream.name() == "AudioScrubbing") {
+ stream.readNext();
+ enable_audio_scrubbing = (stream.text() == "1");
+ }
}
}
if (stream.hasError()) {
@@ -133,6 +137,7 @@ void Config::save(QString path) {
stream.writeTextElement("AutoscaleByDefault", QString::number(autoscale_by_default));
stream.writeTextElement("RecordingMode", QString::number(recording_mode));
stream.writeTextElement("EnableSeekToImport", QString::number(enable_seek_to_import));
+ stream.writeTextElement("AudioScrubbing", QString::number(enable_audio_scrubbing));
stream.writeEndElement(); // configuration
stream.writeEndDocument(); // doc
diff --git a/io/config.h b/io/config.h
index c87b06ee8..5bd3fd88c 100644
--- a/io/config.h
+++ b/io/config.h
@@ -33,6 +33,7 @@ struct Config {
bool autoscale_by_default;
int recording_mode;
bool enable_seek_to_import;
+ bool enable_audio_scrubbing;
void load(QString path);
void save(QString path);
diff --git a/mainwindow.cpp b/mainwindow.cpp
index fae684765..bd1d7a805 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -649,6 +649,7 @@ void MainWindow::toolMenu_About_To_Be_Shown() {
ui->actionEnable_Drag_Files_to_Timeline->setChecked(config.enable_drag_files_to_timeline);
ui->actionAuto_scale_by_Default->setChecked(config.autoscale_by_default);
ui->actionEnable_Seek_to_Import->setChecked(config.enable_seek_to_import);
+ ui->actionAudio_Scrubbing->setChecked(config.enable_audio_scrubbing);
}
void MainWindow::on_actionEdit_Tool_Selects_Links_triggered() {
@@ -871,3 +872,7 @@ void MainWindow::on_actionEnable_Disable_Clip_triggered() {
void MainWindow::on_actionEnable_Seek_to_Import_triggered() {
config.enable_seek_to_import = !config.enable_seek_to_import;
}
+
+void MainWindow::on_actionAudio_Scrubbing_triggered() {
+ config.enable_audio_scrubbing = !config.enable_audio_scrubbing;
+}
diff --git a/mainwindow.h b/mainwindow.h
index 7695f9772..4f396dfcd 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -189,6 +189,8 @@ private slots:
void on_actionEnable_Seek_to_Import_triggered();
+ void on_actionAudio_Scrubbing_triggered();
+
private:
Ui::MainWindow *ui;
void setup_layout();
diff --git a/mainwindow.ui b/mainwindow.ui
index 3a2ee326e..4dc37e73c 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -33,7 +33,7 @@
0
0
653
- 20
+ 16
diff --git a/panels/viewer.cpp b/panels/viewer.cpp
index 568240e4d..7e6e5df58 100644
--- a/panels/viewer.cpp
+++ b/panels/viewer.cpp
@@ -92,15 +92,18 @@ void Viewer::reset_all_audio() {
if (c != NULL) c->reset_audio();
}
}
- clear_audio_ibuffer();
+ clear_audio_ibuffer();
+}
+
+void Viewer::assert_audio_device() {
+ if (audio_output->format().sampleRate() != seq->audio_frequency
+ || audio_output->format().channelCount() != av_get_channel_layout_nb_channels(seq->audio_layout)) {
+ init_audio(seq);
+ }
}
long timecode_to_frame(const QString& s, int view, double frame_rate) {
- QList list = s.split(QRegExp("[:;]"));
-
- for (int i=0;i list = s.split(QRegExp("[:;]"));
if (view == TIMECODE_FRAMES || list.size() == 1) {
return s.toLong();
@@ -208,6 +211,9 @@ void Viewer::seek(long p) {
pause();
seq->playhead = p;
update_parents();
+ reset_all_audio();
+ assert_audio_device();
+ audio_scrub = true;
}
void Viewer::go_to_start() {
@@ -270,11 +276,8 @@ void Viewer::play() {
if (panel_footage_viewer->playing) panel_footage_viewer->pause();
if (seq != NULL) {
- reset_all_audio();
- if (audio_output->format().sampleRate() != seq->audio_frequency
- || audio_output->format().channelCount() != av_get_channel_layout_nb_channels(seq->audio_layout)) {
- init_audio(seq);
- }
+ reset_all_audio();
+ assert_audio_device();
if (is_recording_cued() && !start_recording()) {
dout << "[ERROR] Failed to record audio";
return;
@@ -285,7 +288,7 @@ void Viewer::play() {
set_playpause_icon(false);
playback_updater.start();
timer_update();
- audio_thread->notifyReceiver();
+ audio_thread->notifyReceiver();
}
}
diff --git a/panels/viewer.h b/panels/viewer.h
index 93b4727e2..da294f8ce 100644
--- a/panels/viewer.h
+++ b/panels/viewer.h
@@ -59,6 +59,7 @@ public:
int recording_track;
void reset_all_audio();
+ void assert_audio_device();
void update_parents();
ViewerWidget* viewer_widget;
diff --git a/playback/audio.cpp b/playback/audio.cpp
index da96cba23..feb0d7aee 100644
--- a/playback/audio.cpp
+++ b/playback/audio.cpp
@@ -23,6 +23,7 @@ extern "C" {
QAudioOutput* audio_output;
QIODevice* audio_io_device;
bool audio_device_set = false;
+bool audio_scrub = false;
QMutex audio_write_lock;
QAudioInput* audio_input = NULL;
QFile output_recording;
@@ -33,7 +34,7 @@ int audio_ibuffer_read = 0;
long audio_ibuffer_frame = 0;
double audio_ibuffer_timecode = 0;
-AudioSenderThread* audio_thread;
+AudioSenderThread* audio_thread = NULL;
void init_audio(Sequence* s) {
stop_audio();
@@ -121,7 +122,7 @@ void AudioSenderThread::run() {
cond.wait(&lock);
if (close) {
break;
- } else if (panel_sequence_viewer->playing || panel_footage_viewer->playing) {
+ } else if (panel_sequence_viewer->playing || panel_footage_viewer->playing || audio_scrub) {
int written_bytes = 0;
int adjusted_read_index = audio_ibuffer_read%audio_ibuffer_size;
@@ -132,6 +133,8 @@ void AudioSenderThread::run() {
// got all the bytes, write again
written_bytes += send_audio_to_output(0, audio_ibuffer_size);
}
+
+ audio_scrub = false;
}
}
lock.unlock();
diff --git a/playback/audio.h b/playback/audio.h
index be96ed74b..344f503b1 100644
--- a/playback/audio.h
+++ b/playback/audio.h
@@ -40,6 +40,7 @@ extern qint8 audio_ibuffer[audio_ibuffer_size];
extern int audio_ibuffer_read;
extern long audio_ibuffer_frame;
extern double audio_ibuffer_timecode;
+extern bool audio_scrub;
void clear_audio_ibuffer();
void init_audio(Sequence *s);
diff --git a/playback/cacher.cpp b/playback/cacher.cpp
index 4461dd283..026a7146b 100644
--- a/playback/cacher.cpp
+++ b/playback/cacher.cpp
@@ -70,15 +70,15 @@ void apply_audio_effects(Clip* c, double timecode_start, AVFrame* frame, int nb_
}
}
-void cache_audio_worker(Clip* c, Clip* nest) {
+void cache_audio_worker(Clip* c, bool scrubbing, Clip* nest) {
long timeline_in = c->timeline_in;
long timeline_out = c->timeline_out;
if (nest != NULL) {
timeline_in = refactor_frame_number(timeline_in, c->sequence->frame_rate, sequence->frame_rate) + nest->timeline_in;
timeline_out = refactor_frame_number(timeline_out, c->sequence->frame_rate, sequence->frame_rate) + nest->timeline_in;
- }
+ }
- while (true) {
+ while (true) {
AVFrame* frame;
int nb_bytes = INT_MAX;
@@ -239,7 +239,7 @@ void cache_audio_worker(Clip* c, Clip* nest) {
} else {
// if there is no more data in the file, we flush the remainder out of swresample
break;
- }
+ }
new_frame = true;
@@ -316,17 +316,25 @@ void cache_audio_worker(Clip* c, Clip* nest) {
default: // shouldn't ever get here
dout << "[ERROR] Tried to cache a non-footage/tone clip";
return;
- }
+ }
// mix audio into internal buffer
if (frame->nb_samples == 0) {
break;
} else {
long buffer_timeline_out = get_buffer_offset_from_frame(c->sequence, timeline_out);
- audio_write_lock.lock();
+ audio_write_lock.lock();
while (c->frame_sample_index < nb_bytes
&& c->audio_buffer_write < audio_ibuffer_read+audio_ibuffer_size-512
&& c->audio_buffer_write < buffer_timeline_out) {
+
+ /*dout << "F (" <<
+ c->frame_sample_index << "/" << nb_bytes
+ << ") (" <<
+ c->audio_buffer_write << "/" << (audio_ibuffer_read+audio_ibuffer_size-512)
+ << ") (" <<
+ c->audio_buffer_write << "/" << buffer_timeline_out << ")";*/
+
int upper_byte_index = (c->audio_buffer_write+1)%audio_ibuffer_size;
int lower_byte_index = (c->audio_buffer_write)%audio_ibuffer_size;
qint16 old_sample = static_cast((audio_ibuffer[upper_byte_index] & 0xFF) << 8 | (audio_ibuffer[lower_byte_index] & 0xFF));
@@ -345,6 +353,10 @@ void cache_audio_worker(Clip* c, Clip* nest) {
audio_write_lock.unlock();
+ if (scrubbing) {
+ audio_thread->notifyReceiver();
+ }
+
if (c->frame_sample_index == nb_bytes) {
c->frame_sample_index = -1;
} else {
@@ -357,6 +369,9 @@ void cache_audio_worker(Clip* c, Clip* nest) {
if (c->reached_end) {
frame->nb_samples = 0;
}
+ if (scrubbing) {
+ break;
+ }
}
}
@@ -786,7 +801,7 @@ void open_clip_worker(Clip* clip) {
dout << "[INFO] Clip opened on track" << clip->track;
}
-void cache_clip_worker(Clip* clip, long playhead, bool reset, Clip* nest) {
+void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, Clip* nest) {
if (reset) {
// note: for video, playhead is in "internal clip" frames - for audio, it's the timeline playhead
reset_cache(clip, playhead);
@@ -798,11 +813,11 @@ void cache_clip_worker(Clip* clip, long playhead, bool reset, Clip* nest) {
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
cache_video_worker(clip, playhead);
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- cache_audio_worker(clip, nest);
+ cache_audio_worker(clip, scrubbing, nest);
}
break;
case MEDIA_TYPE_TONE:
- cache_audio_worker(clip, nest);
+ cache_audio_worker(clip, scrubbing, nest);
break;
}
}
@@ -841,7 +856,7 @@ void Cacher::run() {
if (!caching) {
break;
} else {
- cache_clip_worker(clip, playhead, reset, nest);
+ cache_clip_worker(clip, playhead, reset, scrubbing, nest);
}
}
diff --git a/playback/cacher.h b/playback/cacher.h
index 4f02b5794..1a65f6aae 100644
--- a/playback/cacher.h
+++ b/playback/cacher.h
@@ -17,6 +17,7 @@ public:
// must be set before caching
long playhead;
bool reset;
+ bool scrubbing;
Clip* nest;
private:
@@ -24,7 +25,7 @@ private:
};
void open_clip_worker(Clip* clip);
-void cache_clip_worker(Clip* clip, long playhead, bool reset, Clip *nest);
+void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, Clip *nest);
void close_clip_worker(Clip* clip);
#endif // CACHER_H
diff --git a/playback/playback.cpp b/playback/playback.cpp
index 09b418a70..288207f20 100644
--- a/playback/playback.cpp
+++ b/playback/playback.cpp
@@ -95,16 +95,17 @@ void close_clip(Clip* clip) {
}
}
-void cache_clip(Clip* clip, long playhead, bool reset, Clip* nest) {
+void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, Clip* nest) {
if (clip->media_type == MEDIA_TYPE_FOOTAGE || clip->media_type == MEDIA_TYPE_TONE) {
if (clip->multithreaded) {
clip->cacher->playhead = playhead;
clip->cacher->reset = reset;
clip->cacher->nest = nest;
+ clip->cacher->scrubbing = scrubbing;
clip->can_cache.wakeAll();
} else {
- cache_clip_worker(clip, playhead, reset, nest);
+ cache_clip_worker(clip, playhead, reset, scrubbing, nest);
}
}
}
@@ -224,7 +225,7 @@ void get_clip_frame(Clip* c, long playhead) {
c->queue_lock.unlock();
// get more frames
- if (cache) cache_clip(c, playhead, reset, NULL);
+ if (cache) cache_clip(c, playhead, reset, false, NULL);
}
}
diff --git a/playback/playback.h b/playback/playback.h
index da5d55851..4f5067480 100644
--- a/playback/playback.h
+++ b/playback/playback.h
@@ -13,7 +13,7 @@ extern bool texture_failed;
extern bool rendering;
void open_clip(Clip* clip, bool multithreaded);
-void cache_clip(Clip* clip, long playhead, bool reset, Clip *nest);
+void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, Clip *nest);
void close_clip(Clip* clip);
void cache_audio_worker(Clip* c, bool write_A);
void cache_video_worker(Clip* c, long playhead);
diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp
index 1beccbd42..48163a31f 100644
--- a/ui/viewerwidget.cpp
+++ b/ui/viewerwidget.cpp
@@ -436,14 +436,14 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
glPopMatrix();
}
}
- } else {
- if (render_audio) {
+ } else {
+ if (render_audio || (config.enable_audio_scrubbing && audio_scrub)) {
switch (c->media_type) {
case MEDIA_TYPE_FOOTAGE:
case MEDIA_TYPE_TONE:
if (c->lock.tryLock()) {
- // clip is not caching, start caching audio
- cache_clip(c, playhead, c->audio_reset, nest);
+ // clip is not caching, start caching audio
+ cache_clip(c, playhead, c->audio_reset, !render_audio, nest);
c->lock.unlock();
}
break;
@@ -479,6 +479,7 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
}
void ViewerWidget::paintGL() {
+ bool render_audio = (viewer->playing || rendering);
bool loop = false;
do {
loop = false;
@@ -496,7 +497,7 @@ void ViewerWidget::paintGL() {
// compose video preview
glClearColor(0, 0, 0, 0);
- compose_sequence(NULL, (viewer->playing || rendering));
+ compose_sequence(NULL, render_audio);
if (waveform) {
double waveform_zoom = (double) waveform_ms->audio_preview.size() / (double) width();