use audioprocessor to match device's output
This commit is contained in:
@@ -80,10 +80,11 @@ int InputCallback(const void *input, void *output, unsigned long frameCount, con
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
void AudioManager::PushToOutput(const AudioParams ¶ms, const QByteArray &samples)
|
||||
bool AudioManager::PushToOutput(const AudioParams ¶ms, const QByteArray &samples, QString *error)
|
||||
{
|
||||
if (output_device_ == paNoDevice) {
|
||||
return;
|
||||
if (error) *error = tr("No output device is set");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (output_params_ != params || output_stream_ == nullptr) {
|
||||
@@ -93,7 +94,13 @@ void AudioManager::PushToOutput(const AudioParams ¶ms, const QByteArray &sam
|
||||
|
||||
PaStreamParameters p = GetPortAudioParams(params, output_device_);
|
||||
|
||||
Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_);
|
||||
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_);
|
||||
if (r != paNoError) {
|
||||
// Unhandled error
|
||||
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
|
||||
if (error) *error = Pa_GetErrorText(r);
|
||||
return false;
|
||||
}
|
||||
|
||||
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
|
||||
}
|
||||
@@ -103,6 +110,8 @@ void AudioManager::PushToOutput(const AudioParams ¶ms, const QByteArray &sam
|
||||
if (!Pa_IsStreamActive(output_stream_)) {
|
||||
Pa_StartStream(output_stream_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioManager::ClearBufferedOutput()
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <portaudio.h>
|
||||
|
||||
#include "audiovisualwaveform.h"
|
||||
#include "audio/audioprocessor.h"
|
||||
#include "common/define.h"
|
||||
#include "codec/ffmpeg/ffmpegencoder.h"
|
||||
#include "render/audioparams.h"
|
||||
@@ -52,7 +53,7 @@ public:
|
||||
|
||||
void SetOutputNotifyInterval(int n);
|
||||
|
||||
void PushToOutput(const AudioParams ¶ms, const QByteArray& samples);
|
||||
bool PushToOutput(const AudioParams ¶ms, const QByteArray& samples, QString *error = nullptr);
|
||||
|
||||
void ClearBufferedOutput();
|
||||
|
||||
@@ -86,6 +87,8 @@ public:
|
||||
signals:
|
||||
void OutputNotify();
|
||||
|
||||
void OutputParamsChanged();
|
||||
|
||||
private:
|
||||
AudioManager();
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ public:
|
||||
|
||||
void Flush();
|
||||
|
||||
const AudioParams &from() const { return from_; }
|
||||
const AudioParams &to() const { return to_; }
|
||||
|
||||
private:
|
||||
static AVFilterContext* CreateTempoFilter(AVFilterGraph *graph, AVFilterContext *link, const double& tempo);
|
||||
|
||||
|
||||
@@ -121,6 +121,10 @@ void Config::SetDefaults()
|
||||
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString());
|
||||
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
|
||||
|
||||
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt, 48000);
|
||||
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
|
||||
SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kInt, AudioParams::kFormatSigned16Packed);
|
||||
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM);
|
||||
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000);
|
||||
|
||||
@@ -69,6 +69,40 @@ PreferencesAudioTab::PreferencesAudioTab()
|
||||
|
||||
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(static_cast<AudioParams::Format>(OLIVE_CONFIG("AudioOutputSampleFormat").toInt()));
|
||||
output_param_layout->addWidget(output_fmt_combo_, output_row, 1);
|
||||
}
|
||||
}
|
||||
|
||||
row = 0;
|
||||
@@ -146,12 +180,18 @@ void PreferencesAudioTab::Accept(MultiUndoCommand *command)
|
||||
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") = output_fmt_combo_->GetSampleFormat();
|
||||
|
||||
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") = record_options_->sample_format_combobox()->GetSampleFormat();
|
||||
|
||||
emit AudioManager::instance()->OutputParamsChanged();
|
||||
}
|
||||
|
||||
void PreferencesAudioTab::RefreshBackends()
|
||||
|
||||
@@ -61,6 +61,10 @@ private:
|
||||
*/
|
||||
QPushButton* refresh_devices_btn_;
|
||||
|
||||
SampleRateComboBox *output_rate_combo_;
|
||||
ChannelLayoutComboBox *output_ch_layout_combo_;
|
||||
SampleFormatComboBox *output_fmt_combo_;
|
||||
|
||||
ExportFormatComboBox *record_format_combo_;
|
||||
|
||||
ExportAudioTab *record_options_;
|
||||
|
||||
@@ -57,6 +57,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void SetPackedFormats()
|
||||
{
|
||||
AudioParams::Format tmp = AudioParams::kFormatInvalid;
|
||||
|
||||
if (attempt_to_restore_format_) {
|
||||
tmp = GetSampleFormat();
|
||||
}
|
||||
|
||||
clear();
|
||||
for (int i=AudioParams::kPackedStart; i<AudioParams::kPackedEnd; i++) {
|
||||
AddFormatItem(static_cast<AudioParams::Format>(i));
|
||||
}
|
||||
|
||||
if (attempt_to_restore_format_) {
|
||||
SetSampleFormat(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
AudioParams::Format GetSampleFormat() const
|
||||
{
|
||||
return static_cast<AudioParams::Format>(this->currentData().toInt());
|
||||
|
||||
@@ -141,6 +141,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
|
||||
connect(Core::instance(), &Core::ColorPickerEnabled, this, &ViewerWidget::SetSignalCursorColorEnabled);
|
||||
connect(this, &ViewerWidget::CursorColor, Core::instance(), &Core::ColorPickerColorEmitted);
|
||||
connect(AudioManager::instance(), &AudioManager::OutputParamsChanged, this, &ViewerWidget::UpdateAudioProcessor);
|
||||
}
|
||||
|
||||
ViewerWidget::~ViewerWidget()
|
||||
@@ -457,12 +458,16 @@ void ViewerWidget::DisarmRecording()
|
||||
|
||||
void ViewerWidget::UpdateAudioProcessor()
|
||||
{
|
||||
audio_processor_.Close();
|
||||
if (GetConnectedNode()) {
|
||||
audio_processor_.Close();
|
||||
|
||||
AudioParams ap = GetConnectedNode()->GetAudioParams();
|
||||
AudioParams packed = ap;
|
||||
packed.set_format(AudioParams::GetPackedEquivalent(ap.format()));
|
||||
audio_processor_.Open(ap, packed, (playback_speed_ == 0) ? 1 : std::abs(playback_speed_));
|
||||
AudioParams ap = GetConnectedNode()->GetAudioParams();
|
||||
AudioParams packed(OLIVE_CONFIG("AudioOutputSampleRate").toInt(),
|
||||
OLIVE_CONFIG("AudioOutputChannelLayout").toULongLong(),
|
||||
static_cast<AudioParams::Format>(OLIVE_CONFIG("AudioOutputSampleFormat").toInt()));
|
||||
|
||||
audio_processor_.Open(ap, packed, (playback_speed_ == 0) ? 1 : std::abs(playback_speed_));
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::CloseAudioProcessor()
|
||||
@@ -520,7 +525,7 @@ void ViewerWidget::ReceivedAudioBufferForPlayback()
|
||||
prequeued_audio_.append(pack);
|
||||
} else {
|
||||
// Push directly to audio manager
|
||||
AudioManager::instance()->PushToOutput(GetConnectedNode()->GetAudioParams(), pack);
|
||||
AudioManager::instance()->PushToOutput(audio_processor_.to(), pack);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -560,9 +565,12 @@ void ViewerWidget::ReceivedAudioBufferForScrubbing()
|
||||
|
||||
if (r >= 0) {
|
||||
if (!buf.empty()) {
|
||||
QString error;
|
||||
const QByteArray &packed = buf.at(0);
|
||||
AudioManager::instance()->ClearBufferedOutput();
|
||||
AudioManager::instance()->PushToOutput(samples->audio_params(), packed);
|
||||
if (!AudioManager::instance()->PushToOutput(audio_processor_.to(), packed, &error)) {
|
||||
Core::instance()->ShowStatusBarMessage(tr("Audio scrubbing failed: %1").arg(error));
|
||||
}
|
||||
AudioMonitor::PushBytesOnAll(packed);
|
||||
}
|
||||
} else {
|
||||
@@ -872,7 +880,11 @@ void ViewerWidget::FinishPlayPreprocess()
|
||||
|
||||
// Start audio waveform playback
|
||||
if (!prequeued_audio_.isEmpty()) {
|
||||
AudioManager::instance()->PushToOutput(GetConnectedNode()->GetAudioParams(), prequeued_audio_);
|
||||
QString error;
|
||||
if (!AudioManager::instance()->PushToOutput(audio_processor_.to(), prequeued_audio_, &error)) {
|
||||
QMessageBox::critical(this, tr("Audio Error"), tr("Failed to start audio: %1\n\n"
|
||||
"Please check your audio preferences and try again.").arg(error));
|
||||
}
|
||||
prequeued_audio_.clear();
|
||||
|
||||
AudioMonitor::StartWaveformOnAll(&GetConnectedNode()->audio_playback_cache()->visual(),
|
||||
|
||||
@@ -211,7 +211,6 @@ private:
|
||||
|
||||
void DisarmRecording();
|
||||
|
||||
void UpdateAudioProcessor();
|
||||
void CloseAudioProcessor();
|
||||
|
||||
QStackedWidget* stack_;
|
||||
@@ -319,6 +318,8 @@ private slots:
|
||||
|
||||
void ForceRequeueFromCurrentTime();
|
||||
|
||||
void UpdateAudioProcessor();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user