Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f60d665
Bumping version to 0.5.0, SO 28, this is a major new release of libop…
jonoomph Jun 12, 2025
c6720bb
Lowering version required for libopenshot-audio, since technically it…
jonoomph Jun 12, 2025
515c4ff
Improving Tracker effect to better track occluded objects, follow obj…
jonoomph Jun 15, 2025
055975a
Improving Tracker and Object Detector to include effect ID in the tra…
jonoomph Jun 17, 2025
22cd563
Removing SkipEmptyParts from modified ObjectDetection.cpp code (old Q…
jonoomph Jun 18, 2025
9cbfc80
Fixing Tracker and Object Detection effect to not crash when camera q…
jonoomph Jul 8, 2025
713cf39
Updating godot git ref
jonoomph Jul 8, 2025
6cea273
Fix timeline cache when updating Clips with ApplyJsonDiff (old and ne…
jonoomph Jul 8, 2025
e43f875
Small refactor to assign Clip and Effect ids in base class
jonoomph Aug 11, 2025
4613b52
Fixing logic to set Tracker JSON (Tracker was not updating the box va…
jonoomph Aug 11, 2025
981e18d
Fixing logic to set ObjectDetection JSON (Detector was not updating t…
jonoomph Aug 11, 2025
523fb5a
Massive improvement to object detection sort logic, to keep IDs more …
jonoomph Aug 12, 2025
adff81f
Fixing protobuf loading bug, preventing tracker and object detection …
jonoomph Aug 12, 2025
dd62f5b
Protecting clip GetFrame from crash due to null frame, then setting t…
jonoomph Sep 5, 2025
fbef1bc
Protect the video and audio codec name discovery flow, to prevent cra…
jonoomph Sep 7, 2025
f68d184
Improve GetMinFrame / GetMaxFrame functions for a timeline, to be inc…
jonoomph Sep 8, 2025
f98da72
Improve spherical projection effect to have better quality and separa…
jonoomph Sep 8, 2025
c23c0d1
Fixing regression in SphericalProjection.cpp effect - causing a unit …
jonoomph Sep 9, 2025
a90b4d6
Improving fish eye support for SphericalProjection effect (4 types of…
jonoomph Sep 10, 2025
a07fe18
Adding 5 Spherical test images (needed for unit tests)
jonoomph Sep 11, 2025
79846ea
Adding new Analog Tap effect: Vintage home video wobble, bleed, and g…
jonoomph Sep 11, 2025
9ca7e07
Adding more SphericalProjection unit tests - still a WIP
jonoomph Sep 12, 2025
1533b6a
Fixing SphericalEffect.cpp tests
jonoomph Sep 12, 2025
0570ad0
Large timeline clean-up, speed-up, and fix concurrency bugs:
jonoomph Sep 12, 2025
d77f3e5
Improving performance on Clip class:
jonoomph Sep 12, 2025
f2a5bfb
Fixed AnalogTape tests and an unused Clip test line
jonoomph Sep 12, 2025
a326f54
Fix bug with Wave effect that can cause colored bands to appear in ce…
jonoomph Sep 12, 2025
b94dcac
Adding Benchmark executable to assist with performance testing and co…
jonoomph Sep 12, 2025
523ef17
Adding composite/blend modes to libopenshot:
jonoomph Sep 12, 2025
a66727a
Expanding Clip unit tests to include all composite blend modes availa…
jonoomph Sep 12, 2025
fa4f44d
Fixing small build error on benchmark includes
jonoomph Sep 13, 2025
021c6ec
Adding unit tests to validate FFmpegReader, Clip, and Timeline frame …
jonoomph Sep 13, 2025
3723fbd
Fixing regression on Mac and Windows builds for Clip blend modes (col…
jonoomph Sep 13, 2025
01a4d9f
Fixing regression/bug in video cache thread - to correctly reset cach…
jonoomph Sep 15, 2025
c165eca
Improving AudioWaveformer to be able to correctly generate waveforms …
jonoomph Sep 22, 2025
0c15c16
Adding new reversed time curve unit test, to verify 230,000 samples a…
jonoomph Sep 26, 2025
4cef4da
Fixing a bug in Keyframe that caused the GetDelta() function to retur…
jonoomph Sep 26, 2025
9a26288
A few small refactors of clip unit tests
jonoomph Oct 1, 2025
fd29527
Improving audio directionality, with new function: SetAudioDirection(…
jonoomph Oct 11, 2025
7e29fc0
Improving audio directionality, with new function: SetAudioDirection(…
jonoomph Oct 11, 2025
0932af2
Fixing race condition on unit test for AnalogTape (when comparing fra…
jonoomph Oct 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ For more information, please visit <http://www.openshot.org/>.
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules")

################ PROJECT VERSION ####################
set(PROJECT_VERSION_FULL "0.4.0")
set(PROJECT_SO_VERSION 27)
set(PROJECT_VERSION_FULL "0.5.0")
set(PROJECT_SO_VERSION 28)

# Remove the dash and anything following, to get the #.#.# version for project()
STRING(REGEX REPLACE "\-.*$" "" VERSION_NUM "${PROJECT_VERSION_FULL}")
Expand Down
Binary file added examples/animation.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/eq_sphere_plane.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/fisheye_plane_equidistant.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/fisheye_plane_equisolid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/fisheye_plane_orthographic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/fisheye_plane_stereographic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion external/godot-cpp
Submodule godot-cpp updated 125 files
257 changes: 157 additions & 100 deletions src/AudioWaveformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@

#include "AudioWaveformer.h"

#include <cmath>

#include <algorithm>
#include <vector>

#include "Clip.h"


using namespace std;
using namespace openshot;
Expand All @@ -31,104 +38,154 @@ AudioWaveformer::~AudioWaveformer()

// Extract audio samples from any ReaderBase class
AudioWaveformData AudioWaveformer::ExtractSamples(int channel, int num_per_second, bool normalize) {
AudioWaveformData data;

if (reader) {
// Open reader (if needed)
bool does_reader_have_video = reader->info.has_video;
if (!reader->IsOpen()) {
reader->Open();
}
// Disable video for faster processing
reader->info.has_video = false;

int sample_rate = reader->info.sample_rate;
int sample_divisor = sample_rate / num_per_second;
int total_samples = num_per_second * (reader->info.duration + 1.0);
int extracted_index = 0;

// Force output to zero elements for non-audio readers
if (!reader->info.has_audio) {
total_samples = 0;
}

// Resize and clear audio buffers
data.resize(total_samples);
data.zero(total_samples);

// Bail out, if no samples needed
if (total_samples == 0 || reader->info.channels == 0) {
return data;
}

// Loop through all frames
int sample_index = 0;
float samples_max = 0.0;
float chunk_max = 0.0;
float chunk_squared_sum = 0.0;

// How many channels are we using
int channel_count = 1;
if (channel == -1) {
channel_count = reader->info.channels;
}

for (auto f = 1; f <= reader->info.video_length; f++) {
// Get next frame
shared_ptr<openshot::Frame> frame = reader->GetFrame(f);

// Cache channels for this frame, to reduce # of calls to frame->GetAudioSamples
float* channels[channel_count];
for (auto channel_index = 0; channel_index < reader->info.channels; channel_index++) {
if (channel == channel_index || channel == -1) {
channels[channel_index] = frame->GetAudioSamples(channel_index);
}
}

// Get sample value from a specific channel (or all channels)
for (auto s = 0; s < frame->GetAudioSamplesCount(); s++) {
for (auto channel_index = 0; channel_index < reader->info.channels; channel_index++) {
if (channel == channel_index || channel == -1) {
float *samples = channels[channel_index];
float rms_sample_value = std::sqrt(samples[s] * samples[s]);

// Accumulate sample averages
chunk_squared_sum += rms_sample_value;
chunk_max = std::max(chunk_max, rms_sample_value);
}
}

sample_index += 1;

// Cut-off reached
if (sample_index % sample_divisor == 0) {
float avg_squared_sum = chunk_squared_sum / (sample_divisor * channel_count);
data.max_samples[extracted_index] = chunk_max;
data.rms_samples[extracted_index] = avg_squared_sum;
extracted_index++;

// Track max/min values
samples_max = std::max(samples_max, chunk_max);

// reset sample total and index
sample_index = 0;
chunk_max = 0.0;
chunk_squared_sum = 0.0;
}
}
}

// Scale all values to the -1 to +1 range (regardless of how small or how large the
// original audio sample values are)
if (normalize && samples_max > 0.0) {
float scale = 1.0f / samples_max;
data.scale(total_samples, scale);
}

// Resume previous has_video value
reader->info.has_video = does_reader_have_video;
}


return data;
AudioWaveformData data;

if (!reader || num_per_second <= 0) {
return data;
}

// Open reader (if needed)
bool does_reader_have_video = reader->info.has_video;
if (!reader->IsOpen()) {
reader->Open();
}
// Disable video for faster processing
reader->info.has_video = false;

int sample_rate = reader->info.sample_rate;
if (sample_rate <= 0) {
sample_rate = num_per_second;
}
int sample_divisor = sample_rate / num_per_second;
if (sample_divisor <= 0) {
sample_divisor = 1;
}

// Determine length of video frames (for waveform)
int64_t reader_video_length = reader->info.video_length;
if (const auto *clip = dynamic_cast<Clip*>(reader)) {
// If Clip-based reader, and time keyframes present
if (clip->time.GetCount() > 1) {
reader_video_length = clip->time.GetLength();
}
}
if (reader_video_length < 0) {
reader_video_length = 0;
}
float reader_duration = reader->info.duration;
double fps_value = reader->info.fps.ToDouble();
float frames_duration = 0.0f;
if (reader_video_length > 0 && fps_value > 0.0) {
frames_duration = static_cast<float>(reader_video_length / fps_value);
}
const bool has_source_length = reader->info.video_length > 0;
const bool frames_extended = has_source_length && reader_video_length > reader->info.video_length;
if (reader_duration <= 0.0f) {
reader_duration = frames_duration;
} else if ((frames_extended || !has_source_length) && frames_duration > reader_duration + 1e-4f) {
reader_duration = frames_duration;
}
if (reader_duration < 0.0f) {
reader_duration = 0.0f;
}

if (!reader->info.has_audio) {
reader->info.has_video = does_reader_have_video;
return data;
}

int total_samples = static_cast<int>(std::ceil(reader_duration * num_per_second));
if (total_samples <= 0 || reader->info.channels == 0) {
reader->info.has_video = does_reader_have_video;
return data;
}

if (channel != -1 && (channel < 0 || channel >= reader->info.channels)) {
reader->info.has_video = does_reader_have_video;
return data;
}

// Resize and clear audio buffers
data.resize(total_samples);
data.zero(total_samples);

int extracted_index = 0;
int sample_index = 0;
float samples_max = 0.0f;
float chunk_max = 0.0f;
float chunk_squared_sum = 0.0f;

int channel_count = (channel == -1) ? reader->info.channels : 1;
std::vector<float*> channels(reader->info.channels, nullptr);

for (int64_t f = 1; f <= reader_video_length && extracted_index < total_samples; f++) {
std::shared_ptr<openshot::Frame> frame = reader->GetFrame(f);

for (int channel_index = 0; channel_index < reader->info.channels; channel_index++) {
if (channel == channel_index || channel == -1) {
channels[channel_index] = frame->GetAudioSamples(channel_index);
}
}

for (int s = 0; s < frame->GetAudioSamplesCount(); s++) {
for (int channel_index = 0; channel_index < reader->info.channels; channel_index++) {
if (channel == channel_index || channel == -1) {
float *samples = channels[channel_index];
if (!samples) {
continue;
}
float rms_sample_value = std::sqrt(samples[s] * samples[s]);

chunk_squared_sum += rms_sample_value;
chunk_max = std::max(chunk_max, rms_sample_value);
}
}

sample_index += 1;

if (sample_index % sample_divisor == 0) {
float avg_squared_sum = 0.0f;
if (channel_count > 0) {
avg_squared_sum = chunk_squared_sum / static_cast<float>(sample_divisor * channel_count);
}

if (extracted_index < total_samples) {
data.max_samples[extracted_index] = chunk_max;
data.rms_samples[extracted_index] = avg_squared_sum;
samples_max = std::max(samples_max, chunk_max);
extracted_index++;
}

sample_index = 0;
chunk_max = 0.0f;
chunk_squared_sum = 0.0f;

if (extracted_index >= total_samples) {
break;
}
}
}
}

if (sample_index > 0 && extracted_index < total_samples) {
float avg_squared_sum = 0.0f;
if (channel_count > 0) {
avg_squared_sum = chunk_squared_sum / static_cast<float>(sample_index * channel_count);
}

data.max_samples[extracted_index] = chunk_max;
data.rms_samples[extracted_index] = avg_squared_sum;
samples_max = std::max(samples_max, chunk_max);
extracted_index++;
}

if (normalize && samples_max > 0.0f) {
float scale = 1.0f / samples_max;
data.scale(total_samples, scale);
}

reader->info.has_video = does_reader_have_video;

return data;
}

1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ set(EFFECTS_SOURCES
effects/Deinterlace.cpp
effects/Hue.cpp
effects/LensFlare.cpp
effects/AnalogTape.cpp
effects/Mask.cpp
effects/Negate.cpp
effects/Pixelate.cpp
Expand Down
Loading