Skip to content

Commit d104664

Browse files
committed
Fixed an issue with reversed resampled audio in FrameMapper, which caused lots of clicks / seams between frames... Also, added a new overload to GetInterleavedAudioSamples, to reverse the samples before returning the float* array. Essentially, the FrameMapper is now aware of it's parent clip, and especially the 'time' keyframe, if the audio is in the forward or reverse direction. Also fixed a memory leak in time remapping.
1 parent 59d46e5 commit d104664

File tree

5 files changed

+47
-63
lines changed

5 files changed

+47
-63
lines changed

src/Clip.cpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,6 @@ void Clip::reverse_buffer(juce::AudioBuffer<float>* buffer)
510510
buffer->addFrom(channel, 0, reversed->getReadPointer(channel), number_of_samples, 1.0f);
511511

512512
delete reversed;
513-
reversed = nullptr;
514513
}
515514

516515
// Adjust the audio and image of a time mapped frame
@@ -569,7 +568,7 @@ void Clip::apply_timemapping(std::shared_ptr<Frame> frame)
569568
frame->AddAudioSilence(target_sample_count);
570569
return;
571570
}
572-
571+
573572
// Allocate a new sample buffer for these delta frames
574573
source_samples = new juce::AudioBuffer<float>(Reader()->info.channels, source_sample_count);
575574
source_samples->clear();
@@ -578,26 +577,23 @@ void Clip::apply_timemapping(std::shared_ptr<Frame> frame)
578577
int remaining_samples = source_sample_count;
579578
int source_pos = 0;
580579
while (remaining_samples > 0) {
581-
int frame_sample_count = GetOrCreateFrame(location.frame, false)->GetAudioSamplesCount() - location.sample_start;
580+
std::shared_ptr<Frame> source_frame = GetOrCreateFrame(location.frame, false);
581+
int frame_sample_count = source_frame->GetAudioSamplesCount() - location.sample_start;
582582

583583
if (frame_sample_count == 0) {
584584
// No samples found in source frame (fill with silence)
585-
int expected_frame_sample_count = Frame::GetSamplesPerFrame(clip_frame_number, Reader()->info.fps, Reader()->info.sample_rate, Reader()->info.channels);
586-
source_samples->setSize(Reader()->info.channels, expected_frame_sample_count, false, true, false);
587-
source_samples->clear();
588585
if (is_increasing) {
589586
location.frame++;
590587
} else {
591588
location.frame--;
592589
}
593590
location.sample_start = 0;
594-
remaining_samples = 0;
595591
break;
596592
}
597593
if (remaining_samples - frame_sample_count >= 0) {
598594
// Use all frame samples & increment location
599-
for (int channel = 0; channel < Reader()->info.channels; channel++) {
600-
source_samples->addFrom(channel, source_pos, GetOrCreateFrame(location.frame, false)->GetAudioSamples(channel, !is_increasing) + location.sample_start, frame_sample_count, 1.0f);
595+
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
596+
source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.sample_start, frame_sample_count, 1.0f);
601597
}
602598
if (is_increasing) {
603599
location.frame++;
@@ -608,10 +604,10 @@ void Clip::apply_timemapping(std::shared_ptr<Frame> frame)
608604
remaining_samples -= frame_sample_count;
609605
source_pos += frame_sample_count;
610606

611-
} else if (remaining_samples - frame_sample_count < 0) {
607+
} else {
612608
// Use just what is needed (and reverse samples)
613-
for (int channel = 0; channel < Reader()->info.channels; channel++) {
614-
source_samples->addFrom(channel, source_pos, GetOrCreateFrame(location.frame, false)->GetAudioSamples(channel, !is_increasing) + location.sample_start, remaining_samples, 1.0f);
609+
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
610+
source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.sample_start, remaining_samples, 1.0f);
615611
}
616612
location.sample_start += remaining_samples;
617613
remaining_samples = 0;
@@ -620,31 +616,33 @@ void Clip::apply_timemapping(std::shared_ptr<Frame> frame)
620616

621617
}
622618

623-
// Resample audio (if needed)
619+
// Resize audio for current frame object + fill with silence
620+
// We are fixing to clobber this with actual audio data (possibly resampled)
621+
frame->AddAudioSilence(target_sample_count);
622+
624623
if (source_samples->getNumSamples() != target_sample_count) {
624+
// Resample audio (if needed)
625625
resampler->SetBuffer(source_samples, fabs(delta));
626626

627627
// Resample the data
628628
juce::AudioBuffer<float> *resampled_buffer = resampler->GetResampledBuffer();
629629

630630
// Fill the frame with resampled data
631-
frame->ResizeAudio(Reader()->info.channels, target_sample_count, Reader()->info.sample_rate, Reader()->info.channel_layout);
632631
for (int channel = 0; channel < Reader()->info.channels; channel++) {
633632
// Add new (slower) samples, to the frame object
634633
frame->AddAudio(true, channel, 0, resampled_buffer->getReadPointer(channel, 0), target_sample_count, 1.0f);
635634
}
636-
637-
// Clean up
638-
resampled_buffer = nullptr;
639635
} else {
640636
// Fill the frame
641-
frame->ResizeAudio(Reader()->info.channels, target_sample_count, Reader()->info.sample_rate, Reader()->info.channel_layout);
642637
for (int channel = 0; channel < Reader()->info.channels; channel++) {
643638
// Add new (slower) samples, to the frame object
644639
frame->AddAudio(true, channel, 0, source_samples->getReadPointer(channel, 0), target_sample_count, 1.0f);
645640
}
646641
}
647642

