- Move Behavior-General options (hover focus, slider ladder, scroll zooms) into the General preferences tab. - Move Behavior-Audio option (audio scrubbing) into the Audio preferences tab. - Promote remaining Behavior categories (Timeline, Playback, Project, Nodes, Rendering) to top-level sidebar entries without the 'Behavior - ' prefix. - Update Chinese translations for the new sidebar titles and fill unfinished Behavior tab strings. NodeView: - Remove double-click jump-to-parameter-editor behavior; keep expand/collapse. - Add right-click context menu item 'Show in Parameter Editor'. - Add Shift+P shortcut bound to the same action. Render: - Fix crash in PreviewAutoCacher::ClearSingleFrameRenders when proxy playback causes a render ticket to finish synchronously before the watcher pointer is returned. Defer the watcher Finished signal via queued connection so the caller can safely register the watcher before it is deleted.
326 lines
9.9 KiB
C++
326 lines
9.9 KiB
C++
/***
|
|
|
|
Olive - Non-Linear Video Editor
|
|
Copyright (C) 2022 Olive Team
|
|
Modifications Copyright (C) 2025 mikesolar
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
#include "preferencesaudiotab.h"
|
|
|
|
#include <QGridLayout>
|
|
#include <QGroupBox>
|
|
#include <QLabel>
|
|
|
|
#include "audio/audiomanager.h"
|
|
#include "config/config.h"
|
|
|
|
namespace olive
|
|
{
|
|
|
|
PreferencesAudioTab::PreferencesAudioTab()
|
|
{
|
|
QVBoxLayout *audio_tab_layout = new QVBoxLayout(this);
|
|
|
|
{
|
|
// Backend Layout
|
|
QGridLayout *main_layout = new QGridLayout();
|
|
main_layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
int row = 0;
|
|
|
|
main_layout->addWidget(new QLabel(tr("Backend:")), row, 0);
|
|
|
|
audio_backend_combobox_ = new QComboBox();
|
|
connect(audio_backend_combobox_,
|
|
static_cast<void (QComboBox::*)(int)>(
|
|
&QComboBox::currentIndexChanged),
|
|
this, &PreferencesAudioTab::RefreshDevices);
|
|
main_layout->addWidget(audio_backend_combobox_, row, 1);
|
|
|
|
audio_tab_layout->addLayout(main_layout);
|
|
}
|
|
|
|
audio_scrubbing_ = new QCheckBox(
|
|
PreferencesBehaviorTab::BehaviorPrefTr("Enable audio scrubbing"));
|
|
audio_scrubbing_->setChecked(OLIVE_CONFIG("AudioScrubbing").toBool());
|
|
audio_tab_layout->addWidget(audio_scrubbing_);
|
|
|
|
{
|
|
QGroupBox *groupbox = new QGroupBox();
|
|
audio_tab_layout->addWidget(groupbox);
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(groupbox);
|
|
|
|
int row = 0;
|
|
|
|
{
|
|
// Output Group
|
|
QGroupBox *output_group = new QGroupBox();
|
|
output_group->setTitle(tr("Output"));
|
|
layout->addWidget(output_group);
|
|
|
|
QGridLayout *output_layout = new QGridLayout(output_group);
|
|
|
|
output_layout->addWidget(new QLabel(tr("Device:")), row, 0);
|
|
|
|
audio_output_devices_ = new QComboBox();
|
|
output_layout->addWidget(audio_output_devices_, row, 1);
|
|
|
|
row++;
|
|
|
|
{
|
|
int output_row = 0;
|
|
|
|
QGroupBox *output_param_group = new QGroupBox(tr("Advanced"));
|
|
output_layout->addWidget(output_param_group, row, 0, 1, 2);
|
|
|
|
QGridLayout *output_param_layout =
|
|
new QGridLayout(output_param_group);
|
|
|
|
output_param_layout->addWidget(new QLabel(tr("Sample Rate:")),
|
|
output_row, 0);
|
|
|
|
output_rate_combo_ = new SampleRateComboBox();
|
|
output_rate_combo_->SetSampleRate(
|
|
OLIVE_CONFIG("AudioOutputSampleRate").toInt());
|
|
output_param_layout->addWidget(output_rate_combo_, output_row,
|
|
1);
|
|
|
|
output_row++;
|
|
|
|
output_param_layout->addWidget(
|
|
new QLabel(tr("Channel Layout:")), output_row, 0);
|
|
|
|
output_ch_layout_combo_ = new ChannelLayoutComboBox();
|
|
output_ch_layout_combo_->SetChannelLayout(
|
|
OLIVE_CONFIG("AudioOutputChannelLayout").toULongLong());
|
|
output_param_layout->addWidget(output_ch_layout_combo_,
|
|
output_row, 1);
|
|
|
|
output_row++;
|
|
|
|
output_param_layout->addWidget(new QLabel(tr("Sample Format:")),
|
|
output_row, 0);
|
|
|
|
output_fmt_combo_ = new SampleFormatComboBox();
|
|
output_fmt_combo_->SetPackedFormats();
|
|
output_fmt_combo_->SetSampleFormat(SampleFormat::from_string(
|
|
OLIVE_CONFIG("AudioOutputSampleFormat")
|
|
.toString()
|
|
.toStdString()));
|
|
output_param_layout->addWidget(output_fmt_combo_, output_row,
|
|
1);
|
|
}
|
|
}
|
|
|
|
row = 0;
|
|
|
|
{
|
|
QGroupBox *input_group = new QGroupBox();
|
|
input_group->setTitle(tr("Input"));
|
|
layout->addWidget(input_group);
|
|
|
|
QGridLayout *input_layout = new QGridLayout(input_group);
|
|
|
|
input_layout->addWidget(new QLabel(tr("Device:")), row, 0);
|
|
|
|
audio_input_devices_ = new QComboBox();
|
|
input_layout->addWidget(audio_input_devices_, row, 1);
|
|
|
|
row++;
|
|
|
|
QGroupBox *recording_group = new QGroupBox(tr("Recording"));
|
|
input_layout->addWidget(recording_group, row, 0, 1, 2);
|
|
|
|
QVBoxLayout *recording_layout = new QVBoxLayout(recording_group);
|
|
|
|
QHBoxLayout *fmt_layout = new QHBoxLayout();
|
|
recording_layout->addLayout(fmt_layout);
|
|
|
|
fmt_layout->addWidget(new QLabel(tr("Format:")));
|
|
|
|
record_format_combo_ =
|
|
new ExportFormatComboBox(ExportFormatComboBox::kShowAudioOnly);
|
|
record_format_combo_->setSizePolicy(QSizePolicy::Expanding,
|
|
QSizePolicy::Expanding);
|
|
record_format_combo_->SetFormat(static_cast<ExportFormat::Format>(
|
|
OLIVE_CONFIG("AudioRecordingFormat").toInt()));
|
|
fmt_layout->addWidget(record_format_combo_);
|
|
|
|
record_options_ = new ExportAudioTab();
|
|
record_options_->SetFormat(record_format_combo_->GetFormat());
|
|
record_options_->SetCodec(static_cast<ExportCodec::Codec>(
|
|
OLIVE_CONFIG("AudioRecordingCodec").toInt()));
|
|
record_options_->sample_rate_combobox()->SetSampleRate(
|
|
OLIVE_CONFIG("AudioRecordingSampleRate").toInt());
|
|
record_options_->channel_layout_combobox()->SetChannelLayout(
|
|
OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong());
|
|
record_options_->bit_rate_slider()->SetValue(
|
|
OLIVE_CONFIG("AudioRecordingBitRate").toInt());
|
|
record_options_->sample_format_combobox()->SetSampleFormat(
|
|
SampleFormat::from_string(
|
|
OLIVE_CONFIG("AudioRecordingSampleFormat")
|
|
.toString()
|
|
.toStdString()));
|
|
recording_layout->addWidget(record_options_);
|
|
|
|
connect(record_format_combo_, &ExportFormatComboBox::FormatChanged,
|
|
record_options_, &ExportAudioTab::SetFormat);
|
|
}
|
|
|
|
QHBoxLayout *refresh_layout = new QHBoxLayout();
|
|
layout->addLayout(refresh_layout);
|
|
refresh_layout->addStretch();
|
|
|
|
refresh_devices_btn_ = new QPushButton(tr("Refresh Devices"));
|
|
refresh_layout->addWidget(refresh_devices_btn_);
|
|
|
|
connect(refresh_devices_btn_, &QPushButton::clicked, this,
|
|
&PreferencesAudioTab::HardRefreshBackends);
|
|
}
|
|
|
|
audio_tab_layout->addStretch();
|
|
|
|
// Populate lists
|
|
RefreshBackends();
|
|
}
|
|
|
|
void PreferencesAudioTab::Accept(MultiUndoCommand *command)
|
|
{
|
|
Q_UNUSED(command)
|
|
|
|
// Get device indexes
|
|
PaDeviceIndex output_device =
|
|
audio_output_devices_->currentData().value<PaDeviceIndex>();
|
|
PaDeviceIndex input_device =
|
|
audio_input_devices_->currentData().value<PaDeviceIndex>();
|
|
|
|
// Get device names, which seem to be the closest thing we have to a "unique identifier" for them
|
|
OLIVE_CONFIG("AudioOutput") = audio_output_devices_->currentText();
|
|
OLIVE_CONFIG("AudioInput") = audio_input_devices_->currentText();
|
|
|
|
// Set devices to be used from now on
|
|
AudioManager::instance()->SetOutputDevice(output_device);
|
|
AudioManager::instance()->SetInputDevice(input_device);
|
|
|
|
OLIVE_CONFIG("AudioOutputSampleRate") = output_rate_combo_->GetSampleRate();
|
|
OLIVE_CONFIG("AudioOutputChannelLayout") =
|
|
QVariant::fromValue(output_ch_layout_combo_->GetChannelLayout());
|
|
OLIVE_CONFIG("AudioOutputSampleFormat") = QString::fromStdString(
|
|
output_fmt_combo_->GetSampleFormat().to_string());
|
|
|
|
OLIVE_CONFIG("AudioRecordingFormat") = record_format_combo_->GetFormat();
|
|
OLIVE_CONFIG("AudioRecordingCodec") = record_options_->GetCodec();
|
|
OLIVE_CONFIG("AudioRecordingSampleRate") =
|
|
record_options_->sample_rate_combobox()->GetSampleRate();
|
|
OLIVE_CONFIG("AudioRecordingChannelLayout") = QVariant::fromValue(
|
|
record_options_->channel_layout_combobox()->GetChannelLayout());
|
|
OLIVE_CONFIG("AudioRecordingBitRate") =
|
|
QVariant::fromValue(record_options_->bit_rate_slider()->GetValue());
|
|
OLIVE_CONFIG("AudioRecordingSampleFormat") =
|
|
QString::fromStdString(record_options_->sample_format_combobox()
|
|
->GetSampleFormat()
|
|
.to_string());
|
|
|
|
emit AudioManager::instance() -> OutputParamsChanged();
|
|
|
|
OLIVE_CONFIG("AudioScrubbing") = audio_scrubbing_->isChecked();
|
|
}
|
|
|
|
void PreferencesAudioTab::RefreshBackends()
|
|
{
|
|
audio_backend_combobox_->clear();
|
|
for (PaHostApiIndex i = 0, end = Pa_GetHostApiCount(); i < end; i++) {
|
|
const PaHostApiInfo *info = Pa_GetHostApiInfo(i);
|
|
|
|
audio_backend_combobox_->addItem(info->name);
|
|
}
|
|
|
|
RefreshDevices();
|
|
|
|
AttemptToSetDevicesFromConfig();
|
|
}
|
|
|
|
void PreferencesAudioTab::RefreshDevices()
|
|
{
|
|
if (audio_backend_combobox_->count() == 0) {
|
|
return;
|
|
}
|
|
|
|
PaHostApiIndex host_index = audio_backend_combobox_->currentIndex();
|
|
const PaHostApiInfo *host = Pa_GetHostApiInfo(host_index);
|
|
|
|
audio_output_devices_->clear();
|
|
audio_input_devices_->clear();
|
|
|
|
for (int i = 0; i < host->deviceCount; i++) {
|
|
PaDeviceIndex device_index =
|
|
Pa_HostApiDeviceIndexToDeviceIndex(host_index, i);
|
|
const PaDeviceInfo *device = Pa_GetDeviceInfo(device_index);
|
|
|
|
if (device->maxOutputChannels) {
|
|
audio_output_devices_->addItem(device->name, device_index);
|
|
}
|
|
|
|
if (device->maxInputChannels) {
|
|
audio_input_devices_->addItem(device->name, device_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreferencesAudioTab::HardRefreshBackends()
|
|
{
|
|
AudioManager::instance()->HardReset();
|
|
RefreshBackends();
|
|
}
|
|
|
|
void PreferencesAudioTab::AttemptToSetDevicesFromConfig()
|
|
{
|
|
// Load with currently active devices
|
|
PaDeviceIndex current_output_index =
|
|
AudioManager::instance()->GetOutputDevice();
|
|
PaDeviceIndex current_input_index =
|
|
AudioManager::instance()->GetInputDevice();
|
|
|
|
const PaDeviceInfo *current_output = nullptr, *current_input = nullptr;
|
|
if (current_output_index != paNoDevice) {
|
|
current_output = Pa_GetDeviceInfo(current_output_index);
|
|
}
|
|
if (current_input_index != paNoDevice) {
|
|
current_input = Pa_GetDeviceInfo(current_input_index);
|
|
}
|
|
|
|
if (current_output || current_input) {
|
|
PaHostApiIndex host = current_output ? current_output->hostApi :
|
|
current_input->hostApi;
|
|
|
|
// Set backend accordingly
|
|
audio_backend_combobox_->setCurrentIndex(host);
|
|
|
|
// Device comboboxes should be populated correctly now
|
|
if (current_output) {
|
|
audio_output_devices_->setCurrentText(current_output->name);
|
|
}
|
|
|
|
if (current_input) {
|
|
audio_input_devices_->setCurrentText(current_input->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|