Skip to content

Commit 2f45a4e

Browse files
authored
Adding new audio mixing enum, to allow for no automatic audio mixing, average mixing (where all overlapping clips average to 100% audio), or reduce mixing (where clips overlapping clips are all reduced by a constant value to reduce popping). (#131)
1 parent 9ae61e5 commit 2f45a4e

File tree

4 files changed

+40
-4
lines changed

4 files changed

+40
-4
lines changed

include/Clip.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ namespace openshot {
155155
ScaleType scale; ///< The scale determines how a clip should be resized to fit it's parent
156156
AnchorType anchor; ///< The anchor determines what parent a clip should snap to
157157
FrameDisplayType display; ///< The format to display the frame number (if any)
158+
VolumeMixType mixing; ///< What strategy should be followed when mixing audio with other clips
158159

159160
/// Default Constructor
160161
Clip();

include/Enums.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,13 @@ namespace openshot
6969
FRAME_DISPLAY_TIMELINE, ///< Display the timeline's frame number
7070
FRAME_DISPLAY_BOTH ///< Display both the clip's and timeline's frame number
7171
};
72+
73+
/// This enumeration determines the strategy when mixing audio with other clips.
74+
enum VolumeMixType
75+
{
76+
VOLUME_MIX_NONE, ///< Do not apply any volume mixing adjustments. Just add the samples together.
77+
VOLUME_MIX_AVERAGE, ///< Evenly divide the overlapping clips volume keyframes, so that the sum does not exceed 100%
78+
VOLUME_MIX_REDUCE ///< Reduce volume by about %25, and then mix (louder, but could cause pops if the sum exceeds 100%)
79+
};
7280
}
7381
#endif

src/Clip.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ void Clip::init_settings()
4141
scale = SCALE_FIT;
4242
anchor = ANCHOR_CANVAS;
4343
display = FRAME_DISPLAY_NONE;
44+
mixing = VOLUME_MIX_NONE;
4445
waveform = false;
4546
previous_properties = "";
4647

