1 #include "bcdisplayinfo.h"
3 #include "compressor.h"
15 #define _(String) gettext(String)
16 #define gettext_noop(String) String
17 #define N_(String) gettext_noop (String)
23 REGISTER_PLUGIN(CompressorEffect)
32 CompressorEffect::CompressorEffect(PluginServer *server)
33 : PluginAClient(server)
36 PLUGIN_CONSTRUCTOR_MACRO
39 CompressorEffect::~CompressorEffect()
41 PLUGIN_DESTRUCTOR_MACRO
45 void CompressorEffect::delete_dsp()
49 for(int i = 0; i < PluginClient::total_in_buffers; i++)
50 delete [] input_buffer[i];
51 delete [] input_buffer;
53 if(coefs) delete [] coefs;
55 if(reaction_buffer) delete [] reaction_buffer;
62 reaction_allocated = 0;
63 reaction_position = 0;
67 void CompressorEffect::reset()
75 reaction_allocated = 0;
76 reaction_position = 0;
80 previous_intercept = 1.0;
86 char* CompressorEffect::plugin_title()
88 return _("Compressor");
92 int CompressorEffect::is_realtime()
97 int CompressorEffect::is_multichannel()
107 void CompressorEffect::read_data(KeyFrame *keyframe)
110 input.set_shared_string(keyframe->data, strlen(keyframe->data));
113 config.levels.remove_all();
116 result = input.read_tag();
120 if(input.tag.title_is("COMPRESSOR"))
122 config.preview_len = input.tag.get_property("PREVIEW_LEN", config.preview_len);
123 config.reaction_len = input.tag.get_property("REACTION_LEN", config.reaction_len);
124 config.trigger = input.tag.get_property("TRIGGER", config.trigger);
127 if(input.tag.title_is("LEVEL"))
129 double x = input.tag.get_property("X", (double)0);
130 double y = input.tag.get_property("Y", (double)0);
131 compressor_point_t point = { x, y };
133 config.levels.append(point);
139 void CompressorEffect::save_data(KeyFrame *keyframe)
142 output.set_shared_string(keyframe->data, MESSAGESIZE);
144 output.tag.set_title("COMPRESSOR");
145 output.tag.set_property("PREVIEW_LEN", config.preview_len);
146 output.tag.set_property("TRIGGER", config.trigger);
147 output.tag.set_property("REACTION_LEN", config.reaction_len);
149 output.append_newline();
152 for(int i = 0; i < config.levels.total; i++)
154 output.tag.set_title("LEVEL");
155 output.tag.set_property("X", config.levels.values[i].x);
156 output.tag.set_property("Y", config.levels.values[i].y);
159 output.append_newline();
162 output.terminate_string();
165 int CompressorEffect::load_defaults()
167 char directory[BCTEXTLEN], string[BCTEXTLEN];
168 sprintf(directory, "%scompression.rc", BCASTDIR);
169 defaults = new Defaults(directory);
172 config.preview_len = defaults->get("PREVIEW_LEN", config.preview_len);
173 config.trigger = defaults->get("TRIGGER", config.trigger);
174 config.reaction_len = defaults->get("REACTION_LEN", config.reaction_len);
176 config.levels.remove_all();
177 int total_levels = defaults->get("TOTAL_LEVELS", 0);
178 for(int i = 0; i < total_levels; i++)
180 config.levels.append();
181 sprintf(string, "X_%d", i);
182 config.levels.values[i].x = defaults->get(string, (double)0);
183 sprintf(string, "Y_%d", i);
184 config.levels.values[i].y = defaults->get(string, (double)0);
190 int CompressorEffect::save_defaults()
192 char string[BCTEXTLEN];
194 defaults->update("PREVIEW_LEN", config.preview_len);
195 defaults->update("TRIGGER", config.trigger);
196 defaults->update("REACTION_LEN", config.reaction_len);
197 defaults->update("TOTAL_LEVELS", config.levels.total);
199 defaults->update("TOTAL_LEVELS", config.levels.total);
200 for(int i = 0; i < config.levels.total; i++)
202 sprintf(string, "X_%d", i);
203 defaults->update(string, config.levels.values[i].x);
204 sprintf(string, "Y_%d", i);
205 defaults->update(string, config.levels.values[i].y);
214 void CompressorEffect::update_gui()
218 load_configuration();
219 thread->window->lock_window();
220 thread->window->update();
221 thread->window->unlock_window();
226 NEW_PICON_MACRO(CompressorEffect)
227 SHOW_GUI_MACRO(CompressorEffect, CompressorThread)
228 RAISE_WINDOW_MACRO(CompressorEffect)
229 SET_STRING_MACRO(CompressorEffect)
230 LOAD_CONFIGURATION_MACRO(CompressorEffect, CompressorConfig)
234 int CompressorEffect::process_realtime(int64_t size, double **input_ptr, double **output_ptr)
236 //printf("CompressorEffect::process_realtime 1 %f\n", DB::fromdb(-100));
237 load_configuration();
239 if(coefs_allocated < size)
241 if(coefs) delete [] coefs;
242 coefs = new double[size];
243 coefs_allocated = size;
246 //printf("CompressorEffect::process_realtime 2\n");
247 int preview_samples = (int)(config.preview_len * PluginAClient::project_sample_rate + 0.5);
248 int reaction_samples = (int)(config.reaction_len * PluginAClient::project_sample_rate + 0.5);
249 int trigger = CLIP(config.trigger, 0, PluginAClient::total_in_buffers - 1);
250 // trigger = PluginAClient::total_in_buffers - trigger - 1;
251 CLAMP(reaction_samples, 1, 128000000);
252 CLAMP(preview_samples, 1, 128000000);
254 //printf("CompressorEffect::process_realtime 3\n");
257 // Append input buffers
258 if(input_size + size > input_allocated)
260 double **new_input = new double*[PluginClient::total_in_buffers];
261 for(int i = 0; i < PluginClient::total_in_buffers; i++)
262 new_input[i] = new double[input_size + size];
266 for(int i = 0; i < PluginClient::total_in_buffers; i++)
268 memcpy(new_input[i], input_buffer[i], sizeof(double) * input_size);
269 delete [] input_buffer[i];
272 delete [] input_buffer;
275 input_buffer = new_input;
276 input_allocated = input_size + size;
279 //printf("CompressorEffect::process_realtime 4\n");
280 for(int i = 0; i < PluginClient::total_in_buffers; i++)
281 memcpy(input_buffer[i] + input_size,
283 size * sizeof(double));
288 //printf("CompressorEffect::process_realtime 5 %d\n", size);
293 // Have enough to send to output
294 if(input_size >= preview_samples)
296 int output_offset = 0;
297 if(input_size - preview_samples < size)
299 output_offset = size - (input_size - preview_samples);
300 size = input_size - preview_samples;
301 for(int i = 0; i < PluginAClient::total_in_buffers; i++)
302 bzero(output_ptr[i], output_offset * sizeof(double));
305 //printf("CompressorEffect::process_realtime 6 %d\n", reaction_samples);
306 if(reaction_allocated != reaction_samples)
308 double *new_buffer = new double[reaction_samples];
313 sizeof(double) * MIN(reaction_allocated, reaction_samples));
314 delete [] reaction_buffer;
316 if(reaction_samples - reaction_allocated > 0)
317 bzero(new_buffer + reaction_allocated,
318 sizeof(double) * (reaction_samples - reaction_allocated));
319 reaction_buffer = new_buffer;
320 reaction_allocated = reaction_samples;
321 if(reaction_position >= reaction_allocated) reaction_position = 0;
325 //printf("CompressorEffect::process_realtime 7\n");
326 // Calculate coef buffer for current size
327 double max = 0, min = 0;
330 for(int i = 0; i < size; i++)
332 // Put new sample in reaction buffer
333 reaction_buffer[reaction_position] =
334 input_buffer[trigger][i + preview_samples];
339 // Get peak in last reaction buffer size of samples
343 if(last_peak_age < reaction_samples)
345 if(fabs(input_buffer[trigger][i + preview_samples]) > last_peak)
347 last_peak = fabs(input_buffer[trigger][i + preview_samples]);
355 for(int j = 0; j < reaction_samples; j++)
357 if(reaction_buffer[j] > max)
359 max = reaction_buffer[j];
360 max_age = (j <= reaction_position) ?
361 (reaction_position - j) :
362 (reaction_samples - (j - reaction_position));
365 if(reaction_buffer[j] < min)
367 min = reaction_buffer[j];
368 min_age = (j <= reaction_position) ?
369 (reaction_position - j) :
370 (reaction_samples - (j - reaction_position));
381 last_peak_age = max_age;
384 // Here's the brain of the effect.
386 // Test expiration of previous max. If previous max is bigger than
387 // current max and still valid, it replaces the current max.
388 // Otherwise, the current max becomes the previous max and the counter
391 if(max_counter > 0 && previous_max >= max)
398 // Get new slope based on current coef, peak, and reaction len.
399 // The slope has a counter which needs to expire before it can be
400 // replaced with a less steep value.
401 double x_db = DB::todb(max);
402 double y_db = config.calculate_db(x_db);
403 double y_linear = DB::fromdb(y_db);
404 double new_coef = y_linear / max;
405 double slope = (new_coef - current_coef) / reaction_samples;
406 previous_slope = slope;
408 previous_intercept = current_coef;
409 max_counter = reaction_samples;
413 previous_slope = 0.0;
414 previous_intercept = current_coef;
418 //printf("%f %f %f %d\n", current_coef, previous_slope, previous_intercept, max_counter);
420 current_coef = previous_intercept +
421 previous_slope * (reaction_samples - max_counter);
422 coefs[i] = current_coef;
426 if(reaction_position >= reaction_allocated)
427 reaction_position = 0;
430 //printf("CompressorEffect::process_realtime 8 %f %f\n", input_buffer[0][0], coefs[0]);
431 // Multiply coef buffer by input buffer
432 for(int i = 0; i < PluginAClient::total_in_buffers; i++)
434 for(int j = 0; j < size; j++)
436 output_ptr[i][j + output_offset] = input_buffer[i][j] * coefs[j];
440 //printf("CompressorEffect::process_realtime 9 %d\n", PluginAClient::total_in_buffers);
441 // Shift input forward
442 for(int i = 0; i < PluginAClient::total_in_buffers; i++)
444 for(int j = 0, k = size; k < input_size; j++, k++)
446 input_buffer[i][j] = input_buffer[i][k];
449 //printf("CompressorEffect::process_realtime 10\n");
454 for(int i = 0; i < PluginAClient::total_in_buffers; i++)
455 bzero(output_ptr[i], sizeof(double) * size);
458 //printf("CompressorEffect::process_realtime 11\n");
475 CompressorConfig::CompressorConfig()
487 void CompressorConfig::copy_from(CompressorConfig &that)
489 this->reaction_len = that.reaction_len;
490 this->preview_len = that.preview_len;
491 this->min_db = that.min_db;
492 this->min_x = that.min_x;
493 this->min_y = that.min_y;
494 this->max_x = that.max_x;
495 this->max_y = that.max_y;
496 this->trigger = that.trigger;
498 for(int i = 0; i < that.levels.total; i++)
499 this->levels.append(that.levels.values[i]);
502 int CompressorConfig::equivalent(CompressorConfig &that)
507 void CompressorConfig::interpolate(CompressorConfig &prev,
508 CompressorConfig &next,
511 int64_t current_frame)
516 int CompressorConfig::total_points()
524 void CompressorConfig::dump()
526 printf("CompressorConfig::dump\n");
527 for(int i = 0; i < levels.total; i++)
529 printf(" %f %f\n", levels.values[i].x, levels.values[i].y);
534 double CompressorConfig::get_y(int number)
539 if(number >= levels.total)
540 return levels.values[levels.total - 1].y;
542 return levels.values[number].y;
545 double CompressorConfig::get_x(int number)
550 if(number >= levels.total)
551 return levels.values[levels.total - 1].x;
553 return levels.values[number].x;
556 // Returns linear output given linear input
557 double CompressorConfig::calculate_db(double x)
559 if(x > -0.001) return 0.0;
561 for(int i = levels.total - 1; i >= 0; i--)
563 if(levels.values[i].x <= x)
565 if(i < levels.total - 1)
567 return levels.values[i].y +
568 (x - levels.values[i].x) *
569 (levels.values[i + 1].y - levels.values[i].y) /
570 (levels.values[i + 1].x - levels.values[i].x);
574 return levels.values[i].y +
575 (x - levels.values[i].x) *
576 (max_y - levels.values[i].y) /
577 (max_x - levels.values[i].x);
586 (levels.values[0].y - min_y) /
587 (levels.values[0].x - min_x);
594 int CompressorConfig::set_point(double x, double y)
596 for(int i = levels.total - 1; i >= 0; i--)
598 if(levels.values[i].x < x)
602 for(int j = levels.total - 2; j >= i; j--)
604 levels.values[j + 1] = levels.values[j];
606 levels.values[i].x = x;
607 levels.values[i].y = y;
614 for(int j = levels.total - 2; j >= 0; j--)
616 levels.values[j + 1] = levels.values[j];
618 levels.values[0].x = x;
619 levels.values[0].y = y;
623 void CompressorConfig::remove_point(int number)
625 for(int j = number; j < levels.total - 1; j++)
627 levels.values[j] = levels.values[j + 1];
632 void CompressorConfig::optimize()
641 for(int i = 0; i < levels.total - 1; i++)
643 if(levels.values[i].x >= levels.values[i + 1].x)
646 for(int j = i + 1; j < levels.total - 1; j++)
648 levels.values[j] = levels.values[j + 1];
670 PLUGIN_THREAD_OBJECT(CompressorEffect, CompressorThread, CompressorWindow)
684 CompressorWindow::CompressorWindow(CompressorEffect *plugin, int x, int y)
685 : BC_Window(plugin->gui_string,
696 this->plugin = plugin;
699 void CompressorWindow::create_objects()
703 add_subwindow(canvas = new CompressorCanvas(plugin,
708 canvas->set_cursor(CROSS_CURSOR);
710 add_subwindow(new BC_Title(x, y, _("Preview secs:")));
712 add_subwindow(preview = new CompressorPreview(plugin, x, y));
714 add_subwindow(new BC_Title(x, y, _("Reaction secs:")));
716 add_subwindow(reaction = new CompressorReaction(plugin, x, y));
718 add_subwindow(new BC_Title(x, y, _("Trigger:")));
720 add_subwindow(trigger = new CompressorTrigger(plugin, x, y));
722 add_subwindow(clear = new CompressorClear(plugin, x, y));
725 add_subwindow(new BC_Title(x, y, _("Point:")));
727 add_subwindow(x_text = new CompressorX(plugin, x, y));
729 add_subwindow(new BC_Title(x, y, _("x")));
731 add_subwindow(y_text = new CompressorY(plugin, x, y));
739 WINDOW_CLOSE_EVENT(CompressorWindow)
741 void CompressorWindow::draw_scales()
747 for(int i = 0; i <= DIVISIONS; i++)
749 int y = canvas->get_y() + 10 + canvas->get_h() / DIVISIONS * i;
750 int x = canvas->get_x() - 30;
751 char string[BCTEXTLEN];
753 sprintf(string, "%.0f", (float)i / DIVISIONS * plugin->config.min_db);
754 draw_text(x, y, string);
756 int y1 = canvas->get_y() + canvas->get_h() / DIVISIONS * i;
757 int y2 = canvas->get_y() + canvas->get_h() / DIVISIONS * (i + 1);
758 for(int j = 0; j < 10; j++)
760 y = y1 + (y2 - y1) * j / 10;
763 draw_line(canvas->get_x() - 10, y, canvas->get_x(), y);
768 draw_line(canvas->get_x() - 5, y, canvas->get_x(), y);
775 for(int i = 0; i <= DIVISIONS; i++)
777 int y = canvas->get_h() + 30;
778 int x = canvas->get_x() + (canvas->get_w() - 10) / DIVISIONS * i;
779 char string[BCTEXTLEN];
781 sprintf(string, "%.0f", (1.0 - (float)i / DIVISIONS) * plugin->config.min_db);
782 draw_text(x, y, string);
784 int x1 = canvas->get_x() + canvas->get_w() / DIVISIONS * i;
785 int x2 = canvas->get_x() + canvas->get_w() / DIVISIONS * (i + 1);
786 for(int j = 0; j < 10; j++)
788 x = x1 + (x2 - x1) * j / 10;
791 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + 10);
796 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + 5);
806 void CompressorWindow::update()
812 void CompressorWindow::update_textboxes()
814 preview->update((float)plugin->config.preview_len);
815 trigger->update((int64_t)plugin->config.trigger);
816 reaction->update((float)plugin->config.reaction_len);
817 if(canvas->current_operation == CompressorCanvas::DRAG)
819 x_text->update((float)plugin->config.levels.values[canvas->current_point].x);
820 y_text->update((float)plugin->config.levels.values[canvas->current_point].y);
825 void CompressorWindow::update_canvas()
830 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
831 canvas->set_color(GREEN);
832 for(int i = 1; i < DIVISIONS; i++)
834 int y = canvas->get_h() * i / DIVISIONS;
835 canvas->draw_line(0, y, canvas->get_w(), y);
837 int x = canvas->get_w() * i / DIVISIONS;
838 canvas->draw_line(x, 0, x, canvas->get_h());
843 canvas->set_color(BLACK);
844 for(int i = 0; i < canvas->get_w(); i++)
846 double x_db = ((double)1 - (double)i / canvas->get_w()) * plugin->config.min_db;
847 double y_db = plugin->config.calculate_db(x_db);
848 y2 = (int)(y_db / plugin->config.min_db * canvas->get_h());
852 canvas->draw_line(i - 1, y1, i, y2);
858 int total = plugin->config.levels.total ? plugin->config.levels.total : 1;
859 for(int i = 0; i < plugin->config.levels.total; i++)
861 double x_db = plugin->config.get_x(i);
862 double y_db = plugin->config.get_y(i);
864 int x = (int)(((double)1 - x_db / plugin->config.min_db) * canvas->get_w());
865 int y = (int)(y_db / plugin->config.min_db * canvas->get_h());
867 canvas->draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
881 CompressorCanvas::CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h)
882 : BC_SubWindow(x, y, w, h, WHITE)
884 this->plugin = plugin;
887 int CompressorCanvas::button_press_event()
889 // Check existing points
890 if(is_event_win() && cursor_inside())
892 for(int i = 0; i < plugin->config.levels.total; i++)
894 double x_db = plugin->config.get_x(i);
895 double y_db = plugin->config.get_y(i);
897 int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
898 int y = (int)(y_db / plugin->config.min_db * get_h());
900 if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
901 get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2)
903 current_operation = DRAG;
912 double x_db = (double)(1 - (double)get_cursor_x() / get_w()) * plugin->config.min_db;
913 double y_db = (double)get_cursor_y() / get_h() * plugin->config.min_db;
915 current_point = plugin->config.set_point(x_db, y_db);
916 current_operation = DRAG;
917 plugin->thread->window->update();
918 plugin->send_configure_change();
922 //plugin->config.dump();
925 int CompressorCanvas::button_release_event()
927 if(current_operation == DRAG)
929 if(current_point > 0)
931 if(plugin->config.levels.values[current_point].x <
932 plugin->config.levels.values[current_point - 1].x)
933 plugin->config.remove_point(current_point);
936 if(current_point < plugin->config.levels.total - 1)
938 if(plugin->config.levels.values[current_point].x >=
939 plugin->config.levels.values[current_point + 1].x)
940 plugin->config.remove_point(current_point);
943 plugin->thread->window->update();
944 plugin->send_configure_change();
945 current_operation = NONE;
952 int CompressorCanvas::cursor_motion_event()
954 if(current_operation == DRAG)
956 int x = get_cursor_x();
957 int y = get_cursor_y();
958 CLAMP(x, 0, get_w());
959 CLAMP(y, 0, get_h());
960 double x_db = (double)(1 - (double)x / get_w()) * plugin->config.min_db;
961 double y_db = (double)y / get_h() * plugin->config.min_db;
962 plugin->config.levels.values[current_point].x = x_db;
963 plugin->config.levels.values[current_point].y = y_db;
964 plugin->thread->window->update();
965 plugin->send_configure_change();
967 //plugin->config.dump();
974 CompressorPreview::CompressorPreview(CompressorEffect *plugin, int x, int y)
975 : BC_TextBox(x, y, 100, 1, (float)plugin->config.preview_len)
977 this->plugin = plugin;
980 int CompressorPreview::handle_event()
982 plugin->config.preview_len = atof(get_text());
983 plugin->send_configure_change();
989 CompressorReaction::CompressorReaction(CompressorEffect *plugin, int x, int y)
990 : BC_TextBox(x, y, 100, 1, (float)plugin->config.reaction_len)
992 this->plugin = plugin;
995 int CompressorReaction::handle_event()
997 //printf("CompressorReaction::handle_event 1\n");
998 plugin->config.reaction_len = atof(get_text());
999 plugin->send_configure_change();
1000 //printf("CompressorReaction::handle_event 2\n");
1005 CompressorX::CompressorX(CompressorEffect *plugin, int x, int y)
1006 : BC_TextBox(x, y, 100, 1, "")
1008 this->plugin = plugin;
1010 int CompressorX::handle_event()
1012 int current_point = plugin->thread->window->canvas->current_point;
1013 if(current_point < plugin->config.levels.total)
1015 plugin->config.levels.values[current_point].x = atof(get_text());
1016 plugin->thread->window->update_canvas();
1017 plugin->send_configure_change();
1024 CompressorY::CompressorY(CompressorEffect *plugin, int x, int y)
1025 : BC_TextBox(x, y, 100, 1, "")
1027 this->plugin = plugin;
1029 int CompressorY::handle_event()
1031 int current_point = plugin->thread->window->canvas->current_point;
1032 if(current_point < plugin->config.levels.total)
1034 plugin->config.levels.values[current_point].y = atof(get_text());
1035 plugin->thread->window->update_canvas();
1036 plugin->send_configure_change();
1042 CompressorTrigger::CompressorTrigger(CompressorEffect *plugin, int x, int y)
1043 : BC_TextBox(x, y, (int64_t)100, (int64_t)1, (int64_t)plugin->config.trigger)
1045 this->plugin = plugin;
1047 int CompressorTrigger::handle_event()
1049 plugin->config.trigger = atol(get_text());
1050 plugin->send_configure_change();
1058 CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y)
1059 : BC_GenericButton(x, y, _("Clear"))
1061 this->plugin = plugin;
1064 int CompressorClear::handle_event()
1066 plugin->config.levels.remove_all();
1067 //plugin->config.dump();
1068 plugin->thread->window->update();
1069 plugin->send_configure_change();