You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
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)
This commit is contained in:
@@ -155,6 +155,7 @@ namespace openshot {
|
|||||||
ScaleType scale; ///< The scale determines how a clip should be resized to fit it's parent
|
ScaleType scale; ///< The scale determines how a clip should be resized to fit it's parent
|
||||||
AnchorType anchor; ///< The anchor determines what parent a clip should snap to
|
AnchorType anchor; ///< The anchor determines what parent a clip should snap to
|
||||||
FrameDisplayType display; ///< The format to display the frame number (if any)
|
FrameDisplayType display; ///< The format to display the frame number (if any)
|
||||||
|
VolumeMixType mixing; ///< What strategy should be followed when mixing audio with other clips
|
||||||
|
|
||||||
/// Default Constructor
|
/// Default Constructor
|
||||||
Clip();
|
Clip();
|
||||||
|
|||||||
@@ -69,5 +69,13 @@ namespace openshot
|
|||||||
FRAME_DISPLAY_TIMELINE, ///< Display the timeline's frame number
|
FRAME_DISPLAY_TIMELINE, ///< Display the timeline's frame number
|
||||||
FRAME_DISPLAY_BOTH ///< Display both the clip's and timeline's frame number
|
FRAME_DISPLAY_BOTH ///< Display both the clip's and timeline's frame number
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This enumeration determines the strategy when mixing audio with other clips.
|
||||||
|
enum VolumeMixType
|
||||||
|
{
|
||||||
|
VOLUME_MIX_NONE, ///< Do not apply any volume mixing adjustments. Just add the samples together.
|
||||||
|
VOLUME_MIX_AVERAGE, ///< Evenly divide the overlapping clips volume keyframes, so that the sum does not exceed 100%
|
||||||
|
VOLUME_MIX_REDUCE ///< Reduce volume by about %25, and then mix (louder, but could cause pops if the sum exceeds 100%)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
10
src/Clip.cpp
10
src/Clip.cpp
@@ -41,6 +41,7 @@ void Clip::init_settings()
|
|||||||
scale = SCALE_FIT;
|
scale = SCALE_FIT;
|
||||||
anchor = ANCHOR_CANVAS;
|
anchor = ANCHOR_CANVAS;
|
||||||
display = FRAME_DISPLAY_NONE;
|
display = FRAME_DISPLAY_NONE;
|
||||||
|
mixing = VOLUME_MIX_NONE;
|
||||||
waveform = false;
|
waveform = false;
|
||||||
previous_properties = "";
|
previous_properties = "";
|
||||||
|
|
||||||
@@ -694,6 +695,7 @@ string Clip::PropertiesJSON(int64_t requested_frame) {
|
|||||||
root["gravity"] = add_property_json("Gravity", gravity, "int", "", NULL, 0, 8, false, requested_frame);
|
root["gravity"] = add_property_json("Gravity", gravity, "int", "", NULL, 0, 8, false, requested_frame);
|
||||||
root["scale"] = add_property_json("Scale", scale, "int", "", NULL, 0, 3, false, requested_frame);
|
root["scale"] = add_property_json("Scale", scale, "int", "", NULL, 0, 3, false, requested_frame);
|
||||||
root["display"] = add_property_json("Frame Number", display, "int", "", NULL, 0, 3, false, requested_frame);
|
root["display"] = add_property_json("Frame Number", display, "int", "", NULL, 0, 3, false, requested_frame);
|
||||||
|
root["mixing"] = add_property_json("Volume Mixing", mixing, "int", "", NULL, 0, 2, false, requested_frame);
|
||||||
root["waveform"] = add_property_json("Waveform", waveform, "int", "", NULL, 0, 1, false, requested_frame);
|
root["waveform"] = add_property_json("Waveform", waveform, "int", "", NULL, 0, 1, false, requested_frame);
|
||||||
|
|
||||||
// Add gravity choices (dropdown style)
|
// Add gravity choices (dropdown style)
|
||||||
@@ -719,6 +721,11 @@ string Clip::PropertiesJSON(int64_t requested_frame) {
|
|||||||
root["display"]["choices"].append(add_property_choice_json("Timeline", FRAME_DISPLAY_TIMELINE, display));
|
root["display"]["choices"].append(add_property_choice_json("Timeline", FRAME_DISPLAY_TIMELINE, display));
|
||||||
root["display"]["choices"].append(add_property_choice_json("Both", FRAME_DISPLAY_BOTH, display));
|
root["display"]["choices"].append(add_property_choice_json("Both", FRAME_DISPLAY_BOTH, display));
|
||||||
|
|
||||||
|
// Add volume mixing choices (dropdown style)
|
||||||
|
root["mixing"]["choices"].append(add_property_choice_json("None", VOLUME_MIX_NONE, mixing));
|
||||||
|
root["mixing"]["choices"].append(add_property_choice_json("Average", VOLUME_MIX_AVERAGE, mixing));
|
||||||
|
root["mixing"]["choices"].append(add_property_choice_json("Reduce", VOLUME_MIX_REDUCE, mixing));
|
||||||
|
|
||||||
// Add waveform choices (dropdown style)
|
// Add waveform choices (dropdown style)
|
||||||
root["waveform"]["choices"].append(add_property_choice_json("Yes", true, waveform));
|
root["waveform"]["choices"].append(add_property_choice_json("Yes", true, waveform));
|
||||||
root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform));
|
root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform));
|
||||||
@@ -758,6 +765,7 @@ Json::Value Clip::JsonValue() {
|
|||||||
root["scale"] = scale;
|
root["scale"] = scale;
|
||||||
root["anchor"] = anchor;
|
root["anchor"] = anchor;
|
||||||
root["display"] = display;
|
root["display"] = display;
|
||||||
|
root["mixing"] = mixing;
|
||||||
root["waveform"] = waveform;
|
root["waveform"] = waveform;
|
||||||
root["scale_x"] = scale_x.JsonValue();
|
root["scale_x"] = scale_x.JsonValue();
|
||||||
root["scale_y"] = scale_y.JsonValue();
|
root["scale_y"] = scale_y.JsonValue();
|
||||||
@@ -844,6 +852,8 @@ void Clip::SetJsonValue(Json::Value root) {
|
|||||||
anchor = (AnchorType) root["anchor"].asInt();
|
anchor = (AnchorType) root["anchor"].asInt();
|
||||||
if (!root["display"].isNull())
|
if (!root["display"].isNull())
|
||||||
display = (FrameDisplayType) root["display"].asInt();
|
display = (FrameDisplayType) root["display"].asInt();
|
||||||
|
if (!root["mixing"].isNull())
|
||||||
|
mixing = (VolumeMixType) root["mixing"].asInt();
|
||||||
if (!root["waveform"].isNull())
|
if (!root["waveform"].isNull())
|
||||||
waveform = root["waveform"].asBool();
|
waveform = root["waveform"].asBool();
|
||||||
if (!root["scale_x"].isNull())
|
if (!root["scale_x"].isNull())
|
||||||
|
|||||||
@@ -294,11 +294,24 @@ void Timeline::add_layer(std::shared_ptr<Frame> new_frame, Clip* source_clip, in
|
|||||||
if (source_frame->GetAudioChannelsCount() == info.channels && source_clip->has_audio.GetInt(clip_frame_number) != 0)
|
if (source_frame->GetAudioChannelsCount() == info.channels && source_clip->has_audio.GetInt(clip_frame_number) != 0)
|
||||||
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++)
|
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++)
|
||||||
{
|
{
|
||||||
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)
|
// Get volume from previous frame and this frame
|
||||||
float volume = source_clip->volume.GetValue(clip_frame_number) / fmaxf(max_volume, 1.0); // percentage of volume (0 to 1)
|
float previous_volume = source_clip->volume.GetValue(clip_frame_number - 1);
|
||||||
|
float volume = source_clip->volume.GetValue(clip_frame_number);
|
||||||
int channel_filter = source_clip->channel_filter.GetInt(clip_frame_number); // optional channel to filter (if not -1)
|
int channel_filter = source_clip->channel_filter.GetInt(clip_frame_number); // optional channel to filter (if not -1)
|
||||||
int channel_mapping = source_clip->channel_mapping.GetInt(clip_frame_number); // optional channel to map this channel to (if not -1)
|
int channel_mapping = source_clip->channel_mapping.GetInt(clip_frame_number); // optional channel to map this channel to (if not -1)
|
||||||
|
|
||||||
|
// Apply volume mixing strategy
|
||||||
|
if (source_clip->mixing == VOLUME_MIX_AVERAGE && max_volume > 1.0) {
|
||||||
|
// Don't allow this clip to exceed 100% (divide volume equally between all overlapping clips with volume
|
||||||
|
previous_volume = previous_volume / max_volume;
|
||||||
|
volume = volume / max_volume;
|
||||||
|
}
|
||||||
|
else if (source_clip->mixing == VOLUME_MIX_REDUCE && max_volume > 1.0) {
|
||||||
|
// Reduce clip volume by a bit, hoping it will prevent exceeding 100% (but it is very possible it will)
|
||||||
|
previous_volume = previous_volume * 0.77;
|
||||||
|
volume = volume * 0.77;
|
||||||
|
}
|
||||||
|
|
||||||
// If channel filter enabled, check for correct channel (and skip non-matching channels)
|
// If channel filter enabled, check for correct channel (and skip non-matching channels)
|
||||||
if (channel_filter != -1 && channel_filter != channel)
|
if (channel_filter != -1 && channel_filter != channel)
|
||||||
continue; // skip to next channel
|
continue; // skip to next channel
|
||||||
@@ -760,14 +773,18 @@ std::shared_ptr<Frame> Timeline::GetFrame(int64_t requested_frame)
|
|||||||
long nearby_clip_start_frame = (nearby_clip->Start() * info.fps.ToDouble()) + 1;
|
long nearby_clip_start_frame = (nearby_clip->Start() * info.fps.ToDouble()) + 1;
|
||||||
long nearby_clip_frame_number = frame_number - nearby_clip_start_position + nearby_clip_start_frame;
|
long nearby_clip_frame_number = frame_number - nearby_clip_start_position + nearby_clip_start_frame;
|
||||||
|
|
||||||
|
// Determine if top clip
|
||||||
if (clip->Id() != nearby_clip->Id() && clip->Layer() == nearby_clip->Layer() &&
|
if (clip->Id() != nearby_clip->Id() && clip->Layer() == nearby_clip->Layer() &&
|
||||||
nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number &&
|
nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number &&
|
||||||
nearby_clip_start_position > clip_start_position && is_top_clip == true) {
|
nearby_clip_start_position > clip_start_position && is_top_clip == true) {
|
||||||
is_top_clip = false;
|
is_top_clip = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number) {
|
// Determine max volume of overlapping clips
|
||||||
max_volume += nearby_clip->volume.GetValue(nearby_clip_frame_number);
|
if (nearby_clip->Reader() && nearby_clip->Reader()->info.has_audio &&
|
||||||
|
nearby_clip->has_audio.GetInt(nearby_clip_frame_number) != 0 &&
|
||||||
|
nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number) {
|
||||||
|
max_volume += nearby_clip->volume.GetValue(nearby_clip_frame_number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user