simplified and optimized samplebuffer
This commit is contained in:
@@ -40,10 +40,10 @@ AudioVisualWaveform::AudioVisualWaveform() :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::OverwriteSamplesFromBuffer(SampleBufferPtr samples, int sample_rate, const rational &start, double target_rate, Sample& data, int &start_index, int &samples_length)
|
void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational &start, double target_rate, Sample& data, int &start_index, int &samples_length)
|
||||||
{
|
{
|
||||||
start_index = time_to_samples(start, target_rate);
|
start_index = time_to_samples(start, target_rate);
|
||||||
samples_length = time_to_samples(static_cast<double>(samples->sample_count()) / static_cast<double>(sample_rate), target_rate);
|
samples_length = time_to_samples(static_cast<double>(samples.sample_count()) / static_cast<double>(sample_rate), target_rate);
|
||||||
|
|
||||||
int end_index = start_index + samples_length;
|
int end_index = start_index + samples_length;
|
||||||
if (data.size() < end_index) {
|
if (data.size() < end_index) {
|
||||||
@@ -54,7 +54,7 @@ void AudioVisualWaveform::OverwriteSamplesFromBuffer(SampleBufferPtr samples, in
|
|||||||
|
|
||||||
for (int i=0; i<samples_length; i+=channels_) {
|
for (int i=0; i<samples_length; i+=channels_) {
|
||||||
int src_start = qRound((double(i) * chunk_size)) / channels_;
|
int src_start = qRound((double(i) * chunk_size)) / channels_;
|
||||||
int src_end = qMin(qRound((double(i + channels_) * chunk_size)) / channels_, samples->sample_count());
|
int src_end = qMin(qRound((double(i + channels_) * chunk_size)) / channels_, samples.sample_count());
|
||||||
|
|
||||||
Sample summary = SumSamples(samples,
|
Sample summary = SumSamples(samples,
|
||||||
src_start,
|
src_start,
|
||||||
@@ -91,7 +91,7 @@ void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::
|
|||||||
input_length = samples_length;
|
input_length = samples_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVisualWaveform::OverwriteSamples(SampleBufferPtr samples, int sample_rate, const rational &start)
|
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational &start)
|
||||||
{
|
{
|
||||||
if (!channels_) {
|
if (!channels_) {
|
||||||
qWarning() << "Failed to write samples - channel count is zero";
|
qWarning() << "Failed to write samples - channel count is zero";
|
||||||
@@ -125,7 +125,7 @@ void AudioVisualWaveform::OverwriteSamples(SampleBufferPtr samples, int sample_r
|
|||||||
current_mipmap->second);
|
current_mipmap->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
rational sample_length(samples->sample_count(), sample_rate);
|
rational sample_length(samples.sample_count(), sample_rate);
|
||||||
length_ = qMax(length_, start + sample_length);
|
length_ = qMax(length_, start + sample_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const ration
|
|||||||
return AudioVisualWaveform::Sample(channel_count(), {0, 0});
|
return AudioVisualWaveform::Sample(channel_count(), {0, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpandMinMaxChannel(float *a, int start, int length, float &min_val, float &max_val)
|
void ExpandMinMaxChannel(const float *a, int start, int length, float &min_val, float &max_val)
|
||||||
{
|
{
|
||||||
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
||||||
// SSE optimized
|
// SSE optimized
|
||||||
@@ -321,13 +321,13 @@ void ExpandMinMaxChannel(float *a, int start, int length, float &min_val, float
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(SampleBufferPtr samples, int start_index, int length)
|
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &samples, int start_index, int length)
|
||||||
{
|
{
|
||||||
int channels = samples->audio_params().channel_count();
|
int channels = samples.audio_params().channel_count();
|
||||||
AudioVisualWaveform::Sample summed_samples(channels);
|
AudioVisualWaveform::Sample summed_samples(channels);
|
||||||
|
|
||||||
for (int channel=0; channel<samples->audio_params().channel_count(); channel++) {
|
for (int channel=0; channel<samples.audio_params().channel_count(); channel++) {
|
||||||
ExpandMinMaxChannel(samples->data(channel), start_index, length, summed_samples[channel].min, summed_samples[channel].max);
|
ExpandMinMaxChannel(samples.data(channel), start_index, length, summed_samples[channel].min, summed_samples[channel].max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
|
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public:
|
|||||||
*
|
*
|
||||||
* Starting at `start`, writes samples over anything in the buffer, expanding it if necessary.
|
* Starting at `start`, writes samples over anything in the buffer, expanding it if necessary.
|
||||||
*/
|
*/
|
||||||
void OverwriteSamples(SampleBufferPtr samples, int sample_rate, const rational& start = 0);
|
void OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational& start = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Replaces sums at a certain range in this visual waveform
|
* @brief Replaces sums at a certain range in this visual waveform
|
||||||
@@ -98,7 +98,7 @@ public:
|
|||||||
|
|
||||||
Sample GetSummaryFromTime(const rational& start, const rational& length) const;
|
Sample GetSummaryFromTime(const rational& start, const rational& length) const;
|
||||||
|
|
||||||
static Sample SumSamples(SampleBufferPtr samples, int start_index, int length);
|
static Sample SumSamples(const SampleBuffer &samples, int start_index, int length);
|
||||||
|
|
||||||
static Sample ReSumSamples(const SamplePerChannel *samples, int nb_samples, int nb_channels);
|
static Sample ReSumSamples(const SamplePerChannel *samples, int nb_samples, int nb_channels);
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ public:
|
|||||||
static const rational kMaximumSampleRate;
|
static const rational kMaximumSampleRate;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OverwriteSamplesFromBuffer(SampleBufferPtr samples, int sample_rate, const rational& start, double target_rate, Sample &data, int &start_index, int &samples_length);
|
void OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational& start, double target_rate, Sample &data, int &start_index, int &samples_length);
|
||||||
|
|
||||||
void OverwriteSamplesFromMipmap(const Sample& input, double input_sample_rate, int &input_start, int &input_length, const rational& start, double output_rate, Sample &output_data);
|
void OverwriteSamplesFromMipmap(const Sample& input, double input_sample_rate, int &input_start, int &input_length, const rational& start, double output_rate, Sample &output_data);
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ FramePtr Decoder::RetrieveVideo(const rational &timecode, const RetrieveVideoPar
|
|||||||
return RetrieveVideoInternal(timecode, divider, cancelled);
|
return RetrieveVideoInternal(timecode, divider, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBufferPtr dest, const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode)
|
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex_);
|
QMutexLocker locker(&mutex_);
|
||||||
|
|
||||||
@@ -272,14 +272,14 @@ bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const Audi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Decoder::RetrieveAudioFromConform(SampleBufferPtr sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
|
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
|
||||||
{
|
{
|
||||||
PlanarFileDevice input;
|
PlanarFileDevice input;
|
||||||
if (input.open(conform_filenames, QFile::ReadOnly)) {
|
if (input.open(conform_filenames, QFile::ReadOnly)) {
|
||||||
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
|
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
|
||||||
qint64 write_index = 0;
|
qint64 write_index = 0;
|
||||||
|
|
||||||
const qint64 buffer_length_in_bytes = sample_buffer->sample_count() * input_params.bytes_per_sample_per_channel();
|
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel();
|
||||||
|
|
||||||
while (write_index < buffer_length_in_bytes) {
|
while (write_index < buffer_length_in_bytes) {
|
||||||
if (loop_mode == Footage::kLoopModeLoop) {
|
if (loop_mode == Footage::kLoopModeLoop) {
|
||||||
@@ -297,15 +297,15 @@ bool Decoder::RetrieveAudioFromConform(SampleBufferPtr sample_buffer, const QVec
|
|||||||
if (read_index < 0) {
|
if (read_index < 0) {
|
||||||
// Reading before 0, write silence here until audio data would actually start
|
// Reading before 0, write silence here until audio data would actually start
|
||||||
write_count = qMin(-read_index, buffer_length_in_bytes);
|
write_count = qMin(-read_index, buffer_length_in_bytes);
|
||||||
sample_buffer->silence_bytes(write_index, write_index + write_count);
|
sample_buffer.silence_bytes(write_index, write_index + write_count);
|
||||||
} else if (read_index >= input.size()) {
|
} else if (read_index >= input.size()) {
|
||||||
// Reading after data length, write silence until the end of the buffer
|
// Reading after data length, write silence until the end of the buffer
|
||||||
write_count = buffer_length_in_bytes - write_index;
|
write_count = buffer_length_in_bytes - write_index;
|
||||||
sample_buffer->silence_bytes(write_index, write_index + write_count);
|
sample_buffer.silence_bytes(write_index, write_index + write_count);
|
||||||
} else {
|
} else {
|
||||||
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
|
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
|
||||||
input.seek(read_index);
|
input.seek(read_index);
|
||||||
input.read(reinterpret_cast<char**>(sample_buffer->to_raw_ptrs()), write_count, write_index);
|
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
read_index += write_count;
|
read_index += write_count;
|
||||||
|
|||||||
+2
-2
@@ -201,7 +201,7 @@ public:
|
|||||||
*
|
*
|
||||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||||
*/
|
*/
|
||||||
RetrieveAudioStatus RetrieveAudio(SampleBufferPtr dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode);
|
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Determine the last time this decoder instance was used in any way
|
* @brief Determine the last time this decoder instance was used in any way
|
||||||
@@ -307,7 +307,7 @@ signals:
|
|||||||
private:
|
private:
|
||||||
void UpdateLastAccessed();
|
void UpdateLastAccessed();
|
||||||
|
|
||||||
bool RetrieveAudioFromConform(SampleBufferPtr sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams ¶ms);
|
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams ¶ms);
|
||||||
|
|
||||||
CodecStream stream_;
|
CodecStream stream_;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -183,7 +183,7 @@ public slots:
|
|||||||
virtual bool Open() = 0;
|
virtual bool Open() = 0;
|
||||||
|
|
||||||
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) = 0;
|
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) = 0;
|
||||||
virtual bool WriteAudio(olive::SampleBufferPtr audio) = 0;
|
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
|
||||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
|
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
|
||||||
|
|
||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
|
|||||||
@@ -515,7 +515,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
|||||||
|
|
||||||
// Resample audio to our destination parameters
|
// Resample audio to our destination parameters
|
||||||
nb_samples = swr_convert(resampler,
|
nb_samples = swr_convert(resampler,
|
||||||
reinterpret_cast<uint8_t**>(data.to_raw_ptrs()),
|
reinterpret_cast<uint8_t**>(data.to_raw_ptrs().data()),
|
||||||
nb_samples,
|
nb_samples,
|
||||||
const_cast<const uint8_t**>(frame->data),
|
const_cast<const uint8_t**>(frame->data),
|
||||||
frame->nb_samples);
|
frame->nb_samples);
|
||||||
@@ -526,7 +526,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
|
|||||||
nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels;
|
nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels;
|
||||||
|
|
||||||
// Write to files
|
// Write to files
|
||||||
wave_out.write(const_cast<const char**>(reinterpret_cast<char**>(data.to_raw_ptrs())), nb_bytes_per_channel);
|
wave_out.write(const_cast<const char**>(reinterpret_cast<char**>(data.to_raw_ptrs().data())), nb_bytes_per_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free buffer
|
// Free buffer
|
||||||
|
|||||||
@@ -266,26 +266,26 @@ fail:
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio)
|
bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
|
||||||
{
|
{
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
// Create input buffer
|
// Create input buffer
|
||||||
int input_sample_count = 0;
|
int input_sample_count = 0;
|
||||||
uint8_t** input_data = nullptr;
|
uint8_t** input_data = nullptr;
|
||||||
if (audio) {
|
if (audio.is_allocated()) {
|
||||||
input_sample_count = audio->sample_count();
|
input_sample_count = audio.sample_count();
|
||||||
int input_linesize;
|
int input_linesize;
|
||||||
|
|
||||||
av_samples_alloc_array_and_samples(&input_data, &input_linesize, audio->audio_params().channel_count(),
|
av_samples_alloc_array_and_samples(&input_data, &input_linesize, audio.audio_params().channel_count(),
|
||||||
input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio->audio_params().format()), 0);
|
input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio.audio_params().format()), 0);
|
||||||
|
|
||||||
for (int i=0; i<audio->audio_params().channel_count(); i++) {
|
for (int i=0; i<audio.audio_params().channel_count(); i++) {
|
||||||
memcpy(input_data[i], audio->data(i), input_sample_count * audio->audio_params().bytes_per_sample_per_channel());
|
memcpy(input_data[i], audio.data(i), input_sample_count * audio.audio_params().bytes_per_sample_per_channel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = WriteAudioData(audio ? audio->audio_params() : params().audio_params(), const_cast<const uint8_t**>(input_data), input_sample_count);
|
result = WriteAudioData(audio.audio_params().is_valid() ? audio.audio_params() : params().audio_params(), const_cast<const uint8_t**>(input_data), input_sample_count);
|
||||||
|
|
||||||
if (input_data) {
|
if (input_data) {
|
||||||
av_freep(&input_data[0]);
|
av_freep(&input_data[0]);
|
||||||
@@ -774,7 +774,7 @@ void FFmpegEncoder::FlushEncoders()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (audio_codec_ctx_) {
|
if (audio_codec_ctx_) {
|
||||||
WriteAudio(nullptr);
|
WriteAudio(SampleBuffer());
|
||||||
|
|
||||||
FlushCodecCtx(audio_codec_ctx_, audio_stream_);
|
FlushCodecCtx(audio_codec_ctx_, audio_stream_);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public:
|
|||||||
|
|
||||||
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
|
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
|
||||||
|
|
||||||
virtual bool WriteAudio(olive::SampleBufferPtr audio) override;
|
virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
|
||||||
|
|
||||||
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data, int input_sample_count);
|
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data, int input_sample_count);
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OIIOEncoder::WriteAudio(SampleBufferPtr audio)
|
bool OIIOEncoder::WriteAudio(const SampleBuffer &audio)
|
||||||
{
|
{
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public slots:
|
|||||||
virtual bool Open() override;
|
virtual bool Open() override;
|
||||||
|
|
||||||
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
|
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
|
||||||
virtual bool WriteAudio(SampleBufferPtr audio) override;
|
virtual bool WriteAudio(const SampleBuffer &audio) override;
|
||||||
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
|
||||||
|
|
||||||
virtual void Close() override;
|
virtual void Close() override;
|
||||||
|
|||||||
+28
-33
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "samplebuffer.h"
|
#include "samplebuffer.h"
|
||||||
|
|
||||||
|
#include "common/cpuoptimize.h"
|
||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
SampleBuffer::SampleBuffer() :
|
SampleBuffer::SampleBuffer() :
|
||||||
@@ -27,25 +29,18 @@ SampleBuffer::SampleBuffer() :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleBufferPtr SampleBuffer::Create()
|
SampleBuffer::SampleBuffer(const AudioParams &audio_params, const rational &length) :
|
||||||
|
audio_params_(audio_params)
|
||||||
{
|
{
|
||||||
return std::make_shared<SampleBuffer>();
|
sample_count_per_channel_ = audio_params_.time_to_samples(length);
|
||||||
|
allocate();
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleBufferPtr SampleBuffer::CreateAllocated(const AudioParams &audio_params, const rational &length)
|
SampleBuffer::SampleBuffer(const AudioParams &audio_params, int samples_per_channel) :
|
||||||
|
audio_params_(audio_params),
|
||||||
|
sample_count_per_channel_(samples_per_channel)
|
||||||
{
|
{
|
||||||
return CreateAllocated(audio_params, audio_params.time_to_samples(length));
|
allocate();
|
||||||
}
|
|
||||||
|
|
||||||
SampleBufferPtr SampleBuffer::CreateAllocated(const AudioParams &audio_params, int samples_per_channel)
|
|
||||||
{
|
|
||||||
SampleBufferPtr buffer = Create();
|
|
||||||
|
|
||||||
buffer->set_audio_params(audio_params);
|
|
||||||
buffer->set_sample_count(samples_per_channel);
|
|
||||||
buffer->allocate();
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AudioParams &SampleBuffer::audio_params() const
|
const AudioParams &SampleBuffer::audio_params() const
|
||||||
@@ -104,14 +99,11 @@ void SampleBuffer::allocate()
|
|||||||
for (int i=0; i<audio_params_.channel_count(); i++) {
|
for (int i=0; i<audio_params_.channel_count(); i++) {
|
||||||
data_[i].resize(sample_count_per_channel_);
|
data_[i].resize(sample_count_per_channel_);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_raw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleBuffer::destroy()
|
void SampleBuffer::destroy()
|
||||||
{
|
{
|
||||||
data_.clear();
|
data_.clear();
|
||||||
raw_ptrs_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleBuffer::reverse()
|
void SampleBuffer::reverse()
|
||||||
@@ -157,29 +149,40 @@ void SampleBuffer::speed(double speed)
|
|||||||
}
|
}
|
||||||
|
|
||||||
data_ = output_data;
|
data_ = output_data;
|
||||||
update_raw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleBuffer::transform_volume(float f)
|
void SampleBuffer::transform_volume(float f)
|
||||||
{
|
{
|
||||||
for (int i=0;i<audio_params().channel_count();i++) {
|
for (int i=0;i<audio_params().channel_count();i++) {
|
||||||
for (int j=0;j<sample_count_per_channel_;j++) {
|
transform_volume_for_channel(i, f);
|
||||||
data_[i][j] *= f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleBuffer::transform_volume_for_channel(int channel, float volume)
|
void SampleBuffer::transform_volume_for_channel(int channel, float volume)
|
||||||
{
|
{
|
||||||
for (int i=0;i<sample_count_per_channel_;i++) {
|
float *cdat = data_[channel].data();
|
||||||
data_[channel][i] *= volume;
|
int unopt_start = 0;
|
||||||
|
|
||||||
|
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
||||||
|
__m128 mult = _mm_load1_ps(&volume);
|
||||||
|
unopt_start = (sample_count_per_channel_ / 4) * 4;
|
||||||
|
for (int j=0; j<unopt_start; j+=4) {
|
||||||
|
float *here = cdat + j;
|
||||||
|
__m128 samples = _mm_loadu_ps(here);
|
||||||
|
__m128 multiplied = _mm_mul_ps(samples, mult);
|
||||||
|
_mm_storeu_ps(here, multiplied);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int j=unopt_start; j<sample_count_per_channel_; j++) {
|
||||||
|
cdat[j] *= volume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleBuffer::transform_volume_for_sample(int sample_index, float volume)
|
void SampleBuffer::transform_volume_for_sample(int sample_index, float volume)
|
||||||
{
|
{
|
||||||
for (int i=0;i<audio_params().channel_count();i++) {
|
for (int i=0;i<audio_params().channel_count();i++) {
|
||||||
data_[i][sample_index] *= volume;
|
transform_volume_for_sample_on_channel(sample_index, i, volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,12 +223,4 @@ void SampleBuffer::set(int channel, const float *data, int sample_offset, int sa
|
|||||||
memcpy(&data_[channel].data()[sample_offset], data, sizeof(float) * sample_length);
|
memcpy(&data_[channel].data()[sample_offset], data, sizeof(float) * sample_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampleBuffer::update_raw()
|
|
||||||
{
|
|
||||||
raw_ptrs_.resize(data_.size());
|
|
||||||
for (int i=0; i<raw_ptrs_.size(); i++) {
|
|
||||||
raw_ptrs_[i] = data_[i].data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,6 @@
|
|||||||
|
|
||||||
namespace olive {
|
namespace olive {
|
||||||
|
|
||||||
class SampleBuffer;
|
|
||||||
using SampleBufferPtr = std::shared_ptr<SampleBuffer>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A buffer of audio samples
|
* @brief A buffer of audio samples
|
||||||
*
|
*
|
||||||
@@ -42,12 +39,8 @@ class SampleBuffer
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SampleBuffer();
|
SampleBuffer();
|
||||||
|
SampleBuffer(const AudioParams& audio_params, const rational& length);
|
||||||
static SampleBufferPtr Create();
|
SampleBuffer(const AudioParams& audio_params, int samples_per_channel);
|
||||||
static SampleBufferPtr CreateAllocated(const AudioParams& audio_params, const rational& length);
|
|
||||||
static SampleBufferPtr CreateAllocated(const AudioParams& audio_params, int samples_per_channel);
|
|
||||||
|
|
||||||
DISABLE_COPY_MOVE(SampleBuffer)
|
|
||||||
|
|
||||||
const AudioParams& audio_params() const;
|
const AudioParams& audio_params() const;
|
||||||
void set_audio_params(const AudioParams& params);
|
void set_audio_params(const AudioParams& params);
|
||||||
@@ -69,9 +62,13 @@ public:
|
|||||||
return data_.at(channel).constData();
|
return data_.at(channel).constData();
|
||||||
}
|
}
|
||||||
|
|
||||||
float **to_raw_ptrs()
|
QVector<float *> to_raw_ptrs()
|
||||||
{
|
{
|
||||||
return raw_ptrs_.data();
|
QVector<float *> r(data_.size());
|
||||||
|
for (int i=0; i<r.size(); i++) {
|
||||||
|
r[i] = data_[i].data();
|
||||||
|
}
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_allocated() const;
|
bool is_allocated() const;
|
||||||
@@ -96,19 +93,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void update_raw();
|
|
||||||
|
|
||||||
AudioParams audio_params_;
|
AudioParams audio_params_;
|
||||||
|
|
||||||
int sample_count_per_channel_;
|
int sample_count_per_channel_;
|
||||||
|
|
||||||
QVector< QVector<float> > data_;
|
QVector< QVector<float> > data_;
|
||||||
QVector<float*> raw_ptrs_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(olive::SampleBufferPtr)
|
Q_DECLARE_METATYPE(olive::SampleBuffer)
|
||||||
|
|
||||||
#endif // SAMPLEBUFFER_H
|
#endif // SAMPLEBUFFER_H
|
||||||
|
|||||||
+1
-1
@@ -108,7 +108,7 @@ void Core::DeclareTypesForQt()
|
|||||||
qRegisterMetaType<NodeValueTable>();
|
qRegisterMetaType<NodeValueTable>();
|
||||||
qRegisterMetaType<NodeValueDatabase>();
|
qRegisterMetaType<NodeValueDatabase>();
|
||||||
qRegisterMetaType<FramePtr>();
|
qRegisterMetaType<FramePtr>();
|
||||||
qRegisterMetaType<SampleBufferPtr>();
|
qRegisterMetaType<SampleBuffer>();
|
||||||
qRegisterMetaType<AudioParams>();
|
qRegisterMetaType<AudioParams>();
|
||||||
qRegisterMetaType<NodeKeyframe::Type>();
|
qRegisterMetaType<NodeKeyframe::Type>();
|
||||||
qRegisterMetaType<Decoder::RetrieveState>();
|
qRegisterMetaType<Decoder::RetrieveState>();
|
||||||
|
|||||||
+18
-15
@@ -67,45 +67,48 @@ void PanNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
|||||||
Q_UNUSED(globals)
|
Q_UNUSED(globals)
|
||||||
|
|
||||||
// Create a sample job
|
// Create a sample job
|
||||||
SampleJob job(kSamplesInput, value);
|
SampleBuffer samples = value[kSamplesInput].toSamples();
|
||||||
|
if (samples.is_allocated()) {
|
||||||
if (job.HasSamples()) {
|
bool pushed_job = false;
|
||||||
bool push_job = false;
|
|
||||||
|
|
||||||
// This node is only compatible with stereo audio
|
// This node is only compatible with stereo audio
|
||||||
if (job.samples()->audio_params().channel_count() == 2) {
|
if (samples.audio_params().channel_count() == 2) {
|
||||||
// If the input is static, we can just do it now which will be faster
|
// If the input is static, we can just do it now which will be faster
|
||||||
if (IsInputStatic(kPanningInput)) {
|
if (IsInputStatic(kPanningInput)) {
|
||||||
float pan_volume = job.GetValue(kPanningInput).toDouble();
|
float pan_volume = value[kPanningInput].toDouble();
|
||||||
if (!qIsNull(pan_volume)) {
|
if (!qIsNull(pan_volume)) {
|
||||||
if (pan_volume > 0) {
|
if (pan_volume > 0) {
|
||||||
job.samples()->transform_volume_for_channel(0, 1.0f - pan_volume);
|
samples.transform_volume_for_channel(0, 1.0f - pan_volume);
|
||||||
} else {
|
} else {
|
||||||
job.samples()->transform_volume_for_channel(1, 1.0f + pan_volume);
|
samples.transform_volume_for_channel(1, 1.0f + pan_volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Requires job
|
// Requires job
|
||||||
push_job = true;
|
|
||||||
|
pushed_job = true;
|
||||||
|
table->Push(NodeValue::kSamples, SampleJob(kSamplesInput, value), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table->Push(NodeValue::kSamples, push_job ? QVariant::fromValue(job) : QVariant::fromValue(job.samples()), this);
|
if (!pushed_job) {
|
||||||
|
table->Push(value[kSamplesInput]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanNode::ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
void PanNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const
|
||||||
{
|
{
|
||||||
float pan_val = values[kPanningInput].toDouble();
|
float pan_val = values[kPanningInput].toDouble();
|
||||||
|
|
||||||
for (int i=0;i<input->audio_params().channel_count();i++) {
|
for (int i=0;i<input.audio_params().channel_count();i++) {
|
||||||
output->data(i)[index] = input->data(i)[index];
|
output.data(i)[index] = input.data(i)[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pan_val > 0) {
|
if (pan_val > 0) {
|
||||||
output->data(0)[index] *= (1.0F - pan_val);
|
output.data(0)[index] *= (1.0F - pan_val);
|
||||||
} else if (pan_val < 0) {
|
} else if (pan_val < 0) {
|
||||||
output->data(1)[index] *= (1.0F - qAbs(pan_val));
|
output.data(1)[index] *= (1.0F - qAbs(pan_val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
|
|
||||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||||
|
|
||||||
virtual void ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override;
|
||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ void VolumeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, No
|
|||||||
table);
|
table);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeNode::ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
void VolumeNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const
|
||||||
{
|
{
|
||||||
return ProcessSamplesInternal(values, kOpMultiply, kSamplesInput, kVolumeInput, input, output, index);
|
return ProcessSamplesInternal(values, kOpMultiply, kSamplesInput, kVolumeInput, input, output, index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
|
|
||||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||||
|
|
||||||
virtual void ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override;
|
||||||
|
|
||||||
virtual void Retranslate() override;
|
virtual void Retranslate() override;
|
||||||
|
|
||||||
|
|||||||
@@ -60,27 +60,27 @@ void CrossDissolveTransition::ShaderJobEvent(const NodeValueRow &value, ShaderJo
|
|||||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrossDissolveTransition::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
|
void CrossDissolveTransition::SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const
|
||||||
{
|
{
|
||||||
for (int i=0; i<out_samples->sample_count(); i++) {
|
for (int i=0; i<out_samples.sample_count(); i++) {
|
||||||
double this_sample_time = out_samples->audio_params().samples_to_time(i).toDouble() + time_in;
|
double this_sample_time = out_samples.audio_params().samples_to_time(i).toDouble() + time_in;
|
||||||
double progress = GetTotalProgress(this_sample_time);
|
double progress = GetTotalProgress(this_sample_time);
|
||||||
|
|
||||||
for (int j=0; j<out_samples->audio_params().channel_count(); j++) {
|
for (int j=0; j<out_samples.audio_params().channel_count(); j++) {
|
||||||
out_samples->data(j)[i] = 0;
|
out_samples.data(j)[i] = 0;
|
||||||
|
|
||||||
if (from_samples) {
|
if (from_samples.is_allocated()) {
|
||||||
if (i < from_samples->sample_count()) {
|
if (i < from_samples.sample_count()) {
|
||||||
out_samples->data(j)[i] += from_samples->data(j)[i] * TransformCurve(1.0 - progress);
|
out_samples.data(j)[i] += from_samples.data(j)[i] * TransformCurve(1.0 - progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to_samples) {
|
if (to_samples.is_allocated()) {
|
||||||
// Offset input samples from the end
|
// Offset input samples from the end
|
||||||
int in_index = i - (out_samples->sample_count() - to_samples->sample_count());
|
int in_index = i - (out_samples.sample_count() - to_samples.sample_count());
|
||||||
|
|
||||||
if (in_index >= 0) {
|
if (in_index >= 0) {
|
||||||
out_samples->data(j)[i] += to_samples->data(j)[in_index] * TransformCurve(progress);
|
out_samples.data(j)[i] += to_samples.data(j)[in_index] * TransformCurve(progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const override;
|
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const override;
|
||||||
|
|
||||||
virtual void SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const override;
|
virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -206,25 +206,22 @@ void TransitionBlock::Value(const NodeValueRow &value, const NodeGlobals &global
|
|||||||
push_job = QVariant::fromValue(job);
|
push_job = QVariant::fromValue(job);
|
||||||
} else if (data_type == NodeValue::kSamples) {
|
} else if (data_type == NodeValue::kSamples) {
|
||||||
// This must be an audio transition
|
// This must be an audio transition
|
||||||
SampleBufferPtr from_samples = out_buffer.toSamples();
|
SampleBuffer from_samples = out_buffer.toSamples();
|
||||||
SampleBufferPtr to_samples = in_buffer.toSamples();
|
SampleBuffer to_samples = in_buffer.toSamples();
|
||||||
|
|
||||||
if (from_samples || to_samples) {
|
if (from_samples.is_allocated() || to_samples.is_allocated()) {
|
||||||
double time_in = globals.time().in().toDouble();
|
double time_in = globals.time().in().toDouble();
|
||||||
double time_out = globals.time().out().toDouble();
|
double time_out = globals.time().out().toDouble();
|
||||||
|
|
||||||
const AudioParams& params = (from_samples) ? from_samples->audio_params() : to_samples->audio_params();
|
const AudioParams& params = (from_samples.is_allocated()) ? from_samples.audio_params() : to_samples.audio_params();
|
||||||
|
|
||||||
SampleBufferPtr out_samples;
|
SampleBuffer out_samples;
|
||||||
|
|
||||||
if (params.is_valid()) {
|
if (params.is_valid()) {
|
||||||
int nb_samples = params.time_to_samples(time_out - time_in);
|
int nb_samples = params.time_to_samples(time_out - time_in);
|
||||||
|
|
||||||
out_samples = SampleBuffer::CreateAllocated(params, nb_samples);
|
out_samples = SampleBuffer(params, nb_samples);
|
||||||
SampleJobEvent(from_samples, to_samples, out_samples, time_in);
|
SampleJobEvent(from_samples, to_samples, out_samples, time_in);
|
||||||
} else {
|
|
||||||
// Create dummy sample buffer
|
|
||||||
out_samples = SampleBuffer::Create();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
job_type = NodeValue::kSamples;
|
job_type = NodeValue::kSamples;
|
||||||
@@ -251,20 +248,6 @@ void TransitionBlock::InvalidateCache(const TimeRange &range, const QString &fro
|
|||||||
super::InvalidateCache(r, from, element, options);
|
super::InvalidateCache(r, from, element, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransitionBlock::ShaderJobEvent(const NodeValueRow &value, ShaderJob &job) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(value)
|
|
||||||
Q_UNUSED(job)
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitionBlock::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(from_samples)
|
|
||||||
Q_UNUSED(to_samples)
|
|
||||||
Q_UNUSED(out_samples)
|
|
||||||
Q_UNUSED(time_in)
|
|
||||||
}
|
|
||||||
|
|
||||||
double TransitionBlock::TransformCurve(double linear) const
|
double TransitionBlock::TransformCurve(double linear) const
|
||||||
{
|
{
|
||||||
switch (static_cast<CurveType>(GetStandardValue(kCurveInput).toInt())) {
|
switch (static_cast<CurveType>(GetStandardValue(kCurveInput).toInt())) {
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ public:
|
|||||||
static const QString kCenterInput;
|
static const QString kCenterInput;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const;
|
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const {}
|
||||||
|
|
||||||
virtual void SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const;
|
virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const {}
|
||||||
|
|
||||||
double TransformCurve(double linear) const;
|
double TransformCurve(double linear) const;
|
||||||
|
|
||||||
|
|||||||
@@ -72,14 +72,8 @@ void HashTraverser::ProcessVideoFootage(TexturePtr destination, const FootageJob
|
|||||||
texture_ids_.insert(destination.get(), hash_.result());
|
texture_ids_.insert(destination.get(), hash_.result());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashTraverser::ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time)
|
void HashTraverser::ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time)
|
||||||
{
|
{
|
||||||
Hash(FileFunctions::GetUniqueFileIdentifier(stream.filename()));
|
|
||||||
Hash(stream.loop_mode());
|
|
||||||
Hash(stream.audio_params().stream_index());
|
|
||||||
Hash(input_time);
|
|
||||||
|
|
||||||
texture_ids_.insert(destination.get(), hash_.result());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashTraverser::ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob &job)
|
void HashTraverser::ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob &job)
|
||||||
@@ -104,9 +98,8 @@ void HashTraverser::ProcessColorTransform(TexturePtr destination, const Node *no
|
|||||||
texture_ids_.insert(destination.get(), hash_.result());
|
texture_ids_.insert(destination.get(), hash_.result());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashTraverser::ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
void HashTraverser::ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
||||||
{
|
{
|
||||||
texture_ids_.insert(destination.get(), hash_.result());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashTraverser::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
|
void HashTraverser::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
|
||||||
@@ -146,9 +139,6 @@ void HashTraverser::HashNodeValue(const NodeValue &value)
|
|||||||
if (value_type == NodeValue::kTexture) {
|
if (value_type == NodeValue::kTexture) {
|
||||||
TexturePtr texture = value.toTexture();
|
TexturePtr texture = value.toTexture();
|
||||||
id_for_buffer = texture_ids_.value(texture.get());
|
id_for_buffer = texture_ids_.value(texture.get());
|
||||||
} else {
|
|
||||||
SampleBufferPtr samples = value.toSamples();
|
|
||||||
id_for_buffer = texture_ids_.value(samples.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!id_for_buffer.isEmpty()) {
|
if (!id_for_buffer.isEmpty()) {
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
||||||
|
|
||||||
virtual void ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time) override;
|
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time) override;
|
||||||
|
|
||||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||||
|
|
||||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
||||||
|
|
||||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||||
|
|
||||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
|
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ void MathNode::Value(const NodeValueRow &value, const NodeGlobals &globals, Node
|
|||||||
table);
|
table);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MathNode::ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
void MathNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const
|
||||||
{
|
{
|
||||||
return ProcessSamplesInternal(values, GetOperation(), kParamAIn, kParamBIn, input, output, index);
|
return ProcessSamplesInternal(values, GetOperation(), kParamAIn, kParamBIn, input, output, index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public:
|
|||||||
|
|
||||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||||
|
|
||||||
virtual void ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
|
virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override;
|
||||||
|
|
||||||
static const QString kMethodIn;
|
static const QString kMethodIn;
|
||||||
static const QString kParamAIn;
|
static const QString kParamAIn;
|
||||||
|
|||||||
@@ -298,18 +298,18 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
|||||||
|
|
||||||
case kPairSampleSample:
|
case kPairSampleSample:
|
||||||
{
|
{
|
||||||
SampleBufferPtr samples_a = val_a.toSamples();
|
SampleBuffer samples_a = val_a.toSamples();
|
||||||
SampleBufferPtr samples_b = val_b.toSamples();
|
SampleBuffer samples_b = val_b.toSamples();
|
||||||
|
|
||||||
int max_samples = qMax(samples_a->sample_count(), samples_b->sample_count());
|
int max_samples = qMax(samples_a.sample_count(), samples_b.sample_count());
|
||||||
int min_samples = qMin(samples_a->sample_count(), samples_b->sample_count());
|
int min_samples = qMin(samples_a.sample_count(), samples_b.sample_count());
|
||||||
|
|
||||||
SampleBufferPtr mixed_samples = SampleBuffer::CreateAllocated(samples_a->audio_params(), max_samples);
|
SampleBuffer mixed_samples = SampleBuffer(samples_a.audio_params(), max_samples);
|
||||||
|
|
||||||
for (int i=0;i<mixed_samples->audio_params().channel_count();i++) {
|
for (int i=0;i<mixed_samples.audio_params().channel_count();i++) {
|
||||||
// Mix samples that are in both buffers
|
// Mix samples that are in both buffers
|
||||||
for (int j=0;j<min_samples;j++) {
|
for (int j=0;j<min_samples;j++) {
|
||||||
mixed_samples->data(i)[j] = PerformAll<float, float>(operation, samples_a->data(i)[j], samples_b->data(i)[j]);
|
mixed_samples.data(i)[j] = PerformAll<float, float>(operation, samples_a.data(i)[j], samples_b.data(i)[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,11 +317,11 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
|||||||
// Fill in remainder space with 0s
|
// Fill in remainder space with 0s
|
||||||
int remainder = max_samples - min_samples;
|
int remainder = max_samples - min_samples;
|
||||||
|
|
||||||
SampleBufferPtr larger_buffer = (max_samples == samples_a->sample_count()) ? samples_a : samples_b;
|
const SampleBuffer &larger_buffer = (max_samples == samples_a.sample_count()) ? samples_a : samples_b;
|
||||||
|
|
||||||
for (int i=0;i<mixed_samples->audio_params().channel_count();i++) {
|
for (int i=0;i<mixed_samples.audio_params().channel_count();i++) {
|
||||||
memcpy(&mixed_samples->data(i)[min_samples],
|
memcpy(&mixed_samples.data(i)[min_samples],
|
||||||
&larger_buffer->data(i)[min_samples],
|
&larger_buffer.data(i)[min_samples],
|
||||||
remainder * sizeof(float));
|
remainder * sizeof(float));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,24 +396,25 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
|||||||
|
|
||||||
float number = RetrieveNumber(number_val);
|
float number = RetrieveNumber(number_val);
|
||||||
|
|
||||||
SampleJob job(val_a.type() == NodeValue::kSamples ? val_a : val_b);
|
SampleBuffer buffer = val_a.type() == NodeValue::kSamples ? val_a.toSamples() : val_b.toSamples();
|
||||||
job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this));
|
|
||||||
|
|
||||||
if (job.HasSamples()) {
|
if (buffer.is_allocated()) {
|
||||||
if (IsInputStatic(number_param)) {
|
if (IsInputStatic(number_param)) {
|
||||||
if (!NumberIsNoOp(operation, number)) {
|
if (!NumberIsNoOp(operation, number)) {
|
||||||
for (int i=0;i<job.samples()->audio_params().channel_count();i++) {
|
for (int i=0;i<buffer.audio_params().channel_count();i++) {
|
||||||
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
||||||
// Use SSE instructions for optimization
|
// Use SSE instructions for optimization
|
||||||
PerformAllOnFloatBufferSSE(operation, job.samples()->data(i), number, 0, job.samples()->sample_count());
|
PerformAllOnFloatBufferSSE(operation, buffer.data(i), number, 0, buffer.sample_count());
|
||||||
#else
|
#else
|
||||||
PerformAllOnFloatBuffer(operation, job.samples()->data(i), number, 0, job.samples()->sample_count());
|
PerformAllOnFloatBuffer(operation, buffer.data(i), number, 0, buffer.sample_count());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output->Push(NodeValue::kSamples, QVariant::fromValue(job.samples()), this);
|
output->Push(NodeValue::kSamples, QVariant::fromValue(buffer), this);
|
||||||
} else {
|
} else {
|
||||||
|
SampleJob job(val_a.type() == NodeValue::kSamples ? val_a : val_b);
|
||||||
|
job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this));
|
||||||
output->Push(NodeValue::kSamples, QVariant::fromValue(job), this);
|
output->Push(NodeValue::kSamples, QVariant::fromValue(job), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,7 +427,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MathNodeBase::ProcessSamplesInternal(const NodeValueRow &values, MathNodeBase::Operation operation, const QString ¶m_a_in, const QString ¶m_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const
|
void MathNodeBase::ProcessSamplesInternal(const NodeValueRow &values, MathNodeBase::Operation operation, const QString ¶m_a_in, const QString ¶m_b_in, const olive::SampleBuffer &input, olive::SampleBuffer &output, int index) const
|
||||||
{
|
{
|
||||||
// This function is only used for sample+number pairing
|
// This function is only used for sample+number pairing
|
||||||
NodeValue number_val = values[param_a_in];
|
NodeValue number_val = values[param_a_in];
|
||||||
@@ -441,8 +442,8 @@ void MathNodeBase::ProcessSamplesInternal(const NodeValueRow &values, MathNodeBa
|
|||||||
|
|
||||||
float number_flt = RetrieveNumber(number_val);
|
float number_flt = RetrieveNumber(number_val);
|
||||||
|
|
||||||
for (int i=0;i<output->audio_params().channel_count();i++) {
|
for (int i=0;i<output.audio_params().channel_count();i++) {
|
||||||
output->data(i)[index] = PerformAll<float, float>(operation, input->data(i)[index], number_flt);
|
output.data(i)[index] = PerformAll<float, float>(operation, input.data(i)[index], number_flt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ protected:
|
|||||||
|
|
||||||
void ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b, const NodeGlobals &globals, NodeValueTable *output) const;
|
void ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b, const NodeGlobals &globals, NodeValueTable *output) const;
|
||||||
|
|
||||||
void ProcessSamplesInternal(const NodeValueRow &values, Operation operation, const QString& param_a_in, const QString& param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const;
|
void ProcessSamplesInternal(const NodeValueRow &values, Operation operation, const QString& param_a_in, const QString& param_b_in, const SampleBuffer &input, SampleBuffer &output, int index) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1525,7 +1525,7 @@ ShaderCode Node::GetShaderCode(const ShaderRequest &request) const
|
|||||||
return ShaderCode(QString(), QString());
|
return ShaderCode(QString(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::ProcessSamples(const NodeValueRow &, const SampleBufferPtr, SampleBufferPtr, int) const
|
void Node::ProcessSamples(const NodeValueRow &, const SampleBuffer &, SampleBuffer &, int) const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -691,7 +691,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief If Value() pushes a ShaderJob, this is the function that will process them.
|
* @brief If Value() pushes a ShaderJob, this is the function that will process them.
|
||||||
*/
|
*/
|
||||||
virtual void ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const;
|
virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief If Value() pushes a GenerateJob, override this function for the image to create
|
* @brief If Value() pushes a GenerateJob, override this function for the image to create
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ void NodeTraverser::ResolveJobs(NodeValue &val, const TimeRange &range)
|
|||||||
|
|
||||||
} else if (job.type() == Track::kAudio) {
|
} else if (job.type() == Track::kAudio) {
|
||||||
|
|
||||||
SampleBufferPtr buffer = CreateSampleBuffer(GetCacheAudioParams(), range.length());
|
SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), range.length());
|
||||||
ProcessAudioFootage(buffer, job, range);
|
ProcessAudioFootage(buffer, job, range);
|
||||||
val.set_value(buffer);
|
val.set_value(buffer);
|
||||||
|
|
||||||
@@ -409,7 +409,7 @@ void NodeTraverser::ResolveJobs(NodeValue &val, const TimeRange &range)
|
|||||||
} else if (val.canConvert<SampleJob>()) {
|
} else if (val.canConvert<SampleJob>()) {
|
||||||
|
|
||||||
SampleJob job = val.value<SampleJob>();
|
SampleJob job = val.value<SampleJob>();
|
||||||
SampleBufferPtr output_buffer = CreateSampleBuffer(job.samples()->audio_params(), job.samples()->sample_count());
|
SampleBuffer output_buffer = CreateSampleBuffer(job.samples().audio_params(), job.samples().sample_count());
|
||||||
ProcessSamples(output_buffer, val.source(), range, job);
|
ProcessSamples(output_buffer, val.source(), range, job);
|
||||||
val.set_value(QVariant::fromValue(output_buffer));
|
val.set_value(QVariant::fromValue(output_buffer));
|
||||||
|
|
||||||
|
|||||||
@@ -85,13 +85,13 @@ protected:
|
|||||||
|
|
||||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time){}
|
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time){}
|
||||||
|
|
||||||
virtual void ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time){}
|
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time){}
|
||||||
|
|
||||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job){}
|
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job){}
|
||||||
|
|
||||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job){}
|
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job){}
|
||||||
|
|
||||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job){}
|
virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job){}
|
||||||
|
|
||||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job){}
|
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job){}
|
||||||
|
|
||||||
@@ -102,18 +102,18 @@ protected:
|
|||||||
return CreateDummyTexture(p);
|
return CreateDummyTexture(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual SampleBufferPtr CreateSampleBuffer(const AudioParams ¶ms, int sample_count)
|
virtual SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, int sample_count)
|
||||||
{
|
{
|
||||||
// Return dummy by default
|
// Return dummy by default
|
||||||
return SampleBuffer::Create();
|
return SampleBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleBufferPtr CreateSampleBuffer(const AudioParams ¶ms, const rational &length)
|
SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, const rational &length)
|
||||||
{
|
{
|
||||||
if (params.is_valid()) {
|
if (params.is_valid()) {
|
||||||
return CreateSampleBuffer(params, params.time_to_samples(length));
|
return CreateSampleBuffer(params, params.time_to_samples(length));
|
||||||
} else {
|
} else {
|
||||||
return SampleBuffer::Create();
|
return SampleBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -307,7 +307,7 @@ public:
|
|||||||
static void ValidateVectorString(QStringList* list, int count);
|
static void ValidateVectorString(QStringList* list, int count);
|
||||||
|
|
||||||
TexturePtr toTexture() const { return value<TexturePtr>(); }
|
TexturePtr toTexture() const { return value<TexturePtr>(); }
|
||||||
SampleBufferPtr toSamples() const { return value<SampleBufferPtr>(); }
|
SampleBuffer toSamples() const { return value<SampleBuffer>(); }
|
||||||
bool toBool() const { return value<bool>(); }
|
bool toBool() const { return value<bool>(); }
|
||||||
double toDouble() const { return value<double>(); }
|
double toDouble() const { return value<double>(); }
|
||||||
int64_t toInt() const { return value<int64_t>(); }
|
int64_t toInt() const { return value<int64_t>(); }
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ void AudioPlaybackCache::SetParameters(const AudioParams ¶ms)
|
|||||||
emit ParametersChanged();
|
emit ParametersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, SampleBufferPtr samples)
|
void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, const SampleBuffer &samples)
|
||||||
{
|
{
|
||||||
// Ensure if we have enough segments to write this data, creating more if not
|
// Ensure if we have enough segments to write this data, creating more if not
|
||||||
qint64 length_diff = params_.time_to_bytes_per_channel(range.out()) - playlist_.GetLength();
|
qint64 length_diff = params_.time_to_bytes_per_channel(range.out()) - playlist_.GetLength();
|
||||||
@@ -72,7 +72,7 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &v
|
|||||||
TimeRangeList ranges_we_validated;
|
TimeRangeList ranges_we_validated;
|
||||||
|
|
||||||
// Calculate buffer size per channel
|
// Calculate buffer size per channel
|
||||||
qint64 buffer_size_per_channel = samples ? samples->sample_count() * params_.bytes_per_sample_per_channel() : 0;
|
qint64 buffer_size_per_channel = samples.sample_count() * params_.bytes_per_sample_per_channel();
|
||||||
|
|
||||||
// Write each valid range to the segments
|
// Write each valid range to the segments
|
||||||
foreach (const TimeRange& r, valid_ranges) {
|
foreach (const TimeRange& r, valid_ranges) {
|
||||||
@@ -114,7 +114,7 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &v
|
|||||||
if (possible_write_length > 0) {
|
if (possible_write_length > 0) {
|
||||||
// Assume `samples` is valid if we're here, or else `buffer_size_per_channel` and
|
// Assume `samples` is valid if we're here, or else `buffer_size_per_channel` and
|
||||||
// therefore `possible_write_length` will be 0.
|
// therefore `possible_write_length` will be 0.
|
||||||
seg_file.write(reinterpret_cast<const char*>(samples->data(i)) + src_offset, possible_write_length);
|
seg_file.write(reinterpret_cast<const char*>(samples.data(i)) + src_offset, possible_write_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (possible_write_length < total_write_length) {
|
if (possible_write_length < total_write_length) {
|
||||||
@@ -167,7 +167,7 @@ void AudioPlaybackCache::WriteSilence(const TimeRange &range)
|
|||||||
{
|
{
|
||||||
// WritePCM will automatically fill non-existent bytes with silence, so we just have to send
|
// WritePCM will automatically fill non-existent bytes with silence, so we just have to send
|
||||||
// it an empty sample buffer
|
// it an empty sample buffer
|
||||||
WritePCM(range, {range}, nullptr);
|
WritePCM(range, {range}, SampleBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioPlaybackCache::ShiftEvent(const rational &from_in_time, const rational &to_in_time)
|
void AudioPlaybackCache::ShiftEvent(const rational &from_in_time, const rational &to_in_time)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public:
|
|||||||
|
|
||||||
void SetParameters(const AudioParams& params);
|
void SetParameters(const AudioParams& params);
|
||||||
|
|
||||||
void WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, SampleBufferPtr samples);
|
void WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, const SampleBuffer &samples);
|
||||||
|
|
||||||
void WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform);
|
void WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform);
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ class SampleJob : public AcceleratedJob {
|
|||||||
public:
|
public:
|
||||||
SampleJob()
|
SampleJob()
|
||||||
{
|
{
|
||||||
samples_ = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleJob(const NodeValue& value)
|
SampleJob(const NodeValue& value)
|
||||||
@@ -43,18 +42,18 @@ public:
|
|||||||
samples_ = row[from].toSamples();
|
samples_ = row[from].toSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleBufferPtr samples() const
|
const SampleBuffer &samples() const
|
||||||
{
|
{
|
||||||
return samples_;
|
return samples_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasSamples() const
|
bool HasSamples() const
|
||||||
{
|
{
|
||||||
return samples_ && samples_->is_allocated();
|
return samples_.is_allocated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SampleBufferPtr samples_;
|
SampleBuffer samples_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ void PreviewAutoCacher::AudioRendered()
|
|||||||
// WritePCM is tolerant to its buffer being null, it will just write silence instead
|
// WritePCM is tolerant to its buffer being null, it will just write silence instead
|
||||||
viewer_node_->audio_playback_cache()->WritePCM(range,
|
viewer_node_->audio_playback_cache()->WritePCM(range,
|
||||||
valid_ranges,
|
valid_ranges,
|
||||||
watcher->Get().value<SampleBufferPtr>());
|
watcher->Get().value<SampleBuffer>());
|
||||||
}
|
}
|
||||||
|
|
||||||
viewer_node_->audio_playback_cache()->WriteWaveform(range, valid_ranges, &waveform);
|
viewer_node_->audio_playback_cache()->WriteWaveform(range, valid_ranges, &waveform);
|
||||||
|
|||||||
@@ -200,11 +200,11 @@ void RenderProcessor::Run()
|
|||||||
|
|
||||||
ResolveJobs(sample_val, time);
|
ResolveJobs(sample_val, time);
|
||||||
|
|
||||||
SampleBufferPtr samples = sample_val.toSamples();
|
SampleBuffer samples = sample_val.toSamples();
|
||||||
if (samples && ticket_->property("enablewaveforms").toBool()) {
|
if (samples.is_allocated() && ticket_->property("enablewaveforms").toBool()) {
|
||||||
AudioVisualWaveform vis;
|
AudioVisualWaveform vis;
|
||||||
vis.set_channel_count(samples->audio_params().channel_count());
|
vis.set_channel_count(samples.audio_params().channel_count());
|
||||||
vis.OverwriteSamples(samples, samples->audio_params().sample_rate());
|
vis.OverwriteSamples(samples, samples.audio_params().sample_rate());
|
||||||
ticket_->setProperty("waveform", QVariant::fromValue(vis));
|
ticket_->setProperty("waveform", QVariant::fromValue(vis));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,9 +275,8 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
|||||||
QVector<Block*> active_blocks = track->BlocksAtTimeRange(range);
|
QVector<Block*> active_blocks = track->BlocksAtTimeRange(range);
|
||||||
|
|
||||||
// All these blocks will need to output to a buffer so we create one here
|
// All these blocks will need to output to a buffer so we create one here
|
||||||
SampleBufferPtr block_range_buffer = SampleBuffer::CreateAllocated(audio_params,
|
SampleBuffer block_range_buffer(audio_params, range.length());
|
||||||
audio_params.time_to_samples(range.length()));
|
block_range_buffer.silence();
|
||||||
block_range_buffer->silence();
|
|
||||||
|
|
||||||
NodeValueTable merged_table;
|
NodeValueTable merged_table;
|
||||||
|
|
||||||
@@ -292,10 +291,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
|||||||
|
|
||||||
// Destination buffer
|
// Destination buffer
|
||||||
NodeValueTable table = GenerateTable(b, track->GetValueHintForInput(Track::kBlockInput, track->GetArrayIndexFromBlock(b)),Track::TransformRangeForBlock(b, range_for_block));
|
NodeValueTable table = GenerateTable(b, track->GetValueHintForInput(Track::kBlockInput, track->GetArrayIndexFromBlock(b)),Track::TransformRangeForBlock(b, range_for_block));
|
||||||
SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).toSamples();
|
SampleBuffer samples_from_this_block = table.Take(NodeValue::kSamples).toSamples();
|
||||||
ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b);
|
ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b);
|
||||||
|
|
||||||
if (samples_from_this_block) {
|
if (samples_from_this_block.is_allocated()) {
|
||||||
// If this is a clip, we might have extra speed/reverse information
|
// If this is a clip, we might have extra speed/reverse information
|
||||||
if (clip_cast) {
|
if (clip_cast) {
|
||||||
double speed_value = clip_cast->speed();
|
double speed_value = clip_cast->speed();
|
||||||
@@ -303,12 +302,12 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
|||||||
|
|
||||||
if (qIsNull(speed_value)) {
|
if (qIsNull(speed_value)) {
|
||||||
// Just silence, don't think there's any other practical application of 0 speed audio
|
// Just silence, don't think there's any other practical application of 0 speed audio
|
||||||
samples_from_this_block->silence();
|
samples_from_this_block.silence();
|
||||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||||
if (clip_cast->maintain_audio_pitch()) {
|
if (clip_cast->maintain_audio_pitch()) {
|
||||||
AudioProcessor processor;
|
AudioProcessor processor;
|
||||||
|
|
||||||
if (processor.Open(samples_from_this_block->audio_params(), samples_from_this_block->audio_params(), speed_value)) {
|
if (processor.Open(samples_from_this_block.audio_params(), samples_from_this_block.audio_params(), speed_value)) {
|
||||||
AudioProcessor::Buffer out;
|
AudioProcessor::Buffer out;
|
||||||
|
|
||||||
// FIXME: This is not the best way to do this, the TempoProcessor works best
|
// FIXME: This is not the best way to do this, the TempoProcessor works best
|
||||||
@@ -317,7 +316,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
|||||||
// well on export (assuming audio is all generated at once on export), but
|
// well on export (assuming audio is all generated at once on export), but
|
||||||
// users may hear clicks and pops in the audio during preview due to this
|
// users may hear clicks and pops in the audio during preview due to this
|
||||||
// approach.
|
// approach.
|
||||||
int r = processor.Convert(samples_from_this_block->to_raw_ptrs(), samples_from_this_block->sample_count(), nullptr);
|
int r = processor.Convert(samples_from_this_block.to_raw_ptrs().data(), samples_from_this_block.sample_count(), nullptr);
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
qCritical() << "Failed to change tempo of audio:" << r;
|
qCritical() << "Failed to change tempo of audio:" << r;
|
||||||
@@ -327,16 +326,13 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
|||||||
processor.Convert(nullptr, 0, &out);
|
processor.Convert(nullptr, 0, &out);
|
||||||
|
|
||||||
if (!out.empty()) {
|
if (!out.empty()) {
|
||||||
int nb_samples = out.front().size() * samples_from_this_block->audio_params().bytes_per_sample_per_channel();
|
int nb_samples = out.front().size() * samples_from_this_block.audio_params().bytes_per_sample_per_channel();
|
||||||
|
|
||||||
if (nb_samples) {
|
if (nb_samples) {
|
||||||
SampleBufferPtr new_samples = SampleBuffer::Create();
|
SampleBuffer new_samples(samples_from_this_block.audio_params(), nb_samples);
|
||||||
new_samples->set_audio_params(samples_from_this_block->audio_params());
|
|
||||||
new_samples->set_sample_count(nb_samples);
|
|
||||||
new_samples->allocate();
|
|
||||||
|
|
||||||
for (int i=0; i<out.size(); i++) {
|
for (int i=0; i<out.size(); i++) {
|
||||||
memcpy(new_samples->data(i), out[i].data(), out[i].size());
|
memcpy(new_samples.data(i), out[i].data(), out[i].size());
|
||||||
}
|
}
|
||||||
|
|
||||||
samples_from_this_block = new_samples;
|
samples_from_this_block = new_samples;
|
||||||
@@ -346,20 +342,20 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Multiply time
|
// Multiply time
|
||||||
samples_from_this_block->speed(speed_value);
|
samples_from_this_block.speed(speed_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reversed) {
|
if (reversed) {
|
||||||
samples_from_this_block->reverse();
|
samples_from_this_block.reverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count());
|
int copy_length = qMin(max_dest_sz, samples_from_this_block.sample_count());
|
||||||
|
|
||||||
// Copy samples into destination buffer
|
// Copy samples into destination buffer
|
||||||
for (int i=0; i<samples_from_this_block->audio_params().channel_count(); i++) {
|
for (int i=0; i<samples_from_this_block.audio_params().channel_count(); i++) {
|
||||||
block_range_buffer->set(i, samples_from_this_block->data(i), destination_offset, copy_length);
|
block_range_buffer.set(i, samples_from_this_block.data(i), destination_offset, copy_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeValueTable::Merge({merged_table, table});
|
NodeValueTable::Merge({merged_table, table});
|
||||||
@@ -372,7 +368,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
|||||||
waveform_info.block = clip_cast;
|
waveform_info.block = clip_cast;
|
||||||
waveform_info.range = range_for_block - b->in();
|
waveform_info.range = range_for_block - b->in();
|
||||||
|
|
||||||
if (!(waveform_info.silence = !samples_from_this_block.get())) {
|
if (!(waveform_info.silence = !samples_from_this_block.is_allocated())) {
|
||||||
// Generate a visual waveform from the samples acquired from this block
|
// Generate a visual waveform from the samples acquired from this block
|
||||||
AudioVisualWaveform visual_waveform;
|
AudioVisualWaveform visual_waveform;
|
||||||
visual_waveform.set_channel_count(audio_params.channel_count());
|
visual_waveform.set_channel_count(audio_params.channel_count());
|
||||||
@@ -483,7 +479,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderProcessor::ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time)
|
void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time)
|
||||||
{
|
{
|
||||||
DecoderPtr decoder = ResolveDecoderFromInput(stream.decoder(), Decoder::CodecStream(stream.filename(), stream.audio_params().stream_index()));
|
DecoderPtr decoder = ResolveDecoderFromInput(stream.decoder(), Decoder::CodecStream(stream.filename(), stream.audio_params().stream_index()));
|
||||||
|
|
||||||
@@ -526,9 +522,9 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, co
|
|||||||
render_ctx_->BlitToTexture(shader, job, destination.get());
|
render_ctx_->BlitToTexture(shader, job, destination.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderProcessor::ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
||||||
{
|
{
|
||||||
if (!job.samples() || !job.samples()->is_allocated()) {
|
if (!job.samples().is_allocated()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,7 +532,7 @@ void RenderProcessor::ProcessSamples(SampleBufferPtr destination, const Node *no
|
|||||||
|
|
||||||
const AudioParams& audio_params = GetCacheAudioParams();
|
const AudioParams& audio_params = GetCacheAudioParams();
|
||||||
|
|
||||||
for (int i=0;i<job.samples()->sample_count();i++) {
|
for (int i=0;i<job.samples().sample_count();i++) {
|
||||||
// Calculate the exact rational time at this sample
|
// Calculate the exact rational time at this sample
|
||||||
double sample_to_second = static_cast<double>(i) / static_cast<double>(audio_params.sample_rate());
|
double sample_to_second = static_cast<double>(i) / static_cast<double>(audio_params.sample_rate());
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ protected:
|
|||||||
|
|
||||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
||||||
|
|
||||||
virtual void ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time) override;
|
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time) override;
|
||||||
|
|
||||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||||
|
|
||||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||||
|
|
||||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
||||||
|
|
||||||
@@ -63,9 +63,9 @@ protected:
|
|||||||
return render_ctx_->CreateTexture(p);
|
return render_ctx_->CreateTexture(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual SampleBufferPtr CreateSampleBuffer(const AudioParams ¶ms, int sample_count) override
|
virtual SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, int sample_count) override
|
||||||
{
|
{
|
||||||
return SampleBuffer::CreateAllocated(params, sample_count);
|
return SampleBuffer(params, sample_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs) override;
|
virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs) override;
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ bool ExportTask::FrameDownloaded(FramePtr f, const QByteArray &hash, const QVect
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples)
|
bool ExportTask::AudioDownloaded(const TimeRange &range, const SampleBuffer &samples)
|
||||||
{
|
{
|
||||||
TimeRange adjusted_range = range;
|
TimeRange adjusted_range = range;
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ bool ExportTask::EncodeSubtitle(const SubtitleBlock *sub)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportTask::WriteAudioLoop(const TimeRange& time, SampleBufferPtr samples)
|
bool ExportTask::WriteAudioLoop(const TimeRange& time, const SampleBuffer &samples)
|
||||||
{
|
{
|
||||||
if (!encoder_->WriteAudio(samples)) {
|
if (!encoder_->WriteAudio(samples)) {
|
||||||
SetError(encoder_->GetError());
|
SetError(encoder_->GetError());
|
||||||
@@ -232,7 +232,7 @@ bool ExportTask::WriteAudioLoop(const TimeRange& time, SampleBufferPtr samples)
|
|||||||
|
|
||||||
for (auto it=audio_map_.begin(); it!=audio_map_.end(); it++) {
|
for (auto it=audio_map_.begin(); it!=audio_map_.end(); it++) {
|
||||||
TimeRange t = it.key();
|
TimeRange t = it.key();
|
||||||
SampleBufferPtr s = it.value();
|
SampleBuffer s = it.value();
|
||||||
|
|
||||||
if (t.in() == audio_time_) {
|
if (t.in() == audio_time_) {
|
||||||
// Erase from audio map since we're just about to write it
|
// Erase from audio map since we're just about to write it
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ protected:
|
|||||||
|
|
||||||
virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times) override;
|
virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times) override;
|
||||||
|
|
||||||
virtual bool AudioDownloaded(const TimeRange& range, SampleBufferPtr samples) override;
|
virtual bool AudioDownloaded(const TimeRange& range, const SampleBuffer &samples) override;
|
||||||
|
|
||||||
virtual bool EncodeSubtitle(const SubtitleBlock *sub) override;
|
virtual bool EncodeSubtitle(const SubtitleBlock *sub) override;
|
||||||
|
|
||||||
@@ -50,11 +50,11 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool WriteAudioLoop(const TimeRange &time, SampleBufferPtr samples);
|
bool WriteAudioLoop(const TimeRange &time, const SampleBuffer &samples);
|
||||||
|
|
||||||
QHash<rational, FramePtr> time_map_;
|
QHash<rational, FramePtr> time_map_;
|
||||||
|
|
||||||
QHash<TimeRange, SampleBufferPtr> audio_map_;
|
QHash<TimeRange, SampleBuffer> audio_map_;
|
||||||
|
|
||||||
ColorManager* color_manager_;
|
ColorManager* color_manager_;
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ bool PreCacheTask::FrameDownloaded(FramePtr frame, const QByteArray &hash, const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PreCacheTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples)
|
bool PreCacheTask::AudioDownloaded(const TimeRange &range, const SampleBuffer &samples)
|
||||||
{
|
{
|
||||||
// Pre-cache doesn't cache any audio
|
// Pre-cache doesn't cache any audio
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ protected:
|
|||||||
|
|
||||||
virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times) override;
|
virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times) override;
|
||||||
|
|
||||||
virtual bool AudioDownloaded(const TimeRange& range, SampleBufferPtr samples) override;
|
virtual bool AudioDownloaded(const TimeRange& range, const SampleBuffer &samples) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Project* project_;
|
Project* project_;
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ bool RenderTask::Render(ColorManager* manager,
|
|||||||
|
|
||||||
TimeRange range = watcher->property("range").value<TimeRange>();
|
TimeRange range = watcher->property("range").value<TimeRange>();
|
||||||
|
|
||||||
if (!AudioDownloaded(range, watcher->Get().value<SampleBufferPtr>())) {
|
if (!AudioDownloaded(range, watcher->Get().value<SampleBuffer>())) {
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ protected:
|
|||||||
|
|
||||||
virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times) = 0;
|
virtual bool FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times) = 0;
|
||||||
|
|
||||||
virtual bool AudioDownloaded(const TimeRange& range, SampleBufferPtr samples) = 0;
|
virtual bool AudioDownloaded(const TimeRange& range, const SampleBuffer &samples) = 0;
|
||||||
|
|
||||||
virtual bool EncodeSubtitle(const SubtitleBlock *subtitle);
|
virtual bool EncodeSubtitle(const SubtitleBlock *subtitle);
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ void AudioMonitor::Stop()
|
|||||||
// loop will stop itself since file_ and waveform_ are null.
|
// loop will stop itself since file_ and waveform_ are null.
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMonitor::PushSampleBuffer(SampleBufferPtr d)
|
void AudioMonitor::PushSampleBuffer(const SampleBuffer &d)
|
||||||
{
|
{
|
||||||
if (!params_.channel_count()) {
|
if (!params_.channel_count()) {
|
||||||
return;
|
return;
|
||||||
@@ -83,7 +83,7 @@ void AudioMonitor::PushSampleBuffer(SampleBufferPtr d)
|
|||||||
|
|
||||||
QVector<double> v(params_.channel_count(), 0);
|
QVector<double> v(params_.channel_count(), 0);
|
||||||
|
|
||||||
AudioVisualWaveform::Sample summed = AudioVisualWaveform::SumSamples(d, 0, d->sample_count());
|
AudioVisualWaveform::Sample summed = AudioVisualWaveform::SumSamples(d, 0, d.sample_count());
|
||||||
|
|
||||||
AudioVisualWaveformSampleToInternalValues(summed, v);
|
AudioVisualWaveformSampleToInternalValues(summed, v);
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PushSampleBufferOnAll(SampleBufferPtr d)
|
static void PushSampleBufferOnAll(const SampleBuffer &d)
|
||||||
{
|
{
|
||||||
foreach (AudioMonitor *m, instances_) {
|
foreach (AudioMonitor *m, instances_) {
|
||||||
m->PushSampleBuffer(d);
|
m->PushSampleBuffer(d);
|
||||||
@@ -71,7 +71,7 @@ public slots:
|
|||||||
|
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
void PushSampleBuffer(SampleBufferPtr samples);
|
void PushSampleBuffer(const SampleBuffer &samples);
|
||||||
|
|
||||||
void StartWaveform(const AudioVisualWaveform *waveform, const rational& start, int playback_speed);
|
void StartWaveform(const AudioVisualWaveform *waveform, const rational& start, int playback_speed);
|
||||||
|
|
||||||
|
|||||||
@@ -511,16 +511,16 @@ void ViewerWidget::ReceivedAudioBufferForPlayback()
|
|||||||
audio_playback_queue_.pop_front();
|
audio_playback_queue_.pop_front();
|
||||||
|
|
||||||
if (watcher->HasResult()) {
|
if (watcher->HasResult()) {
|
||||||
SampleBufferPtr samples = watcher->Get().value<SampleBufferPtr>();
|
SampleBuffer samples = watcher->Get().value<SampleBuffer>();
|
||||||
if (samples) {
|
if (samples.is_allocated()) {
|
||||||
// If the samples must be reversed, reverse them now
|
// If the samples must be reversed, reverse them now
|
||||||
if (playback_speed_ < 0) {
|
if (playback_speed_ < 0) {
|
||||||
samples->reverse();
|
samples.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to packed data for audio output
|
// Convert to packed data for audio output
|
||||||
AudioProcessor::Buffer buf;
|
AudioProcessor::Buffer buf;
|
||||||
int r = audio_processor_.Convert(samples->to_raw_ptrs(), samples->sample_count(), &buf);
|
int r = audio_processor_.Convert(samples.to_raw_ptrs().data(), samples.sample_count(), &buf);
|
||||||
|
|
||||||
// TempoProcessor may have emptied the array
|
// TempoProcessor may have emptied the array
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
@@ -556,8 +556,9 @@ void ViewerWidget::ReceivedAudioBufferForScrubbing()
|
|||||||
RenderTicketWatcher *watcher = static_cast<RenderTicketWatcher *>(sender());
|
RenderTicketWatcher *watcher = static_cast<RenderTicketWatcher *>(sender());
|
||||||
|
|
||||||
if (watcher->HasResult()) {
|
if (watcher->HasResult()) {
|
||||||
if (SampleBufferPtr samples = watcher->Get().value<SampleBufferPtr>()) {
|
SampleBuffer samples = watcher->Get().value<SampleBuffer>();
|
||||||
if (samples->audio_params().channel_count() > 0) {
|
if (samples.is_allocated()) {
|
||||||
|
if (samples.audio_params().channel_count() > 0) {
|
||||||
/* Fade code
|
/* Fade code
|
||||||
const int kFadeSz = qMin(200, samples->sample_count()/4);
|
const int kFadeSz = qMin(200, samples->sample_count()/4);
|
||||||
for (int i=0; i<kFadeSz; i++) {
|
for (int i=0; i<kFadeSz; i++) {
|
||||||
@@ -567,7 +568,7 @@ void ViewerWidget::ReceivedAudioBufferForScrubbing()
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
AudioProcessor::Buffer buf;
|
AudioProcessor::Buffer buf;
|
||||||
int r = audio_processor_.Convert(samples->to_raw_ptrs(), samples->sample_count(), &buf);
|
int r = audio_processor_.Convert(samples.to_raw_ptrs().data(), samples.sample_count(), &buf);
|
||||||
|
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
if (!buf.empty()) {
|
if (!buf.empty()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user