6#include <QtMultimedia/private/qaudiobuffer_support_p.h>
7#include <QtFFmpegMediaPluginImpl/private/qffmpegframe_p.h>
8#include <QtFFmpegMediaPluginImpl/private/qffmpegresampler_p.h>
9#include <QtFFmpegMediaPluginImpl/private/qffmpegmediaformatinfo_p.h>
11#if defined(Q_CC_MSVC) && defined(QT_MM_OPTIMIZE_DEBUG)
12# pragma optimize("s", on)
16#include <signalsmith-stretch.h>
24qreal sampleRateFactor()
41 static const qreal result = []() {
42 const auto sampleRateFactorStr =
43 qEnvironmentVariable(
"QT_MEDIA_PLAYER_AUDIO_SAMPLE_RATE_FACTOR");
45 const auto result = sampleRateFactorStr.toDouble(&ok);
46 return ok ? result : 1.;
54 explicit TrivialAudioFrameConverter(
const Frame &frame, QAudioFormat outputFormat,
57 int sampleRate = qRound(outputFormat.sampleRate() / playbackRate * sampleRateFactor());
58 outputFormat.setSampleRate(sampleRate);
59 m_converter = createResampler(frame, outputFormat);
62 QAudioBuffer convert(AVFrame *frame)
override
67 return m_converter->resample(frame);
71 std::unique_ptr<QFFmpegResampler> m_converter;
76 explicit PitchShiftingAudioFrameConverter(
const Frame &frame, QAudioFormat outputFormat,
78 : m_playbackRate{ playbackRate }
80 const QAudioFormat mediaFormat = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(
81 *frame.codecContext()->stream()->codecpar);
83 const QAudioFormat floatFormat = [&] {
84 QAudioFormat ret = mediaFormat;
85 ret.setSampleFormat(QAudioFormat::SampleFormat::Float);
89 m_toPCMDecoder = createResampler(frame, floatFormat);
91 m_pendingFractionalFrames = 0.f;
92 m_stretcher.presetDefault(mediaFormat.channelCount(), outputFormat.sampleRate());
94 const QAudioFormat pitchCompensatedFormat = [&] {
95 QAudioFormat ret = floatFormat;
96 ret.setSampleRate(outputFormat.sampleRate());
99 m_toOutputFormatConverter = QFFmpegResampler::createFromInputFormat(pitchCompensatedFormat, outputFormat,
100 frame.startTime().get());
103 QAudioBuffer convert(AVFrame *frame)
override
105 if (!m_toPCMDecoder || !m_toOutputFormatConverter)
108 using namespace QtPrivate;
111 QAudioBuffer wordConverted = m_toPCMDecoder->resample(frame);
114 int mediaFrameCount = wordConverted.frameCount();
115 float expectedNumberOfFrames = mediaFrameCount / m_playbackRate + m_pendingFractionalFrames;
116 int numberOfFullExpectedFrames = qFloor(expectedNumberOfFrames);
117 m_pendingFractionalFrames = expectedNumberOfFrames - numberOfFullExpectedFrames;
119 auto timeStretcherOutput = QAudioBuffer{
120 numberOfFullExpectedFrames,
121 wordConverted.format(),
126 QAudioBufferDeinterleaveAdaptor<
const float>{
130 QAudioBufferDeinterleaveAdaptor<
float>{
133 numberOfFullExpectedFrames);
136 QAudioBuffer outputBuffer = m_toOutputFormatConverter->resample(
137 timeStretcherOutput.constData<
const char>(), timeStretcherOutput.byteCount());
143 std::unique_ptr<QFFmpegResampler> m_toPCMDecoder;
144 signalsmith::stretch::SignalsmithStretch<
float> m_stretcher;
145 std::unique_ptr<QFFmpegResampler> m_toOutputFormatConverter;
146 float m_playbackRate;
147 float m_pendingFractionalFrames = 0.f;
155 const QAudioFormat &outputFormat)
157 return QFFmpegResampler::createFromCodecContext(frame.codecContext(), outputFormat, frame.startTime().get());
163 return std::make_unique<TrivialAudioFrameConverter>(frame, outputFormat, playbackRate);
170 return std::make_unique<PitchShiftingAudioFrameConverter>(frame, outputFormat, playbackRate);
std::unique_ptr< QFFmpegResampler > createResampler(const Frame &frame, const QAudioFormat &outputFormat)
std::unique_ptr< AbstractAudioFrameConverter > makePitchShiftingAudioFrameConverter(const Frame &frame, QAudioFormat outputFormat, float playbackRate)
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
std::unique_ptr< AbstractAudioFrameConverter > makeTrivialAudioFrameConverter(const Frame &frame, QAudioFormat outputFormat, float playbackRate)
Combined button and popup list for selecting options.
virtual ~AbstractAudioFrameConverter()