Skip to content

Commit

Permalink
Implemented position remapper inside FrameMapper to fix audio noise w…
Browse files Browse the repository at this point in the history
…hen exporting to different fps

The FrameMapper class now receives the updated clip position and returns the correct amount of samples for a given frame number
  • Loading branch information
BrennoCaldato authored and ferdnyc committed Oct 19, 2020
1 parent b936ea8 commit a410941
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 25 deletions.
8 changes: 6 additions & 2 deletions include/FrameMapper.h
Expand Up @@ -147,6 +147,9 @@ namespace openshot
bool is_dirty; // When this is true, the next call to GetFrame will re-init the mapping
SWRCONTEXT *avr; // Audio resampling context object

float position;
float start;

// Internal methods used by init
void AddField(int64_t frame);
void AddField(Field field);
Expand All @@ -166,13 +169,13 @@ namespace openshot
std::vector<MappedFrame> frames; // List of all frames

/// Default constructor for openshot::FrameMapper class
FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout);
FrameMapper(ReaderBase *reader, Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart);

/// Destructor
virtual ~FrameMapper();

/// Change frame rate or audio mapping details
void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout);
void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart);

/// Close the openshot::FrameMapper and internal reader
void Close() override;
Expand Down Expand Up @@ -218,6 +221,7 @@ namespace openshot
/// Resample audio and map channels (if needed)
void ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t original_frame_number);

int64_t ConvPositon(int64_t clip_frame_number);
};
}

Expand Down
39 changes: 30 additions & 9 deletions src/FrameMapper.cpp
Expand Up @@ -33,7 +33,7 @@
using namespace std;
using namespace openshot;

FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) :
FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart) :
reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL)
{
// Set the original frame rate from the reader
Expand All @@ -52,6 +52,8 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe
info.width = reader->info.width;
info.height = reader->info.height;

position = clipPosition;
start = clipStart;
// Used to toggle odd / even fields
field_toggle = true;

Expand Down Expand Up @@ -257,12 +259,12 @@ void FrameMapper::Init()
// the original sample rate.
int64_t end_samples_frame = start_samples_frame;
int end_samples_position = start_samples_position;
int remaining_samples = Frame::GetSamplesPerFrame(frame_number, target, reader->info.sample_rate, reader->info.channels);
int remaining_samples = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels);

while (remaining_samples > 0)
{
// get original samples
int original_samples = Frame::GetSamplesPerFrame(end_samples_frame, original, reader->info.sample_rate, reader->info.channels) - end_samples_position;
int original_samples = Frame::GetSamplesPerFrame(ConvPositon(end_samples_frame), original, reader->info.sample_rate, reader->info.channels) - end_samples_position;

// Enough samples
if (original_samples >= remaining_samples)
Expand All @@ -282,12 +284,12 @@ void FrameMapper::Init()


// Create the sample mapping struct
SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(frame_number, target, reader->info.sample_rate, reader->info.channels)};
SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, reader->info.sample_rate, reader->info.channels)};

// Reset the audio variables
start_samples_frame = end_samples_frame;
start_samples_position = end_samples_position + 1;
if (start_samples_position >= Frame::GetSamplesPerFrame(start_samples_frame, original, reader->info.sample_rate, reader->info.channels))
if (start_samples_position >= Frame::GetSamplesPerFrame(ConvPositon(start_samples_frame), original, reader->info.sample_rate, reader->info.channels))
{
start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one)
start_samples_position = 0; // reset to 0, since we wrapped
Expand Down Expand Up @@ -354,7 +356,7 @@ std::shared_ptr<Frame> FrameMapper::GetOrCreateFrame(int64_t number)
std::shared_ptr<Frame> new_frame;

// Init some basic properties about this frame (keep sample rate and # channels the same as the original reader for now)
int samples_in_frame = Frame::GetSamplesPerFrame(number, target, reader->info.sample_rate, reader->info.channels);
int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(number), target, reader->info.sample_rate, reader->info.channels);

try {
// Debug output
Expand Down Expand Up @@ -427,7 +429,7 @@ std::shared_ptr<Frame> FrameMapper::GetFrame(int64_t requested_frame)

// Get # of channels in the actual frame
int channels_in_frame = mapped_frame->GetAudioChannelsCount();
int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, target, mapped_frame->SampleRate(), channels_in_frame);
int samples_in_frame = Frame::GetSamplesPerFrame(ConvPositon(frame_number), target, mapped_frame->SampleRate(), channels_in_frame);

