# The slider (pitch bend) is mapped to master speed.
master_speed: { controller_number: 128 }
+
+# Master speed lock is mapped to lock.
+toggle_lock: { note_number: 25 }
+locked: { note_number: 25 }
optional int32 play_bank = 25;
optional MIDILightProto play_enabled = 26;
+ optional MIDIButtonProto toggle_lock = 36;
+ optional int32 toggle_lock_bank = 37;
+ optional MIDILightProto locked = 38;
+
optional MIDIButtonProto cue_in = 27;
optional int32 cue_in_bank = 28;
optional MIDILightProto cue_in_enabled = 29; // In practice always true currently.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2019 Yngve Molnes, licensed under GPLv3 -->
+<svg width="18px" height="24px" viewBox="0 0 18 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>lock</title>
+ <defs>https://www.gnu.org/licenses/gpl-3.0.txt</defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="Artboard" transform="translate(-470.000000, -322.000000)" stroke="#979797">
+ <g id="lock" transform="translate(470.000000, 323.000000)">
+ <rect id="Rectangle-2" stroke-width="2" fill="#D8D8D8" x="1" y="8" width="16" height="14" rx="2"></rect>
+ <path d="M3,7.85714286 C3.01575393,5.22004386 3.5871825,3.25575815 4.71428571,1.96428571 C5.85714286,0.654761905 7.28571429,3.72189052e-14 9,0 C10.7142857,2.06266882e-14 12.1428571,0.654761905 13.2857143,1.96428571 C14.4285714,3.27380952 15,5.23809524 15,7.85714286" id="Path-2" stroke-width="2"></path>
+ <g id="Group" transform="translate(7.000000, 12.000000)" fill="#979797">
+ <circle id="Oval" cx="2" cy="2" r="2"></circle>
+ <path d="M1.10442487,2.5 L0.571091532,6.5 L3.42890847,6.5 L2.89557513,2.5 L1.10442487,2.5 Z" id="Rectangle-3"></path>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
ui->stop_btn->setEnabled(false);
connect(ui->speed_slider, &QAbstractSlider::valueChanged, this, &MainWindow::speed_slider_changed);
+ connect(ui->speed_lock_btn, &QPushButton::clicked, this, &MainWindow::speed_lock_clicked);
connect(ui->playlist_duplicate_btn, &QPushButton::clicked, this, &MainWindow::playlist_duplicate);
void MainWindow::speed_slider_changed(int percent)
{
float speed = percent / 100.0f;
- ui->speed_label->setText(QString::fromStdString(to_string(percent) + "%"));
+ ui->speed_lock_btn->setText(QString::fromStdString(" " + to_string(percent) + "%"));
live_player->set_master_speed(speed);
}
+void MainWindow::speed_lock_clicked()
+{
+ // TODO: Make for a less abrupt transition if we're not already at 100%.
+ ui->speed_slider->setValue(100); // Also actually sets the master speed and updates the label.
+ ui->speed_slider->setEnabled(!ui->speed_lock_btn->isChecked());
+ midi_mapper.set_locked(ui->speed_lock_btn->isChecked());
+}
+
void MainWindow::live_player_done()
{
playlist_selection_changed();
});
}
+void MainWindow::toggle_lock()
+{
+ post_to_main_thread([this] {
+ ui->speed_lock_btn->setChecked(!ui->speed_lock_btn->isChecked());
+ speed_lock_clicked();
+ });
+}
+
void MainWindow::jog(int delta)
{
post_to_main_thread([this, delta] {
speed = min(max(speed, 0.1f), 2.0f);
post_to_main_thread([this, speed] {
+ if (ui->speed_lock_btn->isChecked()) {
+ return;
+ }
+
int percent = lrintf(speed * 100.0f);
ui->speed_slider->blockSignals(true);
ui->speed_slider->setValue(percent);
ui->speed_slider->blockSignals(false);
- ui->speed_label->setText(QString::fromStdString(to_string(percent) + "%"));
- });
+ ui->speed_lock_btn->setText(QString::fromStdString(" " + to_string(percent) + "%"));
- live_player->set_master_speed(speed);
+ live_player->set_master_speed(speed);
+ });
}
void MainWindow::cue_in()
void preview() override;
void queue() override;
void play() override;
+ void toggle_lock() override;
void jog(int delta) override;
void switch_camera(unsigned camera_idx) override;
void set_master_speed(float speed) override;
void play_clicked();
void stop_clicked();
void speed_slider_changed(int percent);
+ void speed_lock_clicked();
void live_player_done();
void live_player_clip_progress(const std::map<uint64_t, double> &progress, double time_remaining);
void set_output_status(const std::string &status);
--- /dev/null
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>lock.svg</file>
+</qresource>
+</RCC>
</item>
<item>
<widget class="QSlider" name="speed_slider">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="minimum">
<number>10</number>
</property>
</widget>
</item>
<item>
- <widget class="QLabel" name="speed_label">
+ <widget class="QPushButton" name="speed_lock_btn">
<property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
- <width>40</width>
+ <width>72</width>
<height>0</height>
</size>
</property>
<property name="text">
- <string>100%</string>
+ <string> 100%</string>
</property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
+ <property name="icon">
+ <iconset>
+ <normalon>:/lock.svg</normalon>
+ </iconset>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
</property>
</widget>
</item>
<header>jpeg_frame_view.h</header>
</customwidget>
</customwidgets>
- <resources/>
+ <resources>
+ <include location="mainwindow.qrc"/>
+ </resources>
<connections/>
</ui>
bind(&ControllerReceiver::queue, receiver));
match_button(note, MIDIMappingProto::kPlayFieldNumber, MIDIMappingProto::kPlayBankFieldNumber,
bind(&ControllerReceiver::play, receiver));
+ match_button(note, MIDIMappingProto::kToggleLockFieldNumber, MIDIMappingProto::kToggleLockBankFieldNumber,
+ bind(&ControllerReceiver::toggle_lock, receiver));
unsigned num_cameras = std::min(MAX_STREAMS, mapping_proto->camera_size());
for (unsigned camera_idx = 0; camera_idx < num_cameras; ++camera_idx) {
if (play_enabled_light) {
activate_mapped_light(*mapping_proto, MIDIMappingProto::kPlayEnabledFieldNumber, &active_lights);
}
+ if (locked_light) {
+ activate_mapped_light(*mapping_proto, MIDIMappingProto::kLockedFieldNumber, &active_lights);
+ }
if (current_highlighted_camera >= 0 && current_highlighted_camera < mapping_proto->camera_size()) {
const CameraMIDIMappingProto &camera = mapping_proto->camera(current_highlighted_camera);
activate_mapped_light(camera, CameraMIDIMappingProto::kIsCurrentFieldNumber, &active_lights);
virtual void preview() = 0;
virtual void queue() = 0;
virtual void play() = 0;
+ virtual void toggle_lock() = 0;
virtual void jog(int delta) = 0;
virtual void switch_camera(unsigned camera_idx) = 0;
virtual void set_master_speed(float speed) = 0;
play_enabled_light = enabled;
refresh_lights();
}
+ void set_locked(bool locked) {
+ locked_light = locked;
+ refresh_lights();
+ }
void highlight_camera_input(int stream_idx) { // -1 for none.
current_highlighted_camera = stream_idx;
refresh_lights();
std::atomic<bool> preview_enabled_light{false};
std::atomic<bool> queue_enabled_light{false};
std::atomic<bool> play_enabled_light{false};
+ std::atomic<bool> locked_light{true};
std::atomic<int> current_highlighted_camera{-1};
MIDIDevice midi_device;
moc_files = qt5.preprocess(
moc_headers: ['futatabi/mainwindow.h', 'futatabi/jpeg_frame_view.h', 'futatabi/clip_list.h'],
ui_files: ['futatabi/mainwindow.ui'],
+ qresources: ['futatabi/mainwindow.qrc'],
dependencies: qt5deps)
# Flow objects.