Merge branch 'master' of https://github.com/olive-editor/olive
This commit is contained in:
+7
-2
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
+10
-1
@@ -33,7 +33,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>653</width>
|
||||
<height>20</height>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_File">
|
||||
@@ -177,6 +177,7 @@
|
||||
<addaction name="actionEnable_Drag_Files_to_Timeline"/>
|
||||
<addaction name="actionAuto_scale_by_Default"/>
|
||||
<addaction name="actionEnable_Seek_to_Import"/>
|
||||
<addaction name="actionAudio_Scrubbing"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
<addaction name="actionCrash"/>
|
||||
@@ -806,6 +807,14 @@
|
||||
<string>Enable Seek to Import</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAudio_Scrubbing">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Audio Scrubbing</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
|
||||
+15
-12
@@ -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<QString> list = s.split(QRegExp("[:;]"));
|
||||
|
||||
for (int i=0;i<list.size();i++) {
|
||||
dout << "list" << i << list.at(i);
|
||||
}
|
||||
QList<QString> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
int recording_track;
|
||||
|
||||
void reset_all_audio();
|
||||
void assert_audio_device();
|
||||
void update_parents();
|
||||
|
||||
ViewerWidget* viewer_widget;
|
||||
|
||||
+5
-2
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
+25
-10
@@ -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<qint16>((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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+6
-5
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user