OpenShot Library | libopenshot 0.3.3
Loading...
Searching...
No Matches
ParametricEQ.cpp
Go to the documentation of this file.
1
9// Copyright (c) 2008-2019 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include "ParametricEQ.h"
14#include "Exceptions.h"
15
16using namespace openshot;
17using namespace juce;
18
20
22 Keyframe frequency, Keyframe gain, Keyframe q_factor) :
23 filter_type(filter_type),
24 frequency(frequency), gain(gain), q_factor(q_factor)
25{
26 // Init effect properties
27 init_effect_details();
28}
29
30// Init effect settings
31void ParametricEQ::init_effect_details()
32{
35
37 info.class_name = "ParametricEQ";
38 info.name = "Parametric EQ";
39 info.description = "Filter that allows you to adjust the volume level of a frequency in the audio track.";
40 info.has_audio = true;
41 info.has_video = false;
42 initialized = false;
43}
44
45// This method is required for all derived classes of EffectBase, and returns a
46// modified openshot::Frame object
47std::shared_ptr<openshot::Frame> ParametricEQ::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
48{
49 if (!initialized)
50 {
51 filters.clear();
52
53 for (int i = 0; i < frame->audio->getNumChannels(); ++i) {
54 Filter *filter;
55 filters.add(filter = new Filter());
56 }
57
58 initialized = true;
59 }
60
61 const int num_input_channels = frame->audio->getNumChannels();
62 const int num_output_channels = frame->audio->getNumChannels();
63 const int num_samples = frame->audio->getNumSamples();
64 updateFilters(frame_number, num_samples);
65
66 for (int channel = 0; channel < frame->audio->getNumChannels(); channel++)
67 {
68 auto *channel_data = frame->audio->getWritePointer(channel);
69 filters[channel]->processSamples(channel_data, num_samples);
70 }
71
72 for (int channel = num_input_channels; channel < num_output_channels; ++channel)
73 {
74 frame->audio->clear(channel, 0, num_samples);
75 }
76
77 // return the modified frame
78 return frame;
79}
80
82 const double discrete_frequency,
83 const double q_factor,
84 const double gain,
85 const int filter_type)
86{
87 double bandwidth = jmin (discrete_frequency / q_factor, M_PI * 0.99);
88 double two_cos_wc = -2.0 * cos (discrete_frequency);
89 double tan_half_bw = tan (bandwidth / 2.0);
90 double tan_half_wc = tan (discrete_frequency / 2.0);
91 double sqrt_gain = sqrt (gain);
92
93 switch (filter_type) {
94 case 0 /* LOW_PASS */: {
95 coefficients = IIRCoefficients (/* b0 */ tan_half_wc,
96 /* b1 */ tan_half_wc,
97 /* b2 */ 0.0,
98 /* a0 */ tan_half_wc + 1.0,
99 /* a1 */ tan_half_wc - 1.0,
100 /* a2 */ 0.0);
101 break;
102 }
103 case 1 /* HIGH_PASS */: {
104 coefficients = IIRCoefficients (/* b0 */ 1.0,
105 /* b1 */ -1.0,
106 /* b2 */ 0.0,
107 /* a0 */ tan_half_wc + 1.0,
108 /* a1 */ tan_half_wc - 1.0,
109 /* a2 */ 0.0);
110 break;
111 }
112 case 2 /* LOW_SHELF */: {
113 coefficients = IIRCoefficients (/* b0 */ gain * tan_half_wc + sqrt_gain,
114 /* b1 */ gain * tan_half_wc - sqrt_gain,
115 /* b2 */ 0.0,
116 /* a0 */ tan_half_wc + sqrt_gain,
117 /* a1 */ tan_half_wc - sqrt_gain,
118 /* a2 */ 0.0);
119 break;
120 }
121 case 3 /* HIGH_SHELF */: {
122 coefficients = IIRCoefficients (/* b0 */ sqrt_gain * tan_half_wc + gain,
123 /* b1 */ sqrt_gain * tan_half_wc - gain,
124 /* b2 */ 0.0,
125 /* a0 */ sqrt_gain * tan_half_wc + 1.0,
126 /* a1 */ sqrt_gain * tan_half_wc - 1.0,
127 /* a2 */ 0.0);
128 break;
129 }
130 case 4 /* BAND_PASS */: {
131 coefficients = IIRCoefficients (/* b0 */ tan_half_bw,
132 /* b1 */ 0.0,
133 /* b2 */ -tan_half_bw,
134 /* a0 */ 1.0 + tan_half_bw,
135 /* a1 */ two_cos_wc,
136 /* a2 */ 1.0 - tan_half_bw);
137 break;
138 }
139 case 5 /* BAND_STOP */: {
140 coefficients = IIRCoefficients (/* b0 */ 1.0,
141 /* b1 */ two_cos_wc,
142 /* b2 */ 1.0,
143 /* a0 */ 1.0 + tan_half_bw,
144 /* a1 */ two_cos_wc,
145 /* a2 */ 1.0 - tan_half_bw);
146 break;
147 }
148 case 6 /* PEAKING_NOTCH */: {
149 coefficients = IIRCoefficients (/* b0 */ sqrt_gain + gain * tan_half_bw,
150 /* b1 */ sqrt_gain * two_cos_wc,
151 /* b2 */ sqrt_gain - gain * tan_half_bw,
152 /* a0 */ sqrt_gain + tan_half_bw,
153 /* a1 */ sqrt_gain * two_cos_wc,
154 /* a2 */ sqrt_gain - tan_half_bw);
155 break;
156 }
157 }
158
159 setCoefficients(coefficients);
160}
161
162void ParametricEQ::updateFilters(int64_t frame_number, double sample_rate)
163{
164 double discrete_frequency = 2.0 * M_PI * (double)frequency.GetValue(frame_number) / sample_rate;
165 double q_value = (double)q_factor.GetValue(frame_number);
166 double gain_value = pow(10.0, (double)gain.GetValue(frame_number) * 0.05);
167 int filter_type_value = (int)filter_type;
168
169 for (int i = 0; i < filters.size(); ++i)
170 filters[i]->updateCoefficients(discrete_frequency, q_value, gain_value, filter_type_value);
171}
172
173// Generate JSON string of this object
174std::string ParametricEQ::Json() const {
175
176 // Return formatted string
177 return JsonValue().toStyledString();
178}
179
180// Generate Json::Value for this object
181Json::Value ParametricEQ::JsonValue() const {
182
183 // Create root json object
184 Json::Value root = EffectBase::JsonValue(); // get parent properties
185 root["type"] = info.class_name;
186 root["filter_type"] = filter_type;
187 root["frequency"] = frequency.JsonValue();;
188 root["q_factor"] = q_factor.JsonValue();
189 root["gain"] = gain.JsonValue();
190
191 // return JsonValue
192 return root;
193}
194
195// Load JSON string into this object
196void ParametricEQ::SetJson(const std::string value) {
197
198 // Parse JSON string into JSON objects
199 try
200 {
201 const Json::Value root = openshot::stringToJson(value);
202 // Set all values that match
203 SetJsonValue(root);
204 }
205 catch (const std::exception& e)
206 {
207 // Error parsing JSON (or missing keys)
208 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
209 }
210}
211
212// Load Json::Value into this object
213void ParametricEQ::SetJsonValue(const Json::Value root) {
214
215 // Set parent data
217
218 // Set data from Json (if key is found)
219 if (!root["filter_type"].isNull())
220 filter_type = (FilterType)root["filter_type"].asInt();
221
222 if (!root["frequency"].isNull())
223 frequency.SetJsonValue(root["frequency"]);
224
225 if (!root["gain"].isNull())
226 gain.SetJsonValue(root["gain"]);
227
228 if (!root["q_factor"].isNull())
229 q_factor.SetJsonValue(root["q_factor"]);
230}
231
232// Get all properties for a specific frame
233std::string ParametricEQ::PropertiesJSON(int64_t requested_frame) const {
234
235 // Generate JSON properties list
236 Json::Value root = BasePropertiesJSON(requested_frame);
237
238 // Keyframes
239 root["filter_type"] = add_property_json("Filter Type", filter_type, "int", "", NULL, 0, 3, false, requested_frame);
240 root["frequency"] = add_property_json("Frequency (Hz)", frequency.GetValue(requested_frame), "int", "", &frequency, 20, 20000, false, requested_frame);
241 root["gain"] = add_property_json("Gain (dB)", gain.GetValue(requested_frame), "int", "", &gain, -24, 24, false, requested_frame);
242 root["q_factor"] = add_property_json("Q Factor", q_factor.GetValue(requested_frame), "float", "", &q_factor, 0, 20, false, requested_frame);
243
244 // Add filter_type choices (dropdown style)
245 root["filter_type"]["choices"].append(add_property_choice_json("Low Pass", LOW_PASS, filter_type));
246 root["filter_type"]["choices"].append(add_property_choice_json("High Pass", HIGH_PASS, filter_type));
247 root["filter_type"]["choices"].append(add_property_choice_json("Low Shelf", LOW_SHELF, filter_type));
248 root["filter_type"]["choices"].append(add_property_choice_json("High Shelf", HIGH_SHELF, filter_type));
249 root["filter_type"]["choices"].append(add_property_choice_json("Band Pass", BAND_PASS, filter_type));
250 root["filter_type"]["choices"].append(add_property_choice_json("Band Stop", BAND_STOP, filter_type));
251 root["filter_type"]["choices"].append(add_property_choice_json("Peaking Notch", PEAKING_NOTCH, filter_type));
252
253 // Return formatted string
254 return root.toStyledString();
255}
Header file for all Exception classes.
Header file for Parametric EQ audio effect class.
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties)
Definition ClipBase.cpp:132
Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe *keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const
Generate JSON for a property.
Definition ClipBase.cpp:96
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
EffectInfoStruct info
Information about the current effect.
Definition EffectBase.h:69
Exception for invalid JSON.
Definition Exceptions.h:218
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition KeyFrame.h:53
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition KeyFrame.cpp:372
double GetValue(int64_t index) const
Get the value at a specific index.
Definition KeyFrame.cpp:258
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition KeyFrame.cpp:339
void updateCoefficients(const double discrete_frequency, const double q_factor, const double gain, const int filter_type)
This class adds a equalization into the audio.
std::string PropertiesJSON(int64_t requested_frame) const override
void SetJson(const std::string value) override
Load JSON string into this object.
ParametricEQ()
Blank constructor, useful when using Json to load the effect properties.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number) override
This method is required for all derived classes of ClipBase, and returns a new openshot::Frame object...
std::string Json() const override
Generate JSON string of this object.
void updateFilters(int64_t frame_number, double sample_rate)
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Json::Value JsonValue() const override
Generate Json::Value for this object.
openshot::FilterType filter_type
juce::OwnedArray< Filter > filters
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
FilterType
This enumeration determines the filter type of ParametricEQ Effect.
Definition Enums.h:80
@ LOW_SHELF
Definition Enums.h:83
@ HIGH_SHELF
Definition Enums.h:84
@ BAND_PASS
Definition Enums.h:85
@ BAND_STOP
Definition Enums.h:86
@ LOW_PASS
Definition Enums.h:81
@ HIGH_PASS
Definition Enums.h:82
@ PEAKING_NOTCH
Definition Enums.h:87
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
bool has_video
Determines if this effect manipulates the image of a frame.
Definition EffectBase.h:40
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition EffectBase.h:41
std::string class_name
The class name of the effect.
Definition EffectBase.h:36
std::string name
The name of the effect.
Definition EffectBase.h:37
std::string description
The description of this effect and what it does.
Definition EffectBase.h:38