@@ -694,6 +695,7 @@ string Clip::PropertiesJSON(int64_t requested_frame) {
694695
root["gravity"] = add_property_json("Gravity", gravity, "int", "", NULL, 0, 8, false, requested_frame);
695696
root["scale"] = add_property_json("Scale", scale, "int", "", NULL, 0, 3, false, requested_frame);
696697
root["display"] = add_property_json("Frame Number", display, "int", "", NULL, 0, 3, false, requested_frame);
698+
root["mixing"] = add_property_json("Volume Mixing", mixing, "int", "", NULL, 0, 2, false, requested_frame);
697699
root["waveform"] = add_property_json("Waveform", waveform, "int", "", NULL, 0, 1, false, requested_frame);
698700

699701
// Add gravity choices (dropdown style)
@@ -719,6 +721,11 @@ string Clip::PropertiesJSON(int64_t requested_frame) {
719721
root["display"]["choices"].append(add_property_choice_json("Timeline", FRAME_DISPLAY_TIMELINE, display));
720722
root["display"]["choices"].append(add_property_choice_json("Both", FRAME_DISPLAY_BOTH, display));
721723

724+
// Add volume mixing choices (dropdown style)
725+
root["mixing"]["choices"].append(add_property_choice_json("None", VOLUME_MIX_NONE, mixing));
726+
root["mixing"]["choices"].append(add_property_choice_json("Average", VOLUME_MIX_AVERAGE, mixing));
727+
root["mixing"]["choices"].append(add_property_choice_json("Reduce", VOLUME_MIX_REDUCE, mixing));
728+
722729
// Add waveform choices (dropdown style)
723730
root["waveform"]["choices"].append(add_property_choice_json("Yes", true, waveform));
724731
root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform));
@@ -758,6 +765,7 @@ Json::Value Clip::JsonValue() {
758765
root["scale"] = scale;
759766
root["anchor"] = anchor;
760767
root["display"] = display;
768+
root["mixing"] = mixing;
761769
root["waveform"] = waveform;
762770
root["scale_x"] = scale_x.JsonValue();
763771
root["scale_y"] = scale_y.JsonValue();
@@ -844,6 +852,8 @@ void Clip::SetJsonValue(Json::Value root) {
844852
anchor = (AnchorType) root["anchor"].asInt();
845853
if (!root["display"].isNull())
846854
display = (FrameDisplayType) root["display"].asInt();
855+
if (!root["mixing"].isNull())
856+
mixing = (VolumeMixType) root["mixing"].asInt();
847857
if (!root["waveform"].isNull())
848858
waveform = root["waveform"].asBool();
849859
if (!root["scale_x"].isNull())

src/Timeline.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,24 @@ void Timeline::add_layer(std::shared_ptr<Frame> new_frame, Clip* source_clip, in
294294
if (source_frame->GetAudioChannelsCount() == info.channels && source_clip->has_audio.GetInt(clip_frame_number) != 0)
295295
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++)
296296
{
297-
float previous_volume = source_clip->volume.GetValue(clip_frame_number - 1) / fmaxf(max_volume, 1.0); // previous frame's percentage of volume (0 to 1)
298-
float volume = source_clip->volume.GetValue(clip_frame_number) / fmaxf(max_volume, 1.0); // percentage of volume (0 to 1)
297+
// Get volume from previous frame and this frame
298+
float previous_volume = source_clip->volume.GetValue(clip_frame_number - 1);
299+
float volume = source_clip->volume.GetValue(clip_frame_number);
299300
int channel_filter = source_clip->channel_filter.GetInt(clip_frame_number); // optional channel to filter (if not -1)
300301
int channel_mapping = source_clip->channel_mapping.GetInt(clip_frame_number); // optional channel to map this channel to (if not -1)
301302

303+
// Apply volume mixing strategy
304+
if (source_clip->mixing == VOLUME_MIX_AVERAGE && max_volume > 1.0) {
305+
// Don't allow this clip to exceed 100% (divide volume equally between all overlapping clips with volume
306+
previous_volume = previous_volume / max_volume;
307+
volume = volume / max_volume;
308+
}
309+
else if (source_clip->mixing == VOLUME_MIX_REDUCE && max_volume > 1.0) {
310+
// Reduce clip volume by a bit, hoping it will prevent exceeding 100% (but it is very possible it will)
311+
previous_volume = previous_volume * 0.77;
312+
volume = volume * 0.77;
313+
}
314+
302315
// If channel filter enabled, check for correct channel (and skip non-matching channels)
303316
if (channel_filter != -1 && channel_filter != channel)
304317
continue; // skip to next channel
@@ -760,14 +773,18 @@ std::shared_ptr<Frame> Timeline::GetFrame(int64_t requested_frame)
760773
long nearby_clip_start_frame = (nearby_clip->Start() * info.fps.ToDouble()) + 1;
761774
long nearby_clip_frame_number = frame_number - nearby_clip_start_position + nearby_clip_start_frame;
762775

776+
// Determine if top clip
763777
if (clip->Id() != nearby_clip->Id() && clip->Layer() == nearby_clip->Layer() &&
764778
nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number &&
765779
nearby_clip_start_position > clip_start_position && is_top_clip == true) {
766780
is_top_clip = false;
767781
}
768782

769-
if (nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number) {
770-
max_volume += nearby_clip->volume.GetValue(nearby_clip_frame_number);
783+
// Determine max volume of overlapping clips
784+
if (nearby_clip->Reader() && nearby_clip->Reader()->info.has_audio &&
785+
nearby_clip->has_audio.GetInt(nearby_clip_frame_number) != 0 &&
786+
nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number) {
787+
max_volume += nearby_clip->volume.GetValue(nearby_clip_frame_number);
771788
}
772789
}
773790

0 commit comments

Comments
 (0)