643+
// Clean up
644+
delete source_samples;
645+
648646
// Set previous location
649647
previous_location = location;
650648
}

src/Frame.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,13 +372,18 @@ float* Frame::GetPlanarAudioSamples(int new_sample_rate, AudioResampler* resampl
372372

373373

374374
// Get an array of sample data (all channels interleaved together), using any sample rate
375-
float* Frame::GetInterleavedAudioSamples(int new_sample_rate, AudioResampler* resampler, int* sample_count)
375+
float* Frame::GetInterleavedAudioSamples(int new_sample_rate, AudioResampler* resampler, int* sample_count, bool reverse)
376376
{
377377
float *output = NULL;
378378
juce::AudioBuffer<float> *buffer(audio.get());
379379
int num_of_channels = audio->getNumChannels();
380380
int num_of_samples = GetAudioSamplesCount();
381381

382+
if (reverse) {
383+
// Reverse audio samples (if needed)
384+
buffer->reverse(0, buffer->getNumSamples());
385+
}
386+
382387
// Resample to new sample rate (if needed)
383388
if (new_sample_rate != sample_rate && resampler)
384389
{

src/Frame.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ namespace openshot
190190
float* GetAudioSamples(int channel, bool reverse=false);
191191

192192
/// Get an array of sample data (all channels interleaved together), using any sample rate
193-
float* GetInterleavedAudioSamples(int new_sample_rate, openshot::AudioResampler* resampler, int* sample_count);
193+
float* GetInterleavedAudioSamples(int new_sample_rate, openshot::AudioResampler* resampler, int* sample_count, bool reverse=false);
194194

195195
// Get a planar array of sample data, using any sample rate
196196
float* GetPlanarAudioSamples(int new_sample_rate, openshot::AudioResampler* resampler, int* sample_count);

src/FrameMapper.cpp

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,14 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t orig
831831
// Recalculate mappings
832832
Init();
833833

834+
// Determine direction of parent clip at this frame (forward or reverse direction)
835+
// This is important for reversing audio in our resampler, for smooth reversed audio.
836+
Clip *parent = (Clip *) ParentClip();
837+
bool is_increasing = true;
838+
if (parent) {
839+
is_increasing = parent->time.IsIncreasing(original_frame_number);
840+
}
841+
834842
// Init audio buffers / variables
835843
int total_frame_samples = 0;
836844
int channels_in_frame = frame->GetAudioChannelsCount();
@@ -849,7 +857,7 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t orig
849857
// Get audio sample array
850858
float* frame_samples_float = NULL;
851859
// Get samples interleaved together (c1 c2 c1 c2 c1 c2)
852-
frame_samples_float = frame->GetInterleavedAudioSamples(sample_rate_in_frame, NULL, &samples_in_frame);
860+
frame_samples_float = frame->GetInterleavedAudioSamples(sample_rate_in_frame, NULL, &samples_in_frame, !is_increasing);
853861

854862
// Calculate total samples
855863
total_frame_samples = samples_in_frame * channels_in_frame;
@@ -875,21 +883,10 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t orig
875883
frame_samples[s] = conv;
876884
}
877885

878-
879886
// Deallocate float array
880887
delete[] frame_samples_float;
881888
frame_samples_float = NULL;
882889

883-
ZmqLogger::Instance()->AppendDebugMethod(
884-
"FrameMapper::ResampleMappedAudio (got sample data from frame)",
885-
"frame->number", frame->number,
886-
"total_frame_samples", total_frame_samples,
887-
"target channels", info.channels,
888-
"channels_in_frame", channels_in_frame,
889-
"target sample_rate", info.sample_rate,
890-
"samples_in_frame", samples_in_frame);
891-
892-
893890
// Create input frame (and allocate arrays)
894891
AVFrame *audio_frame = AV_ALLOCATE_FRAME();
895892
AV_RESET_FRAME(audio_frame);
@@ -911,30 +908,12 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t orig
911908
// Update total samples & input frame size (due to bigger or smaller data types)
912909
total_frame_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame->number), target, info.sample_rate, info.channels);
913910