// Determine if mapped frame is identical to source frame
// including audio sample distribution according to mapped.Samples,
Expand Down Expand Up @@ -689,6 +691,7 @@ Json::Value FrameMapper::JsonValue() const {
// Create root json object
Json::Value root = ReaderBase::JsonValue(); // get parent properties
root["type"] = "FrameMapper";
root["position"] = position;

// return JsonValue
return root;
Expand Down Expand Up @@ -717,6 +720,10 @@ void FrameMapper::SetJsonValue(const Json::Value root) {
// Set parent data
ReaderBase::SetJsonValue(root);

if(!root["position"].isNull()){
position = root["position"].asDouble();
}

// Re-Open path, and re-init everything (if needed)
if (reader) {

Expand All @@ -726,7 +733,7 @@ void FrameMapper::SetJsonValue(const Json::Value root) {
}

// Change frame rate or audio mapping details
void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout, float clipPosition, float clipStart)
{
ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ChangeMapping", "target_fps.num", target_fps.num, "target_fps.den", target_fps.den, "target_pulldown", target_pulldown, "target_sample_rate", target_sample_rate, "target_channels", target_channels, "target_channel_layout", target_channel_layout);

Expand All @@ -745,6 +752,9 @@ void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldow
info.channels = target_channels;
info.channel_layout = target_channel_layout;

position = clipPosition;
start = clipStart;

// Clear cache
final_cache.Clear();

Expand Down Expand Up @@ -828,7 +838,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t orig
}

// Update total samples & input frame size (due to bigger or smaller data types)
total_frame_samples = Frame::GetSamplesPerFrame(frame->number, target, info.sample_rate, info.channels);
total_frame_samples = Frame::GetSamplesPerFrame(ConvPositon(frame->number), target, info.sample_rate, info.channels);

ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::ResampleMappedAudio (adjust # of samples)", "total_frame_samples", total_frame_samples, "info.sample_rate", info.sample_rate, "sample_rate_in_frame", sample_rate_in_frame, "info.channels", info.channels, "channels_in_frame", channels_in_frame, "original_frame_number", original_frame_number);

Expand Down Expand Up @@ -937,3 +947,14 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t orig
delete[] resampled_samples;
resampled_samples = NULL;
}

int64_t FrameMapper::ConvPositon(int64_t clip_frame_number){

int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1;
int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1;

int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;

///std::cout << "Conv Position " << round(position * info.fps.ToDouble()) << " position: " << position << " info::fps: " << info.fps.ToDouble() << std::endl;
return frame_number;
}
10 changes: 6 additions & 4 deletions src/Timeline.cpp
Expand Up @@ -360,14 +360,14 @@ void Timeline::apply_mapper_to_clip(Clip* clip)
} else {

// Create a new FrameMapper to wrap the current reader
FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout);
FrameMapper* mapper = new FrameMapper(clip->Reader(), info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start());
allocated_frame_mappers.insert(mapper);
clip_reader = (ReaderBase*) mapper;
}

// Update the mapping
FrameMapper* clip_mapped_reader = (FrameMapper*) clip_reader;
clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout);
clip_mapped_reader->ChangeMapping(info.fps, PULLDOWN_NONE, info.sample_rate, info.channels, info.channel_layout, clip->Position(), clip->Start());

// Update clip reader
clip->Reader(clip_reader);
Expand Down Expand Up @@ -546,11 +546,12 @@ void Timeline::add_layer(std::shared_ptr<Frame> new_frame, Clip* source_clip, in
// Currently, the ResampleContext sometimes leaves behind a few samples for the next call, and the
// number of samples returned is variable... and does not match the number expected.
// This is a crude solution at best. =)
if (new_frame->GetAudioSamplesCount() != source_frame->GetAudioSamplesCount())
if (new_frame->GetAudioSamplesCount() != source_frame->GetAudioSamplesCount()){
// Force timeline frame to match the source frame
#pragma omp critical (T_addLayer)
new_frame->ResizeAudio(info.channels, source_frame->GetAudioSamplesCount(), info.sample_rate, info.channel_layout);

new_frame->ResizeAudio(info.channels, source_frame->GetAudioSamplesCount(), info.sample_rate, info.channel_layout);
}
// Copy audio samples (and set initial volume). Mix samples with existing audio samples. The gains are added together, to
// be sure to set the gain's correctly, so the sum does not exceed 1.0 (of audio distortion will happen).
#pragma omp critical (T_addLayer)
Expand Down Expand Up @@ -682,6 +683,7 @@ bool Timeline::isEqual(double a, double b)
// Get an openshot::Frame object for a specific frame number of this reader.
std::shared_ptr<Frame> Timeline::GetFrame(int64_t requested_frame)
{

// Adjust out of bounds frame number
if (requested_frame < 1)
requested_frame = 1;
Expand Down

0 comments on commit a410941

Please sign in to comment.