The macro defined namespaces confused the hell out of lupdate and more or less broke translations permanently. Looks like the only way we can do it is to have a hardcoded namespace, which goes against my instinct, but honestly how likely is it that we'll change the namespace anyway (I guess forks might want to do it, but that's their problem ;) )
334 lines
10 KiB
C++
334 lines
10 KiB
C++
/***
|
|
|
|
Olive - Non-Linear Video Editor
|
|
Copyright (C) 2020 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/>.
|
|
|
|
***/
|
|
|
|
#include "audiovisualwaveform.h"
|
|
|
|
#include <QDebug>
|
|
|
|
#include "config/config.h"
|
|
|
|
namespace olive {
|
|
|
|
const int AudioVisualWaveform::kSumSampleRate = 200;
|
|
|
|
void AudioVisualWaveform::AddSum(const float *samples, int nb_samples, int nb_channels)
|
|
{
|
|
data_.append(SumSamples(samples, nb_samples, nb_channels));
|
|
}
|
|
|
|
void AudioVisualWaveform::OverwriteSamples(SampleBufferPtr samples, int sample_rate, const rational &start)
|
|
{
|
|
if (!channels_) {
|
|
qWarning() << "Failed to write samples - channel count is zero";
|
|
return;
|
|
}
|
|
|
|
int start_index = time_to_samples(start);
|
|
int samples_length = time_to_samples(static_cast<double>(samples->sample_count()) / static_cast<double>(sample_rate));
|
|
|
|
int end_index = start_index + samples_length;
|
|
if (data_.size() < end_index) {
|
|
data_.resize(end_index);
|
|
}
|
|
|
|
int chunk_size = sample_rate / kSumSampleRate;
|
|
|
|
for (int i=0; i<samples_length; i+=channels_) {
|
|
int src_index = (i * chunk_size) / channels_;
|
|
|
|
QVector<SamplePerChannel> summary = SumSamples(samples,
|
|
src_index,
|
|
qMin(chunk_size, samples->sample_count() - src_index));
|
|
|
|
memcpy(&data_.data()[i + start_index],
|
|
summary.constData(),
|
|
summary.size() * sizeof(SamplePerChannel));
|
|
}
|
|
}
|
|
|
|
void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const rational &dest, const rational& offset, const rational& length)
|
|
{
|
|
if (sums.data_.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
int start_index = time_to_samples(dest);
|
|
int sample_start = time_to_samples(offset);
|
|
|
|
int copy_len = sums.data_.size() - sample_start;
|
|
if (!length.isNull()) {
|
|
copy_len = qMin(copy_len, time_to_samples(length));
|
|
}
|
|
|
|
int end_index = start_index + copy_len;
|
|
|
|
if (data_.size() < end_index) {
|
|
data_.resize(end_index);
|
|
}
|
|
|
|
memcpy(reinterpret_cast<char*>(data_.data()) + start_index * sizeof(SamplePerChannel),
|
|
reinterpret_cast<const char*>(sums.data_.constData()) + time_to_samples(offset) * sizeof(SamplePerChannel),
|
|
copy_len * sizeof(SamplePerChannel));
|
|
}
|
|
|
|
AudioVisualWaveform AudioVisualWaveform::Mid(const rational &time) const
|
|
{
|
|
int sample_index = time_to_samples(time);
|
|
|
|
// Create a copy of this waveform chop the early section off
|
|
AudioVisualWaveform copy = *this;
|
|
copy.data_ = data_.mid(sample_index);
|
|
|
|
return copy;
|
|
}
|
|
|
|
void AudioVisualWaveform::Append(const AudioVisualWaveform &waveform)
|
|
{
|
|
data_.append(waveform.data_);
|
|
}
|
|
|
|
void AudioVisualWaveform::TrimIn(const rational &time)
|
|
{
|
|
data_ = data_.mid(time_to_samples(time));
|
|
}
|
|
|
|
void AudioVisualWaveform::TrimOut(const rational &time)
|
|
{
|
|
data_.resize(data_.size() - time_to_samples(time));
|
|
}
|
|
|
|
void AudioVisualWaveform::PrependSilence(const rational &time)
|
|
{
|
|
int added_samples = time_to_samples(time);
|
|
|
|
// Resize buffer for extra space
|
|
data_.resize(data_.size() + added_samples);
|
|
|
|
// Shift all data forward
|
|
for (int i=data_.size()-1; i>=added_samples; i--) {
|
|
data_[i] = data_[i - added_samples];
|
|
}
|
|
|
|
// Fill remainder with silence
|
|
memset(reinterpret_cast<char*>(data_.data()), 0, added_samples * sizeof(SamplePerChannel));
|
|
}
|
|
|
|
void AudioVisualWaveform::AppendSilence(const rational &time)
|
|
{
|
|
int added_samples = time_to_samples(time);
|
|
|
|
// Resize buffer for extra space
|
|
int old_size = data_.size();
|
|
data_.resize(old_size + added_samples);
|
|
|
|
// Fill remainder with silence
|
|
memset(reinterpret_cast<char*>(&data_[old_size]), 0, (data_.size() - old_size) * sizeof(SamplePerChannel));
|
|
}
|
|
|
|
void AudioVisualWaveform::Shift(const rational &from, const rational &to)
|
|
{
|
|
int from_index = time_to_samples(from);
|
|
int to_index = time_to_samples(to);
|
|
|
|
if (from_index == to_index) {
|
|
return;
|
|
}
|
|
|
|
if (from_index > to_index) {
|
|
// Shifting backwards <-
|
|
int copy_sz = data_.size() - from_index;
|
|
|
|
for (int i=0; i<copy_sz; i++) {
|
|
data_.replace(to_index + i, data_.at(from_index + i));
|
|
}
|
|
|
|
data_.resize(data_.size() - (from_index - to_index));
|
|
} else {
|
|
// Shifting forwards ->
|
|
int old_sz = data_.size();
|
|
|
|
int distance = (to_index - from_index);
|
|
|
|
data_.resize(data_.size() + distance);
|
|
|
|
int copy_sz = old_sz - from_index;
|
|
|
|
for (int i=0; i<copy_sz; i++) {
|
|
data_.replace(data_.size() - i - 1, data_.at(old_sz - i - 1));
|
|
}
|
|
|
|
memset(reinterpret_cast<char*>(&data_[from_index]), 0, distance * sizeof(SamplePerChannel));
|
|
}
|
|
}
|
|
|
|
QVector<AudioVisualWaveform::SamplePerChannel> AudioVisualWaveform::SumSamples(const float *samples, int nb_samples, int nb_channels)
|
|
{
|
|
return SumSamplesInternal<float>(samples, nb_samples, nb_channels);
|
|
}
|
|
|
|
QVector<AudioVisualWaveform::SamplePerChannel> AudioVisualWaveform::SumSamples(const qfloat16 *samples, int nb_samples, int nb_channels)
|
|
{
|
|
return SumSamplesInternal<qfloat16>(samples, nb_samples, nb_channels);
|
|
}
|
|
|
|
QVector<AudioVisualWaveform::SamplePerChannel> AudioVisualWaveform::SumSamples(SampleBufferPtr samples, int start_index, int length)
|
|
{
|
|
QVector<AudioVisualWaveform::SamplePerChannel> summed_samples(samples->audio_params().channel_count());
|
|
|
|
int end_index = start_index + length;
|
|
|
|
for (int i=start_index; i<end_index; i++) {
|
|
for (int channel=0; channel<samples->audio_params().channel_count(); channel++) {
|
|
ExpandMinMax<float>(summed_samples[channel], samples->data()[channel][i]);
|
|
}
|
|
}
|
|
|
|
return summed_samples;
|
|
}
|
|
|
|
QVector<AudioVisualWaveform::SamplePerChannel> AudioVisualWaveform::ReSumSamples(const SamplePerChannel* samples,
|
|
int nb_samples,
|
|
int nb_channels)
|
|
{
|
|
QVector<AudioVisualWaveform::SamplePerChannel> summed_samples(nb_channels);
|
|
|
|
for (int i=0;i<nb_samples;i+=nb_channels) {
|
|
for (int j=0;j<nb_channels;j++) {
|
|
const AudioVisualWaveform::SamplePerChannel& sample = samples[i + j];
|
|
|
|
if (sample.min < summed_samples[j].min) {
|
|
summed_samples[j].min = sample.min;
|
|
}
|
|
|
|
if (sample.max > summed_samples[j].max) {
|
|
summed_samples[j].max = sample.max;
|
|
}
|
|
}
|
|
}
|
|
|
|
return summed_samples;
|
|
}
|
|
|
|
void AudioVisualWaveform::DrawSample(QPainter *painter, const QVector<SamplePerChannel>& sample, int x, int y, int height)
|
|
{
|
|
int channel_height = height / sample.size();
|
|
int channel_half_height = channel_height / 2;
|
|
|
|
for (int i=0;i<sample.size();i++) {
|
|
qfloat16 max = qMin(sample.at(i).max, static_cast<qfloat16>(1.0f));
|
|
qfloat16 min = qMax(sample.at(i).min, static_cast<qfloat16>(-1.0));
|
|
|
|
if (Config::Current()[QStringLiteral("RectifiedWaveforms")].toBool()) {
|
|
int channel_bottom = y + channel_height * (i + 1);
|
|
|
|
int diff = qRound((max - min) * channel_half_height);
|
|
|
|
painter->drawLine(x,
|
|
channel_bottom - diff,
|
|
x,
|
|
channel_bottom);
|
|
} else {
|
|
int channel_mid = y + channel_height * i + channel_half_height;
|
|
|
|
painter->drawLine(x,
|
|
channel_mid + qRound(min * static_cast<float>(channel_half_height)),
|
|
x,
|
|
channel_mid + qRound(max * static_cast<float>(channel_half_height)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, const double& scale, const AudioVisualWaveform &samples, const rational& start_time)
|
|
{
|
|
int start_sample_index = samples.time_to_samples(start_time);
|
|
|
|
if (start_sample_index >= samples.nb_samples()) {
|
|
return;
|
|
}
|
|
|
|
int next_sample_index = start_sample_index;
|
|
int sample_index;
|
|
|
|
QVector<SamplePerChannel> summary;
|
|
int summary_index = -1;
|
|
|
|
const QRect& viewport = painter->viewport();
|
|
QPoint top_left = painter->transform().map(viewport.topLeft());
|
|
|
|
int start = qMax(rect.x(), -top_left.x());
|
|
int end = qMin(rect.right(), -top_left.x() + viewport.width());
|
|
|
|
for (int i=start;i<end;i++) {
|
|
sample_index = next_sample_index;
|
|
|
|
if (sample_index == samples.nb_samples()) {
|
|
break;
|
|
}
|
|
|
|
next_sample_index = qMin(samples.nb_samples(),
|
|
start_sample_index + qFloor(static_cast<double>(kSumSampleRate) * static_cast<double>(i - rect.x() + 1) / scale) * samples.channel_count());
|
|
|
|
if (summary_index != sample_index) {
|
|
summary = AudioVisualWaveform::ReSumSamples(&samples.data_.at(sample_index),
|
|
qMax(samples.channel_count(), next_sample_index - sample_index),
|
|
samples.channel_count());
|
|
summary_index = sample_index;
|
|
}
|
|
|
|
DrawSample(painter, summary, i, rect.y(), rect.height());
|
|
}
|
|
}
|
|
|
|
int AudioVisualWaveform::time_to_samples(const rational &time) const
|
|
{
|
|
return time_to_samples(time.toDouble());
|
|
}
|
|
|
|
int AudioVisualWaveform::time_to_samples(const double &time) const
|
|
{
|
|
return qFloor(time * kSumSampleRate) * channels_;
|
|
}
|
|
|
|
template<typename T>
|
|
QVector<AudioVisualWaveform::SamplePerChannel> AudioVisualWaveform::SumSamplesInternal(const T *samples, int nb_samples, int nb_channels)
|
|
{
|
|
QVector<AudioVisualWaveform::SamplePerChannel> summed_samples(nb_channels);
|
|
|
|
for (int i=0;i<nb_samples;i++) {
|
|
ExpandMinMax<T>(summed_samples[i%nb_channels], samples[i]);
|
|
}
|
|
|
|
return summed_samples;
|
|
}
|
|
|
|
template<typename T>
|
|
void AudioVisualWaveform::ExpandMinMax(AudioVisualWaveform::SamplePerChannel &sum, T value)
|
|
{
|
|
if (value < sum.min) {
|
|
sum.min = value;
|
|
}
|
|
|
|
if (value > sum.max) {
|
|
sum.max = value;
|
|
}
|
|
}
|
|
|
|
}
|