OpenShot Library | libopenshot  0.4.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 = 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 }
openshot::ClipBase::add_property_json
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
openshot::stringToJson
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
ParametricEQ.h
Header file for Parametric EQ audio effect class.
openshot::ParametricEQ::GetFrame
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
openshot::EffectBase::info
EffectInfoStruct info
Information about the current effect.
Definition: EffectBase.h:69
openshot::ParametricEQ::ParametricEQ
ParametricEQ()
Blank constructor, useful when using Json to load the effect properties.
Definition: ParametricEQ.cpp:19
openshot::LOW_PASS
@ LOW_PASS
Definition: Enums.h:81
openshot::ParametricEQ::filters
juce::OwnedArray< Filter > filters
Definition: ParametricEQ.h:81
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:28
openshot::ParametricEQ::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition: ParametricEQ.cpp:196
openshot::ParametricEQ
This class adds a equalization into the audio.
Definition: ParametricEQ.h:37
openshot::ParametricEQ::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition: ParametricEQ.cpp:213
openshot::ClipBase::add_property_choice_json
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
openshot::EffectBase::JsonValue
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: EffectBase.cpp:79
openshot::HIGH_SHELF
@ HIGH_SHELF
Definition: Enums.h:84
openshot::HIGH_PASS
@ HIGH_PASS
Definition: Enums.h:82
openshot::FilterType
FilterType
This enumeration determines the filter type of ParametricEQ Effect.
Definition: Enums.h:79
openshot::Keyframe::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:372
openshot::Keyframe::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: KeyFrame.cpp:339
openshot::BAND_STOP
@ BAND_STOP
Definition: Enums.h:86
openshot::LOW_SHELF
@ LOW_SHELF
Definition: Enums.h:83
openshot::EffectBase::BasePropertiesJSON
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
Definition: EffectBase.cpp:179
openshot::ParametricEQ::Filter::updateCoefficients
void updateCoefficients(const double discrete_frequency, const double q_factor, const double gain, const int filter_type)
Definition: ParametricEQ.cpp:81
openshot::Keyframe
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition: KeyFrame.h:53
openshot::BAND_PASS
@ BAND_PASS
Definition: Enums.h:85
openshot::ParametricEQ::initialized
bool initialized
Definition: ParametricEQ.h:48
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:217
openshot::EffectBase::InitEffectInfo
void InitEffectInfo()
Definition: EffectBase.cpp:24
openshot::EffectInfoStruct::has_audio
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition: EffectBase.h:41
juce
Definition: Robotization.h:29
openshot::ParametricEQ::updateFilters
void updateFilters(int64_t frame_number, double sample_rate)
Definition: ParametricEQ.cpp:162
openshot::ParametricEQ::filter_type
openshot::FilterType filter_type
Definition: ParametricEQ.h:44
openshot::ParametricEQ::gain
Keyframe gain
Definition: ParametricEQ.h:47
openshot::ParametricEQ::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition: ParametricEQ.cpp:181
openshot::EffectInfoStruct::class_name
std::string class_name
The class name of the effect.
Definition: EffectBase.h:36
openshot::ParametricEQ::Filter
Definition: ParametricEQ.h:72
openshot::EffectInfoStruct::description
std::string description
The description of this effect and what it does.
Definition: EffectBase.h:38
openshot::EffectInfoStruct::has_video
bool has_video
Determines if this effect manipulates the image of a frame.
Definition: EffectBase.h:40
openshot::PEAKING_NOTCH
@ PEAKING_NOTCH
Definition: Enums.h:87
openshot::ParametricEQ::q_factor
Keyframe q_factor
Definition: ParametricEQ.h:46
openshot::EffectInfoStruct::name
std::string name
The name of the effect.
Definition: EffectBase.h:37
openshot::ParametricEQ::PropertiesJSON
std::string PropertiesJSON(int64_t requested_frame) const override
Definition: ParametricEQ.cpp:233
openshot::ParametricEQ::Json
std::string Json() const override
Generate JSON string of this object.
Definition: ParametricEQ.cpp:174
Exceptions.h
Header file for all Exception classes.
openshot::ParametricEQ::frequency
Keyframe frequency
Definition: ParametricEQ.h:45
openshot::EffectBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: EffectBase.cpp:115
openshot::Keyframe::GetValue
double GetValue(int64_t index) const
Get the value at a specific index.
Definition: KeyFrame.cpp:258