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->