00001 #include "bcdisplayinfo.h"
00002 #include "bcsignals.h"
00003 #include "clip.h"
00004 #include "compressor.h"
00005 #include "cursors.h"
00006 #include "bchash.h"
00007 #include "filexml.h"
00008 #include "language.h"
00009 #include "picon_png.h"
00010 #include "units.h"
00011 #include "vframe.h"
00012
00013 #include <math.h>
00014 #include <string.h>
00015
00016
00017
00018
00019
00020 REGISTER_PLUGIN(CompressorEffect)
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051 CompressorEffect::CompressorEffect(PluginServer *server)
00052 : PluginAClient(server)
00053 {
00054 reset();
00055 PLUGIN_CONSTRUCTOR_MACRO
00056 }
00057
00058 CompressorEffect::~CompressorEffect()
00059 {
00060 PLUGIN_DESTRUCTOR_MACRO
00061 delete_dsp();
00062 levels.remove_all();
00063 }
00064
00065 void CompressorEffect::delete_dsp()
00066 {
00067 if(input_buffer)
00068 {
00069 for(int i = 0; i < PluginClient::total_in_buffers; i++)
00070 delete [] input_buffer[i];
00071 delete [] input_buffer;
00072 }
00073
00074
00075 input_buffer = 0;
00076 input_size = 0;
00077 input_allocated = 0;
00078 }
00079
00080
00081 void CompressorEffect::reset()
00082 {
00083 input_buffer = 0;
00084 input_size = 0;
00085 input_allocated = 0;
00086 input_start = 0;
00087
00088 next_target = 1.0;
00089 previous_target = 1.0;
00090 target_samples = 1;
00091 target_current_sample = -1;
00092 current_value = 1.0;
00093 }
00094
00095 char* CompressorEffect::plugin_title() { return N_("Compressor"); }
00096 int CompressorEffect::is_realtime() { return 1; }
00097 int CompressorEffect::is_multichannel() { return 1; }
00098
00099
00100
00101 void CompressorEffect::read_data(KeyFrame *keyframe)
00102 {
00103 FileXML input;
00104 input.set_shared_string(keyframe->data, strlen(keyframe->data));
00105
00106 int result = 0;
00107 config.levels.remove_all();
00108 while(!result)
00109 {
00110 result = input.read_tag();
00111
00112 if(!result)
00113 {
00114 if(input.tag.title_is("COMPRESSOR"))
00115 {
00116 config.reaction_len = input.tag.get_property("REACTION_LEN", config.reaction_len);
00117 config.decay_len = input.tag.get_property("DECAY_LEN", config.decay_len);
00118 config.trigger = input.tag.get_property("TRIGGER", config.trigger);
00119 config.smoothing_only = input.tag.get_property("SMOOTHING_ONLY", config.smoothing_only);
00120 config.input = input.tag.get_property("INPUT", config.input);
00121 }
00122 else
00123 if(input.tag.title_is("LEVEL"))
00124 {
00125 double x = input.tag.get_property("X", (double)0);
00126 double y = input.tag.get_property("Y", (double)0);
00127 compressor_point_t point = { x, y };
00128
00129 config.levels.append(point);
00130 }
00131 }
00132 }
00133 }
00134
00135 void CompressorEffect::save_data(KeyFrame *keyframe)
00136 {
00137 FileXML output;
00138 output.set_shared_string(keyframe->data, MESSAGESIZE);
00139
00140 output.tag.set_title("COMPRESSOR");
00141 output.tag.set_property("TRIGGER", config.trigger);
00142 output.tag.set_property("REACTION_LEN", config.reaction_len);
00143 output.tag.set_property("DECAY_LEN", config.decay_len);
00144 output.tag.set_property("SMOOTHING_ONLY", config.smoothing_only);
00145 output.tag.set_property("INPUT", config.input);
00146 output.append_tag();
00147 output.tag.set_title("/COMPRESSOR");
00148 output.append_tag();
00149 output.append_newline();
00150
00151
00152 for(int i = 0; i < config.levels.total; i++)
00153 {
00154 output.tag.set_title("LEVEL");
00155 output.tag.set_property("X", config.levels.values[i].x);
00156 output.tag.set_property("Y", config.levels.values[i].y);
00157
00158 output.append_tag();
00159 output.append_newline();
00160 }
00161
00162 output.terminate_string();
00163 }
00164
00165 int CompressorEffect::load_defaults()
00166 {
00167 char directory[BCTEXTLEN], string[BCTEXTLEN];
00168 sprintf(directory, "%scompression.rc", BCASTDIR);
00169 defaults = new BC_Hash(directory);
00170 defaults->load();
00171
00172 config.trigger = defaults->get("TRIGGER", config.trigger);
00173 config.reaction_len = defaults->get("REACTION_LEN", config.reaction_len);
00174 config.decay_len = defaults->get("DECAY_LEN", config.decay_len);
00175 config.smoothing_only = defaults->get("SMOOTHING_ONLY", config.smoothing_only);
00176 config.input = defaults->get("INPUT", config.input);
00177
00178 config.levels.remove_all();
00179 int total_levels = defaults->get("TOTAL_LEVELS", 0);
00180 for(int i = 0; i < total_levels; i++)
00181 {
00182 config.levels.append();
00183 sprintf(string, "X_%d", i);
00184 config.levels.values[i].x = defaults->get(string, (double)0);
00185 sprintf(string, "Y_%d", i);
00186 config.levels.values[i].y = defaults->get(string, (double)0);
00187 }
00188
00189 return 0;
00190 }
00191
00192 int CompressorEffect::save_defaults()
00193 {
00194 char string[BCTEXTLEN];
00195
00196 defaults->update("TRIGGER", config.trigger);
00197 defaults->update("REACTION_LEN", config.reaction_len);
00198 defaults->update("DECAY_LEN", config.decay_len);
00199 defaults->update("SMOOTHING_ONLY", config.smoothing_only);
00200 defaults->update("TOTAL_LEVELS", config.levels.total);
00201 defaults->update("INPUT", config.input);
00202
00203 defaults->update("TOTAL_LEVELS", config.levels.total);
00204 for(int i = 0; i < config.levels.total; i++)
00205 {
00206 sprintf(string, "X_%d", i);
00207 defaults->update(string, config.levels.values[i].x);
00208 sprintf(string, "Y_%d", i);
00209 defaults->update(string, config.levels.values[i].y);
00210 }
00211
00212 defaults->save();
00213
00214 return 0;
00215 }
00216
00217
00218 void CompressorEffect::update_gui()
00219 {
00220 if(thread)
00221 {
00222 if(load_configuration())
00223 {
00224 thread->window->lock_window("CompressorEffect::update_gui");
00225 thread->window->update();
00226 thread->window->unlock_window();
00227 }
00228 }
00229 }
00230
00231
00232 NEW_PICON_MACRO(CompressorEffect)
00233 SHOW_GUI_MACRO(CompressorEffect, CompressorThread)
00234 RAISE_WINDOW_MACRO(CompressorEffect)
00235 SET_STRING_MACRO(CompressorEffect)
00236 LOAD_CONFIGURATION_MACRO(CompressorEffect, CompressorConfig)
00237
00238
00239
00240
00241
00242 int CompressorEffect::process_buffer(int64_t size,
00243 double **buffer,
00244 int64_t start_position,
00245 int sample_rate)
00246 {
00247 load_configuration();
00248
00249
00250 levels.remove_all();
00251 for(int i = 0; i < config.levels.total; i++)
00252 {
00253 levels.append();
00254 levels.values[i].x = DB::fromdb(config.levels.values[i].x);
00255 levels.values[i].y = DB::fromdb(config.levels.values[i].y);
00256 }
00257 min_x = DB::fromdb(config.min_db);
00258 min_y = DB::fromdb(config.min_db);
00259 max_x = 1.0;
00260 max_y = 1.0;
00261
00262
00263 int reaction_samples = (int)(config.reaction_len * sample_rate + 0.5);
00264 int decay_samples = (int)(config.decay_len * sample_rate + 0.5);
00265 int trigger = CLIP(config.trigger, 0, PluginAClient::total_in_buffers - 1);
00266
00267 CLAMP(reaction_samples, -1000000, 1000000);
00268 CLAMP(decay_samples, reaction_samples, 1000000);
00269 CLAMP(decay_samples, 1, 1000000);
00270 if(labs(reaction_samples) < 1) reaction_samples = 1;
00271 if(labs(decay_samples) < 1) decay_samples = 1;
00272
00273 int total_buffers = get_total_buffers();
00274 if(reaction_samples > 0)
00275 {
00276 if(target_current_sample < 0) target_current_sample = reaction_samples;
00277 for(int i = 0; i < total_buffers; i++)
00278 {
00279 read_samples(buffer[i],
00280 i,
00281 sample_rate,
00282 start_position,
00283 size);
00284 }
00285
00286 double current_slope = (next_target - previous_target) /
00287 reaction_samples;
00288 double *trigger_buffer = buffer[trigger];
00289 for(int i = 0; i < size; i++)
00290 {
00291
00292
00293 double sample;
00294 switch(config.input)
00295 {
00296 case CompressorConfig::MAX:
00297 {
00298 double max = 0;
00299 for(int j = 0; j < total_buffers; j++)
00300 {
00301 sample = fabs(buffer[j][i]);
00302 if(sample > max) max = sample;
00303 }
00304 sample = max;
00305 break;
00306 }
00307
00308 case CompressorConfig::TRIGGER:
00309 sample = fabs(trigger_buffer[i]);
00310 break;
00311
00312 case CompressorConfig::SUM:
00313 {
00314 double max = 0;
00315 for(int j = 0; j < total_buffers; j++)
00316 {
00317 sample = fabs(buffer[j][i]);
00318 max += sample;
00319 }
00320 sample = max;
00321 break;
00322 }
00323 }
00324
00325 double new_slope = (sample - current_value) /
00326 reaction_samples;
00327
00328
00329 if(new_slope >= current_slope &&
00330 (current_slope >= 0 ||
00331 new_slope >= 0))
00332 {
00333 next_target = sample;
00334 previous_target = current_value;
00335 target_current_sample = 0;
00336 target_samples = reaction_samples;
00337 current_slope = new_slope;
00338 }
00339 else
00340 if(sample > next_target && current_slope < 0)
00341 {
00342 next_target = sample;
00343 previous_target = current_value;
00344 target_current_sample = 0;
00345 target_samples = decay_samples;
00346 current_slope = (sample - current_value) / decay_samples;
00347 }
00348
00349 if(target_current_sample >= target_samples)
00350 {
00351 next_target = sample;
00352 previous_target = current_value;
00353 target_current_sample = 0;
00354 target_samples = decay_samples;
00355 current_slope = (sample - current_value) / decay_samples;
00356 }
00357
00358
00359 current_value = (next_target * target_current_sample +
00360 previous_target * (target_samples - target_current_sample)) /
00361 target_samples;
00362
00363 target_current_sample++;
00364
00365 if(config.smoothing_only)
00366 {
00367 for(int j = 0; j < total_buffers; j++)
00368 buffer[j][i] = current_value;
00369 }
00370 else
00371 {
00372 double gain = calculate_gain(current_value);
00373 for(int j = 0; j < total_buffers; j++)
00374 {
00375 buffer[j][i] *= gain;
00376 }
00377 }
00378 }
00379 }
00380 else
00381 {
00382 if(target_current_sample < 0) target_current_sample = target_samples;
00383 int64_t preview_samples = -reaction_samples;
00384
00385
00386 if(start_position < input_start ||
00387 start_position >= input_start + input_size)
00388 {
00389 input_size = 0;
00390 input_start = start_position;
00391 }
00392 else
00393
00394 if(start_position > input_start &&
00395 start_position < input_start + input_size)
00396 {
00397 if(input_buffer)
00398 {
00399 int len = input_start + input_size - start_position;
00400 for(int i = 0; i < total_buffers; i++)
00401 {
00402 memcpy(input_buffer[i],
00403 input_buffer[i] + (start_position - input_start),
00404 len * sizeof(double));
00405 }
00406 input_size = len;
00407 input_start = start_position;
00408 }
00409 }
00410
00411
00412 if(size + preview_samples > input_allocated)
00413 {
00414 double **new_input_buffer = new double*[total_buffers];
00415 for(int i = 0; i < total_buffers; i++)
00416 {
00417 new_input_buffer[i] = new double[size + preview_samples];
00418 if(input_buffer)
00419 {
00420 memcpy(new_input_buffer[i],
00421 input_buffer[i],
00422 input_size * sizeof(double));
00423 delete [] input_buffer[i];
00424 }
00425 }
00426 if(input_buffer) delete [] input_buffer;
00427
00428 input_allocated = size + preview_samples;
00429 input_buffer = new_input_buffer;
00430 }
00431
00432
00433 #define MAX_FRAGMENT_SIZE 131072
00434 while(input_size < size + preview_samples)
00435 {
00436 int fragment_size = MAX_FRAGMENT_SIZE;
00437 if(fragment_size + input_size > size + preview_samples)
00438 fragment_size = size + preview_samples - input_size;
00439 for(int i = 0; i < total_buffers; i++)
00440 {
00441 read_samples(input_buffer[i] + input_size,
00442 i,
00443 sample_rate,
00444 input_start + input_size,
00445 fragment_size);
00446 }
00447 input_size += fragment_size;
00448 }
00449
00450
00451 double current_slope = (next_target - previous_target) /
00452 target_samples;
00453 double *trigger_buffer = input_buffer[trigger];
00454 for(int i = 0; i < size; i++)
00455 {
00456
00457
00458
00459
00460
00461
00462 int first_slope = preview_samples - 1;
00463
00464 if(target_current_sample >= target_samples)
00465 first_slope = 1;
00466 for(int j = first_slope;
00467 j < preview_samples;
00468 j++)
00469 {
00470 double sample;
00471 switch(config.input)
00472 {
00473 case CompressorConfig::MAX:
00474 {
00475 double max = 0;
00476 for(int k = 0; k < total_buffers; k++)
00477 {
00478 sample = fabs(input_buffer[k][i + j]);
00479 if(sample > max) max = sample;
00480 }
00481 sample = max;
00482 break;
00483 }
00484
00485 case CompressorConfig::TRIGGER:
00486 sample = fabs(trigger_buffer[i + j]);
00487 break;
00488
00489 case CompressorConfig::SUM:
00490 {
00491 double max = 0;
00492 for(int k = 0; k < total_buffers; k++)
00493 {
00494 sample = fabs(input_buffer[k][i + j]);
00495 max += sample;
00496 }
00497 sample = max;
00498 break;
00499 }
00500 }
00501
00502
00503
00504
00505
00506
00507 double new_slope = (sample - current_value) /
00508 j;
00509
00510 if(new_slope >= current_slope &&
00511 (current_slope >= 0 ||
00512 new_slope >= 0))
00513 {
00514 target_current_sample = 0;
00515 target_samples = j;
00516 current_slope = new_slope;
00517 next_target = sample;
00518 previous_target = current_value;
00519 }
00520 else
00521 if(sample > next_target && current_slope < 0)
00522 {
00523 target_current_sample = 0;
00524 target_samples = decay_samples;
00525 current_slope = (sample - current_value) /
00526 decay_samples;
00527 next_target = sample;
00528 previous_target = current_value;
00529 }
00530
00531
00532 if(target_current_sample >= target_samples)
00533 {
00534 target_current_sample = 0;
00535 target_samples = decay_samples;
00536 current_slope = (sample - current_value) / decay_samples;
00537 next_target = sample;
00538 previous_target = current_value;
00539 }
00540 }
00541
00542
00543 current_value = (next_target * target_current_sample +
00544 previous_target * (target_samples - target_current_sample)) /
00545 target_samples;
00546
00547 target_current_sample++;
00548
00549 if(config.smoothing_only)
00550 {
00551 for(int j = 0; j < total_buffers; j++)
00552 buffer[j][i] = current_value;
00553 }
00554 else
00555 {
00556 double gain = calculate_gain(current_value);
00557 for(int j = 0; j < total_buffers; j++)
00558 {
00559 buffer[j][i] = input_buffer[j][i] * gain;
00560 }
00561 }
00562 }
00563
00564
00565
00566 }
00567
00568
00569
00570
00571
00572 return 0;
00573 }
00574
00575 double CompressorEffect::calculate_output(double x)
00576 {
00577 if(x > 0.999) return 1.0;
00578
00579 for(int i = levels.total - 1; i >= 0; i--)
00580 {
00581 if(levels.values[i].x <= x)
00582 {
00583 if(i < levels.total - 1)
00584 {
00585 return levels.values[i].y +
00586 (x - levels.values[i].x) *
00587 (levels.values[i + 1].y - levels.values[i].y) /
00588 (levels.values[i + 1].x - levels.values[i].x);
00589 }
00590 else
00591 {
00592 return levels.values[i].y +
00593 (x - levels.values[i].x) *
00594 (max_y - levels.values[i].y) /
00595 (max_x - levels.values[i].x);
00596 }
00597 }
00598 }
00599
00600 if(levels.total)
00601 {
00602 return min_y +
00603 (x - min_x) *
00604 (levels.values[0].y - min_y) /
00605 (levels.values[0].x - min_x);
00606 }
00607 else
00608 return x;
00609 }
00610
00611
00612 double CompressorEffect::calculate_gain(double input)
00613 {
00614
00615
00616
00617 double y_linear = calculate_output(input);
00618 double gain;
00619 if(input != 0)
00620 gain = y_linear / input;
00621 else
00622 gain = 100000;
00623 return gain;
00624 }
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635 CompressorConfig::CompressorConfig()
00636 {
00637 reaction_len = 1.0;
00638 min_db = -80.0;
00639 min_x = min_db;
00640 min_y = min_db;
00641 max_x = 0;
00642 max_y = 0;
00643 trigger = 0;
00644 input = CompressorConfig::TRIGGER;
00645 smoothing_only = 0;
00646 decay_len = 1.0;
00647 }
00648
00649 void CompressorConfig::copy_from(CompressorConfig &that)
00650 {
00651 this->reaction_len = that.reaction_len;
00652 this->decay_len = that.decay_len;
00653 this->min_db = that.min_db;
00654 this->min_x = that.min_x;
00655 this->min_y = that.min_y;
00656 this->max_x = that.max_x;
00657 this->max_y = that.max_y;
00658 this->trigger = that.trigger;
00659 this->input = that.input;
00660 this->smoothing_only = that.smoothing_only;
00661 levels.remove_all();
00662 for(int i = 0; i < that.levels.total; i++)
00663 this->levels.append(that.levels.values[i]);
00664 }
00665
00666 int CompressorConfig::equivalent(CompressorConfig &that)
00667 {
00668 if(!EQUIV(this->reaction_len, that.reaction_len) ||
00669 !EQUIV(this->decay_len, that.decay_len) ||
00670 this->trigger != that.trigger ||
00671 this->input != that.input ||
00672 this->smoothing_only != that.smoothing_only)
00673 return 0;
00674 if(this->levels.total != that.levels.total) return 0;
00675 for(int i = 0;
00676 i < this->levels.total && i < that.levels.total;
00677 i++)
00678 {
00679 compressor_point_t *this_level = &this->levels.values[i];
00680 compressor_point_t *that_level = &that.levels.values[i];
00681 if(!EQUIV(this_level->x, that_level->x) ||
00682 !EQUIV(this_level->y, that_level->y))
00683 return 0;
00684 }
00685 return 1;
00686 }
00687
00688 void CompressorConfig::interpolate(CompressorConfig &prev,
00689 CompressorConfig &next,
00690 int64_t prev_frame,
00691 int64_t next_frame,
00692 int64_t current_frame)
00693 {
00694 copy_from(prev);
00695 }
00696
00697 int CompressorConfig::total_points()
00698 {
00699 if(!levels.total)
00700 return 1;
00701 else
00702 return levels.total;
00703 }
00704
00705 void CompressorConfig::dump()
00706 {
00707 printf("CompressorConfig::dump\n");
00708 for(int i = 0; i < levels.total; i++)
00709 {
00710 printf(" %f %f\n", levels.values[i].x, levels.values[i].y);
00711 }
00712 }
00713
00714
00715 double CompressorConfig::get_y(int number)
00716 {
00717 if(!levels.total)
00718 return 1.0;
00719 else
00720 if(number >= levels.total)
00721 return levels.values[levels.total - 1].y;
00722 else
00723 return levels.values[number].y;
00724 }
00725
00726 double CompressorConfig::get_x(int number)
00727 {
00728 if(!levels.total)
00729 return 0.0;
00730 else
00731 if(number >= levels.total)
00732 return levels.values[levels.total - 1].x;
00733 else
00734 return levels.values[number].x;
00735 }
00736
00737 double CompressorConfig::calculate_db(double x)
00738 {
00739 if(x > -0.001) return 0.0;
00740
00741 for(int i = levels.total - 1; i >= 0; i--)
00742 {
00743 if(levels.values[i].x <= x)
00744 {
00745 if(i < levels.total - 1)
00746 {
00747 return levels.values[i].y +
00748 (x - levels.values[i].x) *
00749 (levels.values[i + 1].y - levels.values[i].y) /
00750 (levels.values[i + 1].x - levels.values[i].x);
00751 }
00752 else
00753 {
00754 return levels.values[i].y +
00755 (x - levels.values[i].x) *
00756 (max_y - levels.values[i].y) /
00757 (max_x - levels.values[i].x);
00758 }
00759 }
00760 }
00761
00762 if(levels.total)
00763 {
00764 return min_y +
00765 (x - min_x) *
00766 (levels.values[0].y - min_y) /
00767 (levels.values[0].x - min_x);
00768 }
00769 else
00770 return x;
00771 }
00772
00773
00774 int CompressorConfig::set_point(double x, double y)
00775 {
00776 for(int i = levels.total - 1; i >= 0; i--)
00777 {
00778 if(levels.values[i].x < x)
00779 {
00780 levels.append();
00781 i++;
00782 for(int j = levels.total - 2; j >= i; j--)
00783 {
00784 levels.values[j + 1] = levels.values[j];
00785 }
00786 levels.values[i].x = x;
00787 levels.values[i].y = y;
00788
00789 return i;
00790 }
00791 }
00792
00793 levels.append();
00794 for(int j = levels.total - 2; j >= 0; j--)
00795 {
00796 levels.values[j + 1] = levels.values[j];
00797 }
00798 levels.values[0].x = x;
00799 levels.values[0].y = y;
00800 return 0;
00801 }
00802
00803 void CompressorConfig::remove_point(int number)
00804 {
00805 for(int j = number; j < levels.total - 1; j++)
00806 {
00807 levels.values[j] = levels.values[j + 1];
00808 }
00809 levels.remove();
00810 }
00811
00812 void CompressorConfig::optimize()
00813 {
00814 int done = 0;
00815
00816 while(!done)
00817 {
00818 done = 1;
00819
00820
00821 for(int i = 0; i < levels.total - 1; i++)
00822 {
00823 if(levels.values[i].x >= levels.values[i + 1].x)
00824 {
00825 done = 0;
00826 for(int j = i + 1; j < levels.total - 1; j++)
00827 {
00828 levels.values[j] = levels.values[j + 1];
00829 }
00830 levels.remove();
00831 }
00832 }
00833
00834 }
00835 }
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850 PLUGIN_THREAD_OBJECT(CompressorEffect, CompressorThread, CompressorWindow)
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864 CompressorWindow::CompressorWindow(CompressorEffect *plugin, int x, int y)
00865 : BC_Window(plugin->gui_string,
00866 x,
00867 y,
00868 650,
00869 480,
00870 650,
00871 480,
00872 0,
00873 0,
00874 1)
00875 {
00876 this->plugin = plugin;
00877 }
00878
00879 void CompressorWindow::create_objects()
00880 {
00881 int x = 35, y = 10;
00882 int control_margin = 130;
00883
00884 add_subwindow(canvas = new CompressorCanvas(plugin,
00885 x,
00886 y,
00887 get_w() - x - control_margin - 10,
00888 get_h() - y - 70));
00889 canvas->set_cursor(CROSS_CURSOR);
00890 x = get_w() - control_margin;
00891 add_subwindow(new BC_Title(x, y, _("Reaction secs:")));
00892 y += 20;
00893 add_subwindow(reaction = new CompressorReaction(plugin, x, y));
00894 y += 30;
00895 add_subwindow(new BC_Title(x, y, _("Decay secs:")));
00896 y += 20;
00897 add_subwindow(decay = new CompressorDecay(plugin, x, y));
00898 y += 30;
00899 add_subwindow(new BC_Title(x, y, _("Trigger Type:")));
00900 y += 20;
00901 add_subwindow(input = new CompressorInput(plugin, x, y));
00902 input->create_objects();
00903 y += 30;
00904 add_subwindow(new BC_Title(x, y, _("Trigger:")));
00905 y += 20;
00906 add_subwindow(trigger = new CompressorTrigger(plugin, x, y));
00907 if(plugin->config.input != CompressorConfig::TRIGGER) trigger->disable();
00908 y += 30;
00909 add_subwindow(smooth = new CompressorSmooth(plugin, x, y));
00910 y += 60;
00911 add_subwindow(clear = new CompressorClear(plugin, x, y));
00912 x = 10;
00913 y = get_h() - 40;
00914 add_subwindow(new BC_Title(x, y, _("Point:")));
00915 x += 50;
00916 add_subwindow(x_text = new CompressorX(plugin, x, y));
00917 x += 110;
00918 add_subwindow(new BC_Title(x, y, _("x")));
00919 x += 20;
00920 add_subwindow(y_text = new CompressorY(plugin, x, y));
00921 draw_scales();
00922
00923 update_canvas();
00924 show_window();
00925 flush();
00926 }
00927
00928 WINDOW_CLOSE_EVENT(CompressorWindow)
00929
00930 void CompressorWindow::draw_scales()
00931 {
00932 set_font(SMALLFONT);
00933 set_color(get_resources()->default_text_color);
00934
00935 #define DIVISIONS 8
00936 for(int i = 0; i <= DIVISIONS; i++)
00937 {
00938 int y = canvas->get_y() + 10 + canvas->get_h() / DIVISIONS * i;
00939 int x = canvas->get_x() - 30;
00940 char string[BCTEXTLEN];
00941
00942 sprintf(string, "%.0f", (float)i / DIVISIONS * plugin->config.min_db);
00943 draw_text(x, y, string);
00944
00945 int y1 = canvas->get_y() + canvas->get_h() / DIVISIONS * i;
00946 int y2 = canvas->get_y() + canvas->get_h() / DIVISIONS * (i + 1);
00947 for(int j = 0; j < 10; j++)
00948 {
00949 y = y1 + (y2 - y1) * j / 10;
00950 if(j == 0)
00951 {
00952 draw_line(canvas->get_x() - 10, y, canvas->get_x(), y);
00953 }
00954 else
00955 if(i < DIVISIONS)
00956 {
00957 draw_line(canvas->get_x() - 5, y, canvas->get_x(), y);
00958 }
00959 }
00960 }
00961
00962
00963
00964 for(int i = 0; i <= DIVISIONS; i++)
00965 {
00966 int y = canvas->get_h() + 30;
00967 int x = canvas->get_x() + (canvas->get_w() - 10) / DIVISIONS * i;
00968 char string[BCTEXTLEN];
00969
00970 sprintf(string, "%.0f", (1.0 - (float)i / DIVISIONS) * plugin->config.min_db);
00971 draw_text(x, y, string);
00972
00973 int x1 = canvas->get_x() + canvas->get_w() / DIVISIONS * i;
00974 int x2 = canvas->get_x() + canvas->get_w() / DIVISIONS * (i + 1);
00975 for(int j = 0; j < 10; j++)
00976 {
00977 x = x1 + (x2 - x1) * j / 10;
00978 if(j == 0)
00979 {
00980 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + 10);
00981 }
00982 else
00983 if(i < DIVISIONS)
00984 {
00985 draw_line(x, canvas->get_y() + canvas->get_h(), x, canvas->get_y() + canvas->get_h() + 5);
00986 }
00987 }
00988 }
00989
00990
00991
00992 flash();
00993 }
00994
00995 void CompressorWindow::update()
00996 {
00997 update_textboxes();
00998 update_canvas();
00999 }
01000
01001 void CompressorWindow::update_textboxes()
01002 {
01003 if(atol(trigger->get_text()) != plugin->config.trigger)
01004 trigger->update((int64_t)plugin->config.trigger);
01005 if(strcmp(input->get_text(), CompressorInput::value_to_text(plugin->config.input)))
01006 input->set_text(CompressorInput::value_to_text(plugin->config.input));
01007
01008 if(plugin->config.input != CompressorConfig::TRIGGER && trigger->get_enabled())
01009 trigger->disable();
01010 else
01011 if(plugin->config.input == CompressorConfig::TRIGGER && !trigger->get_enabled())
01012 trigger->enable();
01013
01014 if(!EQUIV(atof(reaction->get_text()), plugin->config.reaction_len))
01015 reaction->update((float)plugin->config.reaction_len);
01016 if(!EQUIV(atof(decay->get_text()), plugin->config.decay_len))
01017 decay->update((float)plugin->config.decay_len);
01018 smooth->update(plugin->config.smoothing_only);
01019 if(canvas->current_operation == CompressorCanvas::DRAG)
01020 {
01021 x_text->update((float)plugin->config.levels.values[canvas->current_point].x);
01022 y_text->update((float)plugin->config.levels.values[canvas->current_point].y);
01023 }
01024 }
01025
01026 #define POINT_W 10
01027 void CompressorWindow::update_canvas()
01028 {
01029 int y1, y2;
01030
01031
01032 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
01033 canvas->set_color(GREEN);
01034 for(int i = 1; i < DIVISIONS; i++)
01035 {
01036 int y = canvas->get_h() * i / DIVISIONS;
01037 canvas->draw_line(0, y, canvas->get_w(), y);
01038
01039 int x = canvas->get_w() * i / DIVISIONS;
01040 canvas->draw_line(x, 0, x, canvas->get_h());
01041 }
01042
01043
01044 canvas->set_font(MEDIUMFONT);
01045 canvas->draw_text(5,
01046 canvas->get_h() / 2 - 20,
01047 _("Output"));
01048 canvas->draw_text(canvas->get_w() / 2 - canvas->get_text_width(MEDIUMFONT, _("Input level")) / 2,
01049 canvas->get_h() - canvas->get_text_height(MEDIUMFONT),
01050 _("Input"));
01051
01052
01053 canvas->set_color(BLACK);
01054 for(int i = 0; i < canvas->get_w(); i++)
01055 {
01056 double x_db = ((double)1 - (double)i / canvas->get_w()) * plugin->config.min_db;
01057 double y_db = plugin->config.calculate_db(x_db);
01058 y2 = (int)(y_db / plugin->config.min_db * canvas->get_h());
01059
01060 if(i > 0)
01061 {
01062 canvas->draw_line(i - 1, y1, i, y2);
01063 }
01064
01065 y1 = y2;
01066 }
01067
01068 int total = plugin->config.levels.total ? plugin->config.levels.total : 1;
01069 for(int i = 0; i < plugin->config.levels.total; i++)
01070 {
01071 double x_db = plugin->config.get_x(i);
01072 double y_db = plugin->config.get_y(i);
01073
01074 int x = (int)(((double)1 - x_db / plugin->config.min_db) * canvas->get_w());
01075 int y = (int)(y_db / plugin->config.min_db * canvas->get_h());
01076
01077 canvas->draw_box(x - POINT_W / 2, y - POINT_W / 2, POINT_W, POINT_W);
01078 }
01079
01080 canvas->flash();
01081 canvas->flush();
01082 }
01083
01084
01085
01086
01087
01088
01089
01090
01091 CompressorCanvas::CompressorCanvas(CompressorEffect *plugin, int x, int y, int w, int h)
01092 : BC_SubWindow(x, y, w, h, WHITE)
01093 {
01094 this->plugin = plugin;
01095 }
01096
01097 int CompressorCanvas::button_press_event()
01098 {
01099
01100 if(is_event_win() && cursor_inside())
01101 {
01102 for(int i = 0; i < plugin->config.levels.total; i++)
01103 {
01104 double x_db = plugin->config.get_x(i);
01105 double y_db = plugin->config.get_y(i);
01106
01107 int x = (int)(((double)1 - x_db / plugin->config.min_db) * get_w());
01108 int y = (int)(y_db / plugin->config.min_db * get_h());
01109
01110 if(get_cursor_x() < x + POINT_W / 2 && get_cursor_x() >= x - POINT_W / 2 &&
01111 get_cursor_y() < y + POINT_W / 2 && get_cursor_y() >= y - POINT_W / 2)
01112 {
01113 current_operation = DRAG;
01114 current_point = i;
01115 return 1;
01116 }
01117 }
01118
01119
01120
01121
01122 double x_db = (double)(1 - (double)get_cursor_x() / get_w()) * plugin->config.min_db;
01123 double y_db = (double)get_cursor_y() / get_h() * plugin->config.min_db;
01124
01125 current_point = plugin->config.set_point(x_db, y_db);
01126 current_operation = DRAG;
01127 plugin->thread->window->update();
01128 plugin->send_configure_change();
01129 return 1;
01130 }
01131 return 0;
01132
01133 }
01134
01135 int CompressorCanvas::button_release_event()
01136 {
01137 if(current_operation == DRAG)
01138 {
01139 if(current_point > 0)
01140 {
01141 if(plugin->config.levels.values[current_point].x <
01142 plugin->config.levels.values[current_point - 1].x)
01143 plugin->config.remove_point(current_point);
01144 }
01145
01146 if(current_point < plugin->config.levels.total - 1)
01147 {
01148 if(plugin->config.levels.values[current_point].x >=
01149 plugin->config.levels.values[current_point + 1].x)
01150 plugin->config.remove_point(current_point);
01151 }
01152
01153 plugin->thread->window->update();
01154 plugin->send_configure_change();
01155 current_operation = NONE;
01156 return 1;
01157 }
01158
01159 return 0;
01160 }
01161
01162 int CompressorCanvas::cursor_motion_event()
01163 {
01164 if(current_operation == DRAG)
01165 {
01166 int x = get_cursor_x();
01167 int y = get_cursor_y();
01168 CLAMP(x, 0, get_w());
01169 CLAMP(y, 0, get_h());
01170 double x_db = (double)(1 - (double)x / get_w()) * plugin->config.min_db;
01171 double y_db = (double)y / get_h() * plugin->config.min_db;
01172 plugin->config.levels.values[current_point].x = x_db;
01173 plugin->config.levels.values[current_point].y = y_db;
01174 plugin->thread->window->update();
01175 plugin->send_configure_change();
01176 return 1;
01177
01178 }
01179 return 0;
01180 }
01181
01182
01183
01184
01185
01186 CompressorReaction::CompressorReaction(CompressorEffect *plugin, int x, int y)
01187 : BC_TextBox(x, y, 100, 1, (float)plugin->config.reaction_len)
01188 {
01189 this->plugin = plugin;
01190 }
01191
01192 int CompressorReaction::handle_event()
01193 {
01194 plugin->config.reaction_len = atof(get_text());
01195 plugin->send_configure_change();
01196 return 1;
01197 }
01198
01199 int CompressorReaction::button_press_event()
01200 {
01201 if(is_event_win())
01202 {
01203 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
01204 if(get_buttonpress() == 4)
01205 {
01206 plugin->config.reaction_len += 0.1;
01207 }
01208 else
01209 if(get_buttonpress() == 5)
01210 {
01211 plugin->config.reaction_len -= 0.1;
01212 }
01213 update((float)plugin->config.reaction_len);
01214 plugin->send_configure_change();
01215 return 1;
01216 }
01217 return 0;
01218 }
01219
01220 CompressorDecay::CompressorDecay(CompressorEffect *plugin, int x, int y)
01221 : BC_TextBox(x, y, 100, 1, (float)plugin->config.decay_len)
01222 {
01223 this->plugin = plugin;
01224 }
01225 int CompressorDecay::handle_event()
01226 {
01227 plugin->config.decay_len = atof(get_text());
01228 plugin->send_configure_change();
01229 return 1;
01230 }
01231
01232 int CompressorDecay::button_press_event()
01233 {
01234 if(is_event_win())
01235 {
01236 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
01237 if(get_buttonpress() == 4)
01238 {
01239 plugin->config.decay_len += 0.1;
01240 }
01241 else
01242 if(get_buttonpress() == 5)
01243 {
01244 plugin->config.decay_len -= 0.1;
01245 }
01246 update((float)plugin->config.decay_len);
01247 plugin->send_configure_change();
01248 return 1;
01249 }
01250 return 0;
01251 }
01252
01253
01254
01255 CompressorX::CompressorX(CompressorEffect *plugin, int x, int y)
01256 : BC_TextBox(x, y, 100, 1, "")
01257 {
01258 this->plugin = plugin;
01259 }
01260 int CompressorX::handle_event()
01261 {
01262 int current_point = plugin->thread->window->canvas->current_point;
01263 if(current_point < plugin->config.levels.total)
01264 {
01265 plugin->config.levels.values[current_point].x = atof(get_text());
01266 plugin->thread->window->update_canvas();
01267 plugin->send_configure_change();
01268 }
01269 return 1;
01270 }
01271
01272
01273
01274 CompressorY::CompressorY(CompressorEffect *plugin, int x, int y)
01275 : BC_TextBox(x, y, 100, 1, "")
01276 {
01277 this->plugin = plugin;
01278 }
01279 int CompressorY::handle_event()
01280 {
01281 int current_point = plugin->thread->window->canvas->current_point;
01282 if(current_point < plugin->config.levels.total)
01283 {
01284 plugin->config.levels.values[current_point].y = atof(get_text());
01285 plugin->thread->window->update_canvas();
01286 plugin->send_configure_change();
01287 }
01288 return 1;
01289 }
01290
01291
01292
01293
01294
01295 CompressorTrigger::CompressorTrigger(CompressorEffect *plugin, int x, int y)
01296 : BC_TextBox(x, y, (int64_t)100, (int64_t)1, (int64_t)plugin->config.trigger)
01297 {
01298 this->plugin = plugin;
01299 }
01300 int CompressorTrigger::handle_event()
01301 {
01302 plugin->config.trigger = atol(get_text());
01303 plugin->send_configure_change();
01304 return 1;
01305 }
01306
01307 int CompressorTrigger::button_press_event()
01308 {
01309 if(is_event_win())
01310 {
01311 if(get_buttonpress() < 4) return BC_TextBox::button_press_event();
01312 if(get_buttonpress() == 4)
01313 {
01314 plugin->config.trigger++;
01315 }
01316 else
01317 if(get_buttonpress() == 5)
01318 {
01319 plugin->config.trigger--;
01320 }
01321 update((int64_t)plugin->config.trigger);
01322 plugin->send_configure_change();
01323 return 1;
01324 }
01325 return 0;
01326 }
01327
01328
01329
01330
01331
01332 CompressorInput::CompressorInput(CompressorEffect *plugin, int x, int y)
01333 : BC_PopupMenu(x,
01334 y,
01335 100,
01336 CompressorInput::value_to_text(plugin->config.input),
01337 1)
01338 {
01339 this->plugin = plugin;
01340 }
01341 int CompressorInput::handle_event()
01342 {
01343 plugin->config.input = text_to_value(get_text());
01344 plugin->thread->window->update();
01345 plugin->send_configure_change();
01346 return 1;
01347 }
01348
01349 void CompressorInput::create_objects()
01350 {
01351 for(int i = 0; i < 3; i++)
01352 {
01353 add_item(new BC_MenuItem(value_to_text(i)));
01354 }
01355 }
01356
01357 char* CompressorInput::value_to_text(int value)
01358 {
01359 switch(value)
01360 {
01361 case CompressorConfig::TRIGGER: return "Trigger";
01362 case CompressorConfig::MAX: return "Maximum";
01363 case CompressorConfig::SUM: return "Total";
01364 }
01365
01366 return "Trigger";
01367 }
01368
01369 int CompressorInput::text_to_value(char *text)
01370 {
01371 for(int i = 0; i < 3; i++)
01372 {
01373 if(!strcmp(value_to_text(i), text)) return i;
01374 }
01375
01376 return CompressorConfig::TRIGGER;
01377 }
01378
01379
01380
01381
01382
01383
01384 CompressorClear::CompressorClear(CompressorEffect *plugin, int x, int y)
01385 : BC_GenericButton(x, y, _("Clear"))
01386 {
01387 this->plugin = plugin;
01388 }
01389
01390 int CompressorClear::handle_event()
01391 {
01392 plugin->config.levels.remove_all();
01393
01394 plugin->thread->window->update();
01395 plugin->send_configure_change();
01396 return 1;
01397 }
01398
01399
01400
01401 CompressorSmooth::CompressorSmooth(CompressorEffect *plugin, int x, int y)
01402 : BC_CheckBox(x, y, plugin->config.smoothing_only, _("Smooth only"))
01403 {
01404 this->plugin = plugin;
01405 }
01406
01407 int CompressorSmooth::handle_event()
01408 {
01409 plugin->config.smoothing_only = get_value();
01410 plugin->send_configure_change();
01411 return 1;
01412 }
01413
01414
01415
01416