914-
ZmqLogger::Instance()->AppendDebugMethod(
915-
"FrameMapper::ResampleMappedAudio (adjust # of samples)",
916-
"total_frame_samples", total_frame_samples,
917-
"info.sample_rate", info.sample_rate,
918-
"sample_rate_in_frame", sample_rate_in_frame,
919-
"info.channels", info.channels,
920-
"channels_in_frame", channels_in_frame,
921-
"original_frame_number", original_frame_number);
922-
923911
// Create output frame (and allocate arrays)
924912
AVFrame *audio_converted = AV_ALLOCATE_FRAME();
925913
AV_RESET_FRAME(audio_converted);
926914
audio_converted->nb_samples = total_frame_samples;
927915
av_samples_alloc(audio_converted->data, audio_converted->linesize, info.channels, total_frame_samples, AV_SAMPLE_FMT_S16, 0);
928916

929-
ZmqLogger::Instance()->AppendDebugMethod(
930-
"FrameMapper::ResampleMappedAudio (preparing for resample)",
931-
"in_sample_fmt", AV_SAMPLE_FMT_S16,
932-
"out_sample_fmt", AV_SAMPLE_FMT_S16,
933-
"in_sample_rate", sample_rate_in_frame,
934-
"out_sample_rate", info.sample_rate,
935-
"in_channels", channels_in_frame,
936-
"out_channels", info.channels);
937-
938917
int nb_samples = 0;
939918

940919
// setup resample context
@@ -1023,11 +1002,6 @@ void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t orig
10231002

10241003
// Add samples to frame for this channel
10251004
frame->AddAudio(true, channel_filter, 0, channel_buffer, position, 1.0f);
1026-
1027-
ZmqLogger::Instance()->AppendDebugMethod(
1028-
"FrameMapper::ResampleMappedAudio (Add audio to channel)",
1029-
"number of samples", position,
1030-
"channel_filter", channel_filter);
10311005
}
10321006

10331007
// Update frame's audio meta data

tests/Clip.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "DummyReader.h"
2424
#include "Enums.h"
2525
#include "Exceptions.h"
26+
#include "FFmpegReader.h"
2627
#include "Frame.h"
2728
#include "Fraction.h"
2829
#include "FrameMapper.h"
@@ -469,8 +470,16 @@ TEST_CASE( "resample_audio_48000_to_41000_reverse", "[libopenshot][clip]" )
469470
// Create a reader
470471
std::stringstream path;
471472
path << TEST_MEDIA_PATH << "sintel_trailer-720p.mp4";
472-
Clip clip(path.str());
473-
int original_video_length = clip.Reader()->info.video_length;
473+
openshot::FFmpegReader reader(path.str(), true);
474+
475+
// Map to 24 fps, 2 channels stereo, 44100 sample rate
476+
FrameMapper map(&reader, Fraction(24,1), PULLDOWN_NONE, 44100, 2, LAYOUT_STEREO);
477+
map.Open();
478+
479+
Clip clip;
480+
clip.Reader(&map);
481+
clip.Open();
482+
int original_video_length = clip.Reader()->info.video_length + 1;
474483

475484
clip.Position(0.0);
476485
clip.Start(0.0);
@@ -479,18 +488,14 @@ TEST_CASE( "resample_audio_48000_to_41000_reverse", "[libopenshot][clip]" )
479488
clip.time.AddPoint(1, original_video_length, openshot::LINEAR);
480489
clip.time.AddPoint(original_video_length, 1.0, openshot::LINEAR);
481490

482-
// Map to 24 fps, 2 channels stereo, 44100 sample rate
483-
FrameMapper map(&clip, Fraction(24,1), PULLDOWN_NONE, 44100, 2, LAYOUT_STEREO);
484-
map.Open();
485-
486491
// Loop again through frames
487492
// Time-remapping should start over (detect a gap)
488493
for (int64_t frame = 1; frame < 100; frame++) {
489494
int expected_sample_count = Frame::GetSamplesPerFrame(frame, map.info.fps,
490495
map.info.sample_rate,
491496
map.info.channels);
492497

493-
std::shared_ptr<Frame> f = map.GetFrame(frame);
498+
std::shared_ptr<Frame> f = clip.GetFrame(frame);
494499
if (expected_sample_count != f->GetAudioSamplesCount()) {
495500
CHECK(expected_sample_count == f->GetAudioSamplesCount());
496501
}
@@ -506,12 +511,14 @@ TEST_CASE( "resample_audio_48000_to_41000_reverse", "[libopenshot][clip]" )
506511
map.info.sample_rate,
507512
map.info.channels);
508513

509-
std::shared_ptr<Frame> f = map.GetFrame(frame);
514+
std::shared_ptr<Frame> f = clip.GetFrame(frame);
510515
if (expected_sample_count != f->GetAudioSamplesCount()) {
511516
CHECK(expected_sample_count == f->GetAudioSamplesCount());
512517
}
513518
}
514519

515520
// Close mapper
516521
map.Close();
522+
reader.Close();
523+
clip.Close();
517524
}

0 commit comments

Comments
 (0)