From d4692b94802ffa92434a21cfc659402d95d3c1d7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 26 Oct 2018 20:02:48 +1100 Subject: [PATCH] added in-timeline recording --- panels/project.cpp | 4 +++ panels/project.h | 2 ++ panels/timeline.cpp | 10 +++---- panels/timeline.h | 3 +- panels/viewer.cpp | 70 +++++++++++++++++++++++++++++++++++++++++-- panels/viewer.h | 11 +++++++ playback/audio.cpp | 29 ++++++++++++++---- playback/audio.h | 1 + playback/cacher.cpp | 6 ++-- ui/timelinewidget.cpp | 56 ++++++++++++++++++++++++++++++---- ui/viewerwidget.cpp | 3 ++ 11 files changed, 172 insertions(+), 23 deletions(-) diff --git a/panels/project.cpp b/panels/project.cpp index 104c01e01..592d3af66 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -371,6 +371,8 @@ void Project::process_file_list(bool recursive, QStringList& files, QTreeWidgetI QVector image_sequence_importassequence; QStringList image_sequence_formats = config.img_seq_formats.split("|"); + if (!recursive) last_imported_media.clear(); + bool create_undo_action = (!recursive && replace == NULL); ComboAction* ca; if (create_undo_action) ca = new ComboAction(); @@ -494,6 +496,8 @@ void Project::process_file_list(bool recursive, QStringList& files, QTreeWidgetI set_footage_of_tree(item, m); + last_imported_media.append(m); + if (replace == NULL) { if (create_undo_action) { ca->append(new AddMediaCommand(item, parent)); diff --git a/panels/project.h b/panels/project.h index d05b8e84c..7b73cacef 100644 --- a/panels/project.h +++ b/panels/project.h @@ -68,6 +68,8 @@ public: QVector list_all_project_sequences(); SourceTable* source_table; + + QVector last_imported_media; public slots: void import_dialog(); void delete_selected_media(); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index b500ba161..a7c024247 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -94,9 +94,7 @@ Timeline::Timeline(QWidget *parent) : connect(ui->videoScrollbar, SIGNAL(valueChanged(int)), ui->video_area, SLOT(setScroll(int))); connect(ui->audioScrollbar, SIGNAL(valueChanged(int)), ui->audio_area, SLOT(setScroll(int))); - update_sequence(); - - ui->recordButton->setVisible(false); + update_sequence(); } Timeline::~Timeline() @@ -1147,7 +1145,7 @@ void Timeline::on_addButton_clicked() { void Timeline::addMenuItem(QAction* action) { creating = true; - creatingObject = action->data().toInt(); + creating_object = action->data().toInt(); } void Timeline::setScroll(int s) { @@ -1157,5 +1155,7 @@ void Timeline::setScroll(int s) { } void Timeline::on_recordButton_clicked() { - qDebug() << "recording is a" << start_recording(); + creating = true; + creating_object = ADD_OBJ_AUDIO; + //qDebug() << "recording is a" << start_recording(); } diff --git a/panels/timeline.h b/panels/timeline.h index cad39b7af..baba1157f 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -15,6 +15,7 @@ #define ADD_OBJ_BARS 2 #define ADD_OBJ_TONE 3 #define ADD_OBJ_NOISE 4 +#define ADD_OBJ_AUDIO 5 class QPushButton; class SourceTable; @@ -160,7 +161,7 @@ public: // creating variables bool creating; - int creatingObject; + int creating_object; Ui::Timeline *ui; public slots: diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 95d76dc1f..e987b8777 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -3,6 +3,7 @@ #include "playback/audio.h" #include "timeline.h" +#include "panels/project.h" #include "project/sequence.h" #include "panels/panels.h" #include "io/config.h" @@ -25,13 +26,15 @@ extern "C" { #include #include #include +#include Viewer::Viewer(QWidget *parent) : QDockWidget(parent), ui(new Ui::Viewer), seq(NULL), playing(false), - created_sequence(false) + created_sequence(false), + cue_recording_internal(false) { ui->setupUi(this); ui->headers->viewer = this; @@ -49,7 +52,10 @@ Viewer::Viewer(QWidget *parent) : ui->currentTimecode->set_display_type(LABELSLIDER_FRAMENUMBER); connect(ui->currentTimecode, SIGNAL(valueChanged()), this, SLOT(update_playhead())); + recording_flasher.setInterval(500); + connect(&playback_updater, SIGNAL(timeout()), this, SLOT(timer_update())); + connect(&recording_flasher, SIGNAL(timeout()), this, SLOT(recording_flasher_update())); update_playhead_timecode(0); update_end_timecode(); @@ -231,6 +237,25 @@ void Viewer::go_to_end() { } } +void Viewer::cue_recording(long start, long end, int track) { + recording_start = start; + recording_end = end; + recording_track = track; + panel_sequence_viewer->seek(recording_start); + cue_recording_internal = true; + recording_flasher.start(); +} + +void Viewer::uncue_recording() { + cue_recording_internal = false; + recording_flasher.stop(); + ui->pushButton_3->setStyleSheet(QString()); +} + +bool Viewer::is_recording_cued() { + return cue_recording_internal; +} + void Viewer::toggle_play() { if (playing) { pause(); @@ -249,6 +274,10 @@ void Viewer::play() { || audio_output->format().channelCount() != av_get_channel_layout_nb_channels(seq->audio_layout)) { init_audio(seq); } + if (is_recording_cued() && !start_recording()) { + qDebug() << "[ERROR] Failed to record audio"; + return; + } playhead_start = seq->playhead; start_msecs = QDateTime::currentMSecsSinceEpoch(); playing = true; @@ -263,7 +292,36 @@ void Viewer::pause() { playing = false; set_playpause_icon(true); playback_updater.stop(); - stop_recording(); + + if (is_recording_cued()) { + uncue_recording(); + + if (recording) { + stop_recording(); + + // import audio + panel_project->process_file_list(false, QStringList(get_recorded_audio_filename()), NULL, NULL); + + // add it to the sequence + Clip* c = new Clip(seq); + Media* m = panel_project->last_imported_media.at(0); + c->media = m; // latest media + c->media_type = MEDIA_TYPE_FOOTAGE; + c->media_stream = 0; + c->timeline_in = recording_start; + c->timeline_out = seq->playhead; + c->clip_in = 0; + c->track = recording_track; + c->color_r = 128; + c->color_g = 192; + c->color_b = 128; + c->name = m->name; + + QVector add_clips; + add_clips.append(c); + undo_stack.push(new AddClipCommand(seq, add_clips)); // add clip + } + } } void Viewer::update_playhead_timecode(long p) { @@ -417,6 +475,14 @@ void Viewer::timer_update() { update_parents(); } +void Viewer::recording_flasher_update() { + if (ui->pushButton_3->styleSheet().isEmpty()) { + ui->pushButton_3->setStyleSheet("background: red;"); + } else { + ui->pushButton_3->setStyleSheet(QString()); + } +} + void Viewer::clean_created_seq() { viewer_widget->waveform = false; diff --git a/panels/viewer.h b/panels/viewer.h index a06342612..93b4727e2 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -51,6 +51,13 @@ public: qint64 start_msecs; QTimer playback_updater; + void cue_recording(long start, long end, int track); + void uncue_recording(); + bool is_recording_cued(); + long recording_start; + long recording_end; + int recording_track; + void reset_all_audio(); void update_parents(); @@ -70,11 +77,15 @@ private slots: void on_pushButton_3_clicked(); void update_playhead(); void timer_update(); + void recording_flasher_update(); private: void clean_created_seq(); void set_sequence(bool main, Sequence* s); bool main_sequence; bool created_sequence; + + bool cue_recording_internal; + QTimer recording_flasher; }; #endif // VIEWER_H diff --git a/playback/audio.cpp b/playback/audio.cpp index d5666074b..1a59643ca 100644 --- a/playback/audio.cpp +++ b/playback/audio.cpp @@ -2,6 +2,7 @@ #include "project/sequence.h" +#include "panels/project.h" #include "panels/panels.h" #include "panels/timeline.h" #include "panels/viewer.h" @@ -11,6 +12,7 @@ #include #include #include +#include #include extern "C" { @@ -263,7 +265,22 @@ bool start_recording() { qDebug() << "[ERROR] No active sequence to record into"; return false; } - output_recording.setFileName("C:/Users/Matt/output.wav"); + + QString audio_path = project_url + " Audio"; + QDir audio_dir(audio_path); + if (!audio_dir.exists() && !audio_dir.mkpath(".")) { + qDebug() << "[ERROR] Failed to create audio directory"; + return false; + } + + QString audio_filename; + int file_number = 0; + do { + file_number++; + audio_filename = audio_path + "/Recording " + QString::number(file_number) + ".wav"; + } while (QFile(audio_filename).exists()); + + output_recording.setFileName(audio_filename); if (!output_recording.open(QFile::WriteOnly)) { qDebug() << "[ERROR] Failed to open output file. Does Olive have permission to write to this directory?"; return false; @@ -287,16 +304,16 @@ void stop_recording() { if (recording) { audio_input->stop(); - /*if (audio_input->state() == QAudio::ActiveState) { - - }*/ - write_wave_trailer(output_recording); output_recording.close(); delete audio_input; audio_input = NULL; + recording = false; } - recording = false; +} + +const QString& get_recorded_audio_filename() { + return output_recording.fileName(); } diff --git a/playback/audio.h b/playback/audio.h index 913ed0ac9..9fb47b91d 100644 --- a/playback/audio.h +++ b/playback/audio.h @@ -48,6 +48,7 @@ int get_buffer_offset_from_frame(Sequence *s, long frame); bool start_recording(); void stop_recording(); +const QString& get_recorded_audio_filename(); extern bool recording; #endif // AUDIO_H diff --git a/playback/cacher.cpp b/playback/cacher.cpp index 641209d5d..5a04af50e 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -293,14 +293,13 @@ void cache_audio_worker(Clip* c, Clip* nest) { break; case MEDIA_TYPE_TONE: frame = c->frame; - nb_bytes = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, static_cast(frame->format), 1); - if (c->frame_sample_index < 0 || c->frame_sample_index >= nb_bytes) { + nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; + while ((c->frame_sample_index == -1 || c->frame_sample_index >= nb_bytes) && nb_bytes > 0) { // create "new frame" memset(c->frame->data[0], 0, nb_bytes); apply_audio_effects(c, bytes_to_seconds(frame->pts, frame->channels, frame->sample_rate), frame, nb_bytes); c->frame->pts += nb_bytes; c->frame_sample_index = 0; - qDebug() << "2 >> c->audio_target_frame:" << c->audio_target_frame; if (c->audio_buffer_write == 0) c->audio_buffer_write = get_buffer_offset_from_frame(c->sequence, qMax(timeline_in, c->audio_target_frame)); int offset = audio_ibuffer_read - c->audio_buffer_write; if (offset > 0) { @@ -546,6 +545,7 @@ void reset_cache(Clip* c, long target_frame) { } break; case MEDIA_TYPE_TONE: + c->reached_end = false; c->audio_target_frame = target_frame; c->frame_sample_index = -1; c->frame->pts = 0; diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 658136d9c..746e23440 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -1,5 +1,6 @@ #include "timelinewidget.h" +#include "playback/audio.h" #include "panels/panels.h" #include "io/config.h" #include "project/sequence.h" @@ -60,8 +61,6 @@ void TimelineWidget::right_click_ripple() { s.out = rc_ripple_max; s.track = panel_timeline->cursor_track; - qDebug() << s.in << s.out << s.track; - sels.append(s); panel_timeline->delete_selection(sels, true); @@ -700,7 +699,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) { if (panel_timeline->creating) { int comp = 0; - switch (panel_timeline->creatingObject) { + switch (panel_timeline->creating_object) { case ADD_OBJ_TITLE: case ADD_OBJ_SOLID: case ADD_OBJ_BARS: @@ -708,6 +707,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) { break; case ADD_OBJ_TONE: case ADD_OBJ_NOISE: + case ADD_OBJ_AUDIO: comp = 1; break; } @@ -857,7 +857,10 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { if (panel_timeline->ghosts.size() > 0) { const Ghost& g = panel_timeline->ghosts.at(0); - if (g.in != g.out) { + if (panel_timeline->creating_object == ADD_OBJ_AUDIO) { + panel_sequence_viewer->cue_recording(qMin(g.in, g.out), qMax(g.in, g.out), g.track); + panel_timeline->creating = false; + } else if (g.in != g.out) { Clip* c = new Clip(sequence); c->media = NULL; c->timeline_in = qMin(g.in, g.out); @@ -878,7 +881,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { c->media_type = MEDIA_TYPE_SOLID; } - switch (panel_timeline->creatingObject) { + switch (panel_timeline->creating_object) { case ADD_OBJ_TITLE: c->name = "Title"; c->effects.append(create_effect(VIDEO_TEXT_EFFECT, c)); @@ -2004,7 +2007,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) { for (int i=0;iclips.size();i++) { Clip* clip = sequence->clips.at(i); if (clip != NULL && is_track_visible(clip->track)) { - QRect clip_rect(panel_timeline->getTimelineScreenPointFromFrame(clip->timeline_in), getScreenPointFromTrack(clip->track), clip->getLength() * panel_timeline->zoom, panel_timeline->calculate_track_height(clip->track, -1)); + QRect clip_rect(panel_timeline->getTimelineScreenPointFromFrame(clip->timeline_in), getScreenPointFromTrack(clip->track), getScreenPointFromFrame(panel_timeline->zoom, clip->getLength()), panel_timeline->calculate_track_height(clip->track, -1)); QRect text_rect(clip_rect.left() + CLIP_TEXT_PADDING, clip_rect.top() + CLIP_TEXT_PADDING, clip_rect.width() - CLIP_TEXT_PADDING - 1, clip_rect.height() - CLIP_TEXT_PADDING - 1); if (clip_rect.left() < width() && clip_rect.right() >= 0 && clip_rect.top() < height() && clip_rect.bottom() >= 0) { QRect actual_clip_rect = clip_rect; @@ -2202,6 +2205,47 @@ void TimelineWidget::paintEvent(QPaintEvent*) { } } + // Draw recording clip if recording if valid + if (panel_sequence_viewer->is_recording_cued() && is_track_visible(panel_sequence_viewer->recording_track)) { + int rec_track_x = panel_timeline->getTimelineScreenPointFromFrame(panel_sequence_viewer->recording_start); + int rec_track_y = getScreenPointFromTrack(panel_sequence_viewer->recording_track); + int rec_track_height = panel_timeline->calculate_track_height(panel_sequence_viewer->recording_track, -1); + if (panel_sequence_viewer->recording_start != panel_sequence_viewer->recording_end) { + QRect rec_rect( + rec_track_x, + rec_track_y, + getScreenPointFromFrame(panel_timeline->zoom, panel_sequence_viewer->recording_end - panel_sequence_viewer->recording_start), + rec_track_height + ); + p.setPen(QPen(QColor(96, 96, 96), 2)); + p.fillRect(rec_rect, QColor(192, 192, 192)); + p.drawRect(rec_rect); + } + QRect active_rec_rect( + rec_track_x, + rec_track_y, + getScreenPointFromFrame(panel_timeline->zoom, panel_sequence_viewer->seq->playhead - panel_sequence_viewer->recording_start), + rec_track_height + ); + p.setPen(QPen(QColor(192, 0, 0), 2)); + p.fillRect(active_rec_rect, QColor(255, 96, 96)); + p.drawRect(active_rec_rect); + + p.setPen(Qt::NoPen); + + if (!panel_sequence_viewer->playing) { + int rec_marker_size = 8; + int rec_track_midY = rec_track_y + (rec_track_height >> 1); + p.setBrush(Qt::white); + QPoint cue_marker[3] = { + QPoint(rec_track_x, rec_track_midY - rec_marker_size), + QPoint(rec_track_x + rec_marker_size, rec_track_midY), + QPoint(rec_track_x, rec_track_midY + rec_marker_size) + }; + p.drawPolygon(cue_marker, 3); + } + } + // Draw track lines if (config.show_track_lines) { p.setPen(QColor(0, 0, 0, 96)); diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index dd5b74089..8d5efd057 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -529,9 +529,12 @@ void ViewerWidget::paintGL() { glDisable(GL_TEXTURE_2D); } while (loop); + // TODO this could be cleaned up but it's not a priority rn if (!recording && viewer->playing && (viewer->seq->playhead == viewer->seq->getEndFrame() || (viewer->seq->using_workarea && viewer->seq->playhead >= viewer->seq->workarea_out))) { viewer->pause(); + } else if (recording && viewer->recording_start != viewer->recording_end && viewer->seq->playhead >= viewer->recording_end) { + viewer->pause(); } }