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) (#132)
(cherry picked from commit 2f45a4e)
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
|
||||
AnchorType anchor; ///< The anchor determines what parent a clip should snap to
|
||||
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
|
||||
Clip();
|
||||
|
||||
@@ -69,5 +69,13 @@ namespace openshot
|
||||
FRAME_DISPLAY_TIMELINE, ///< Display the 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
|
||||
|
||||
10
src/Clip.cpp
10
src/Clip.cpp
@@ -41,6 +41,7 @@ void Clip::init_settings()
|
||||
scale = SCALE_FIT;
|
||||
anchor = ANCHOR_CANVAS;
|
||||
display = FRAME_DISPLAY_NONE;
|
||||
mixing = VOLUME_MIX_NONE;
|
||||
waveform = false;
|
||||
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["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["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);
|
||||
|
||||
// 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("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)
|
||||
root["waveform"]["choices"].append(add_property_choice_json("Yes", true, waveform));
|
||||
root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform));
|
||||
@@ -758,6 +765,7 @@ Json::Value Clip::JsonValue() {
|
||||
root["scale"] = scale;
|
||||
root["anchor"] = anchor;
|
||||
root["display"] = display;
|
||||
root["mixing"] = mixing;
|
||||
root["waveform"] = waveform;
|
||||
root["scale_x"] = scale_x.JsonValue();
|
||||
root["scale_y"] = scale_y.JsonValue();
|
||||
@@ -844,6 +852,8 @@ void Clip::SetJsonValue(Json::Value root) {
|
||||
anchor = (AnchorType) root["anchor"].asInt();
|
||||
if (!root["display"].isNull())
|
||||
display = (FrameDisplayType) root["display"].asInt();
|
||||
if (!root["mixing"].isNull())
|
||||
mixing = (VolumeMixType) root["mixing"].asInt();
|
||||
if (!root["waveform"].isNull())
|
||||
waveform = root["waveform"].asBool();
|
||||
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)
|
||||
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)
|
||||
float volume = source_clip->volume.GetValue(clip_frame_number) / fmaxf(max_volume, 1.0); // percentage of volume (0 to 1)
|
||||
// Get volume from previous frame and this frame
|
||||
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_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 != -1 && channel_filter != 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_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() &&
|
||||
nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number &&
|
||||
nearby_clip_start_position > clip_start_position && is_top_clip == true) {
|
||||
is_top_clip = false;
|
||||
}
|
||||
|
||||
if (nearby_clip_start_position <= frame_number && nearby_clip_end_position >= frame_number) {
|
||||
max_volume += nearby_clip->volume.GetValue(nearby_clip_frame_number);
|
||||
// Determine max volume of overlapping clips
|
||||
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