This commit is contained in:
itsmattkc
2018-11-04 11:08:23 +11:00
14 changed files with 85 additions and 37 deletions
+7 -2
View File
@@ -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
+1
View File
@@ -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);
+5
View File
@@ -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;
}
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
}
+1
View File
@@ -59,6 +59,7 @@ public:
int recording_track;
void reset_all_audio();
void assert_audio_device();
void update_parents();
ViewerWidget* viewer_widget;
+5 -2
View File
@@ -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();
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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();