completed audio recording implementation

This commit is contained in:
itsmattkc
2022-05-02 17:05:11 -07:00
parent 026cc9f064
commit ef515579df
19 changed files with 204 additions and 37 deletions
+4 -8
View File
@@ -184,25 +184,21 @@ void AudioManager::HardReset()
Pa_Initialize();
}
bool AudioManager::StartRecording(const QString &filename, const AudioParams &params)
bool AudioManager::StartRecording(const EncodingParams &params)
{
if (input_device_ == paNoDevice) {
return false;
}
EncodingParams encode_param;
encode_param.EnableAudio(params, ExportCodec::kCodecMP3);
encode_param.SetFilename(filename);
input_encoder_ = new FFmpegEncoder(encode_param);
input_encoder_ = new FFmpegEncoder(params);
if (!input_encoder_->Open()) {
qCritical() << "Failed to open encoder for recording";
return false;
}
PaStreamParameters p = GetPortAudioParams(params, input_device_);
PaStreamParameters p = GetPortAudioParams(params.audio_params(), input_device_);
if (Pa_OpenStream(&input_stream_, &p, nullptr, params.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_) == paNoError) {
if (Pa_OpenStream(&input_stream_, &p, nullptr, params.audio_params().sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_) == paNoError) {
if (Pa_StartStream(input_stream_) == paNoError) {
return true;
}
+1 -1
View File
@@ -74,7 +74,7 @@ public:
void HardReset();
bool StartRecording(const QString &filename, const AudioParams &params);
bool StartRecording(const EncodingParams &params);
void StopRecording();
+22 -5
View File
@@ -50,7 +50,7 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
{
QStringList pix_fmts;
const AVCodec* codec_info = GetEncoder(c);
const AVCodec* codec_info = GetEncoder(c, AudioParams::kFormatInvalid);
if (codec_info) {
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
@@ -579,7 +579,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
}
// Find encoder
const AVCodec* encoder = GetEncoder(codec);
const AVCodec* encoder = GetEncoder(codec, params().audio_params().format());
if (!encoder) {
SetError(tr("Failed to find codec for 0x%1").arg(codec, 16));
return false;
@@ -653,7 +653,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
codec_ctx->sample_rate = params().audio_params().sample_rate();
codec_ctx->channel_layout = params().audio_params().channel_layout();
codec_ctx->channels = av_get_channel_layout_nb_channels(codec_ctx->channel_layout);
codec_ctx->sample_fmt = encoder->sample_fmts[0];
codec_ctx->sample_fmt = FFmpegUtils::GetFFmpegSampleFormat(params().audio_params().format());
codec_ctx->time_base = {1, codec_ctx->sample_rate};
if (params().audio_bit_rate() > 0) {
@@ -842,7 +842,7 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio, bool pla
return true;
}
const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c)
const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c, AudioParams::Format aformat)
{
switch (c) {
case ExportCodec::kCodecH264:
@@ -872,7 +872,24 @@ const AVCodec *FFmpegEncoder::GetEncoder(ExportCodec::Codec c)
case ExportCodec::kCodecAAC:
return avcodec_find_encoder(AV_CODEC_ID_AAC);
case ExportCodec::kCodecPCM:
return avcodec_find_encoder(AV_CODEC_ID_PCM_S16LE);
switch (aformat) {
case AudioParams::kFormatInvalid:
case AudioParams::kFormatCount:
break;
case AudioParams::kFormatUnsigned8:
return avcodec_find_encoder(AV_CODEC_ID_PCM_U8);
case AudioParams::kFormatSigned16:
return avcodec_find_encoder(AV_CODEC_ID_PCM_S16LE);
case AudioParams::kFormatSigned32:
return avcodec_find_encoder(AV_CODEC_ID_PCM_S32LE);
case AudioParams::kFormatSigned64:
return avcodec_find_encoder(AV_CODEC_ID_PCM_S64LE);
case AudioParams::kFormatFloat32:
return avcodec_find_encoder(AV_CODEC_ID_PCM_F32LE);
case AudioParams::kFormatFloat64:
return avcodec_find_encoder(AV_CODEC_ID_PCM_F64LE);
}
break;
case ExportCodec::kCodecFLAC:
return avcodec_find_encoder(AV_CODEC_ID_FLAC);
case ExportCodec::kCodecOpus:
+1 -1
View File
@@ -80,7 +80,7 @@ private:
bool InitializeResampleContext(const AudioParams &audio, bool planar);
static const AVCodec *GetEncoder(ExportCodec::Codec c);
static const AVCodec *GetEncoder(ExportCodec::Codec c, AudioParams::Format aformat);
AVFormatContext* fmt_ctx_;
+5
View File
@@ -122,6 +122,11 @@ void Config::SetDefaults()
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV);
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM);
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kInt, AudioParams::kFormatSigned16);
SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320);
SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0)));
SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(60)));
+2
View File
@@ -30,6 +30,8 @@
namespace olive {
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
class Config {
public:
static Config& Current();
+3 -2
View File
@@ -211,6 +211,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(Config::Current()[QStringLiteral("OnlinePixelFormat")].toInt()));
video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate());
audio_tab_->sample_format_combobox()->SetSampleFormat(ap.format());
audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout());
video_aspect_ratio_ = static_cast<double>(vp.width()) / static_cast<double>(vp.height());
@@ -515,7 +516,7 @@ ExportParams ExportDialog::GenerateParams() const
AudioParams audio_render_params(audio_tab_->sample_rate_combobox()->currentData().toInt(),
audio_tab_->channel_layout_combobox()->GetChannelLayout(),
AudioParams::kInternalFormat);
audio_tab_->sample_format_combobox()->GetSampleFormat());
ExportParams params;
params.set_encoder(Encoder::GetTypeFromFormat(format_combobox_->GetFormat()));
@@ -553,7 +554,7 @@ ExportParams ExportDialog::GenerateParams() const
}
if (audio_enabled_->isChecked()) {
ExportCodec::Codec audio_codec = static_cast<ExportCodec::Codec>(audio_tab_->codec_combobox()->currentData().toInt());
ExportCodec::Codec audio_codec = audio_tab_->GetCodec();
params.EnableAudio(audio_render_params, audio_codec);
params.set_audio_bit_rate(audio_tab_->bit_rate_slider()->GetValue() * 1000);
+6 -4
View File
@@ -59,7 +59,9 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) :
row++;
layout->addWidget(new QLabel(tr("Format:")), row, 0);
layout->addWidget(new QComboBox(), row, 1);
sample_format_combobox_ = new SampleFormatComboBox();
layout->addWidget(sample_format_combobox_, row, 1);
row++;
@@ -68,7 +70,7 @@ ExportAudioTab::ExportAudioTab(QWidget* parent) :
bit_rate_slider_ = new IntegerSlider();
bit_rate_slider_->SetMinimum(32);
bit_rate_slider_->SetMaximum(320);
bit_rate_slider_->SetValue(256);
bit_rate_slider_->SetValue(320);
bit_rate_slider_->SetFormat(tr("%1 kbps"));
layout->addWidget(bit_rate_slider_, row, 1);
@@ -79,9 +81,9 @@ int ExportAudioTab::SetFormat(ExportFormat::Format format)
{
QList<ExportCodec::Codec> acodecs = ExportFormat::GetAudioCodecs(format);
setEnabled(!acodecs.isEmpty());
codec_combobox()->clear();
codec_combobox_->clear();
foreach (ExportCodec::Codec acodec, acodecs) {
codec_combobox()->addItem(ExportCodec::GetCodecName(acodec), acodec);
codec_combobox_->addItem(ExportCodec::GetCodecName(acodec), acodec);
}
return acodecs.size();
}
+18 -2
View File
@@ -37,9 +37,19 @@ class ExportAudioTab : public QWidget
public:
ExportAudioTab(QWidget* parent = nullptr);
QComboBox* codec_combobox() const
ExportCodec::Codec GetCodec() const
{
return codec_combobox_;
return static_cast<ExportCodec::Codec>(codec_combobox_->currentData().toInt());
}
void SetCodec(ExportCodec::Codec c)
{
for (int i=0; i<codec_combobox_->count(); i++) {
if (codec_combobox_->itemData(i) == c) {
codec_combobox_->setCurrentIndex(i);
break;
}
}
}
SampleRateComboBox* sample_rate_combobox() const
@@ -47,6 +57,11 @@ public:
return sample_rate_combobox_;
}
SampleFormatComboBox* sample_format_combobox() const
{
return sample_format_combobox_;
}
ChannelLayoutComboBox* channel_layout_combobox() const
{
return channel_layout_combobox_;
@@ -64,6 +79,7 @@ private:
QComboBox* codec_combobox_;
SampleRateComboBox* sample_rate_combobox_;
ChannelLayoutComboBox* channel_layout_combobox_;
SampleFormatComboBox *sample_format_combobox_;
IntegerSlider* bit_rate_slider_;
};
+6 -2
View File
@@ -33,12 +33,16 @@ ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent) :
case kShowAllFormats:
break;
case kShowAudioOnly:
if (!ExportFormat::GetVideoCodecs(f).isEmpty()) {
if (!ExportFormat::GetVideoCodecs(f).isEmpty()
|| !ExportFormat::GetSubtitleCodecs(f).isEmpty()
|| ExportFormat::GetAudioCodecs(f).isEmpty()) {
continue;
}
break;
case kShowVideoOnly:
if (!ExportFormat::GetAudioCodecs(f).isEmpty()) {
if (ExportFormat::GetVideoCodecs(f).isEmpty()
|| !ExportFormat::GetSubtitleCodecs(f).isEmpty()
|| !ExportFormat::GetAudioCodecs(f).isEmpty()) {
continue;
}
break;
@@ -26,8 +26,6 @@
#include "audio/audiomanager.h"
#include "config/config.h"
#include "dialog/export/exportaudiotab.h"
#include "dialog/export/exportformatcombobox.h"
namespace olive {
@@ -99,14 +97,22 @@ PreferencesAudioTab::PreferencesAudioTab()
fmt_layout->addWidget(new QLabel(tr("Format:")));
ExportFormatComboBox *fmt_combo = new ExportFormatComboBox(ExportFormatComboBox::kShowAudioOnly);
fmt_combo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
fmt_layout->addWidget(fmt_combo);
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_);
ExportAudioTab *audio_recording_options = new ExportAudioTab();
recording_layout->addWidget(audio_recording_options);
record_options_ = new ExportAudioTab();
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(static_cast<AudioParams::Format>(OLIVE_CONFIG("AudioRecordingSampleFormat").toInt()));
recording_layout->addWidget(record_options_);
connect(fmt_combo, &ExportFormatComboBox::FormatChanged, audio_recording_options, &ExportAudioTab::SetFormat);
connect(record_format_combo_, &ExportFormatComboBox::FormatChanged, record_options_, &ExportAudioTab::SetFormat);
record_options_->SetFormat(record_format_combo_->GetFormat());
}
QHBoxLayout* refresh_layout = new QHBoxLayout();
@@ -134,12 +140,19 @@ void PreferencesAudioTab::Accept(MultiUndoCommand *command)
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
Config::Current()[QStringLiteral("AudioOutput")] = audio_output_devices_->currentText();
Config::Current()[QStringLiteral("AudioInput")] = audio_input_devices_->currentText();
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("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();
}
void PreferencesAudioTab::RefreshBackends()
@@ -25,6 +25,8 @@
#include <QPushButton>
#include "dialog/configbase/configdialogbase.h"
#include "dialog/export/exportaudiotab.h"
#include "dialog/export/exportformatcombobox.h"
namespace olive {
@@ -59,6 +61,10 @@ private:
*/
QPushButton* refresh_devices_btn_;
ExportFormatComboBox *record_format_combo_;
ExportAudioTab *record_options_;
private slots:
void RefreshBackends();
+30
View File
@@ -244,4 +244,34 @@ QString AudioParams::ChannelLayoutToString(const uint64_t &layout)
}
}
QString AudioParams::FormatToString(const Format &f)
{
switch (f) {
case kFormatUnsigned8:
return QCoreApplication::translate("AudioParams", "Unsigned 8-bit");
break;
case kFormatSigned16:
return QCoreApplication::translate("AudioParams", "Signed 16-bit");
break;
case kFormatSigned32:
return QCoreApplication::translate("AudioParams", "Signed 32-bit");
break;
case kFormatSigned64:
return QCoreApplication::translate("AudioParams", "Signed 64-bit");
break;
case kFormatFloat32:
return QCoreApplication::translate("AudioParams", "Float 32-bit");
break;
case kFormatFloat64:
return QCoreApplication::translate("AudioParams", "Float 64-bit");
break;
case kFormatInvalid:
case kFormatCount:
break;
}
return QCoreApplication::translate("AudioParams", "Unknown (0x%1)").arg(f, 1, 16);
}
}
+2
View File
@@ -200,6 +200,8 @@ public:
*/
static QString ChannelLayoutToString(const uint64_t &layout);
static QString FormatToString(const Format &f);
private:
void set_default_footage_parameters()
{
+1
View File
@@ -21,6 +21,7 @@ set(OLIVE_SOURCES
widget/standardcombos/interlacedcombobox.h
widget/standardcombos/pixelaspectratiocombobox.h
widget/standardcombos/pixelformatcombobox.h
widget/standardcombos/sampleformatcombobox.h
widget/standardcombos/sampleratecombobox.h
widget/standardcombos/standardcombos.h
widget/standardcombos/videodividercombobox.h
@@ -0,0 +1,64 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 Olive Team
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/>.
***/
#ifndef SAMPLEFORMATCOMBOBOX_H
#define SAMPLEFORMATCOMBOBOX_H
#include <QComboBox>
#include "render/audioparams.h"
namespace olive {
class SampleFormatComboBox : public QComboBox
{
Q_OBJECT
public:
SampleFormatComboBox(QWidget* parent = nullptr) :
QComboBox(parent)
{
// Set up preview formats
for (int i=0;i<AudioParams::kFormatCount;i++) {
AudioParams::Format smp_fmt = static_cast<AudioParams::Format>(i);
this->addItem(AudioParams::FormatToString(smp_fmt), smp_fmt);
}
}
AudioParams::Format GetSampleFormat() const
{
return static_cast<AudioParams::Format>(this->currentData().toInt());
}
void SetSampleFormat(AudioParams::Format fmt)
{
for (int i=0; i<this->count(); i++) {
if (this->itemData(i).toInt() == fmt) {
this->setCurrentIndex(i);
break;
}
}
}
};
}
#endif // SAMPLEFORMATCOMBOBOX_H
@@ -26,6 +26,7 @@
#include "interlacedcombobox.h"
#include "pixelaspectratiocombobox.h"
#include "pixelformatcombobox.h"
#include "sampleformatcombobox.h"
#include "sampleratecombobox.h"
#include "videodividercombobox.h"
+1 -1
View File
@@ -21,7 +21,7 @@ void RecordTool::MousePress(TimelineViewMouseEvent *event)
return;
}
if (t->type() != Track::kAudio) {
if (t && t->type() != Track::kAudio) {
// We only support audio tracks here
return;
}
+8 -1
View File
@@ -1183,7 +1183,14 @@ void ViewerWidget::Play(bool in_to_out_only)
ExportFormat::GetExtension(static_cast<ExportFormat::Format>(Config::Current()[QStringLiteral("AudioRecordingFormat")].toInt())))
);
if (AudioManager::instance()->StartRecording(recording_filename_, GetConnectedNode()->GetAudioParams())) {
AudioParams ap(OLIVE_CONFIG("AudioRecordingSampleRate").toInt(), OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong(), static_cast<AudioParams::Format>(OLIVE_CONFIG("AudioRecordingSampleFormat").toInt()));
EncodingParams encode_param;
encode_param.EnableAudio(ap, static_cast<ExportCodec::Codec>(OLIVE_CONFIG("AudioRecordingCodec").toInt()));
encode_param.SetFilename(recording_filename_);
encode_param.set_audio_bit_rate(OLIVE_CONFIG("AudioRecordingBitRate").toInt() * 1000);
if (AudioManager::instance()->StartRecording(encode_param)) {
recording_ = true;
controls_->SetPauseButtonRecordingState(true);
recording_callback_->EnableRecordingOverlay(TimelineCoordinate(recording_range_.in(), recording_track_));