OpenShot Library | libopenshot  0.3.0
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 
16 using namespace openshot;
17 using 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
31 void 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
47 std::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 
162 void 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
174 std::string ParametricEQ::Json() const {
175 
176  // Return formatted string
177  return JsonValue().toStyledString();
178 }
179 
180 // Generate Json::Value for this object
181 Json::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
196 void 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
213 void 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
233 std::string ParametricEQ::PropertiesJSON(int64_t requested_frame) const {
234 
235  // Generate JSON properties list
236  Json::Value root;
237  root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
238  root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame);
239  root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
240  root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
241  root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 1000 * 60 * 30, true, requested_frame);
242 
243  // Keyframes
244  root["filter_type"] = add_property_json("Filter Type", filter_type, "int", "", NULL, 0, 3, false, requested_frame);
245  root["frequency"] = add_property_json("Frequency (Hz)", frequency.GetValue(requested_frame), "int", "", &frequency, 20, 20000, false, requested_frame);
246  root["gain"] = add_property_json("Gain (dB)", gain.GetValue(requested_frame), "int", "", &gain, -24, 24, false, requested_frame);
247  root["q_factor"] = add_property_json("Q Factor", q_factor.GetValue(requested_frame), "float", "", &q_factor, 0, 20, false, requested_frame);
248 
249  // Add filter_type choices (dropdown style)
250  root["filter_type"]["choices"].append(add_property_choice_json("Low Pass", LOW_PASS, filter_type));
251  root["filter_type"]["choices"].append(add_property_choice_json("High Pass", HIGH_PASS, filter_type));
252  root["filter_type"]["choices"].append(add_property_choice_json("Low Shelf", LOW_SHELF, filter_type));
253  root["filter_type"]["choices"].append(add_property_choice_json("High Shelf", HIGH_SHELF, filter_type));
254  root["filter_type"]["choices"].append(add_property_choice_json("Band Pass", BAND_PASS, filter_type));
255  root["filter_type"]["choices"].append(add_property_choice_json("Band Stop", BAND_STOP, filter_type));
256  root["filter_type"]["choices"].append(add_property_choice_json("Peaking Notch", PEAKING_NOTCH, filter_type));
257 
258  // Return formatted string
259  return root.toStyledString();
260 }
Header file for all Exception classes.
Header file for Parametric EQ audio effect class.
float Start() const
Get start position (in seconds) of clip (trim start of video)
Definition: ClipBase.h:88
float Duration() const
Get the length of this clip (in seconds)
Definition: ClipBase.h:90
virtual float End() const
Get end position (in seconds) of clip (trim end of video)
Definition: ClipBase.h:89
std::string Id() const
Get the Id of this clip object.
Definition: ClipBase.h:85
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
int Layer() const
Get layer of clip on timeline (lower number is covered by higher numbers)
Definition: ClipBase.h:87
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.
Definition: EffectBase.cpp:77
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: EffectBase.cpp:112
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:54
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:358
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:325
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.
Definition: ParametricEQ.h:38
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...
Definition: ParametricEQ.h:57
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
Definition: ParametricEQ.h:44
juce::OwnedArray< Filter > filters
Definition: ParametricEQ.h:81
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