• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files

hvirtual/plugins/compressor/compressor.C

Go to the documentation of this file.
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 // More potential compressor algorithms:
00028 // Use single reaction time parameter.  Negative reaction time uses 
00029 // readahead.  Positive reaction time uses slope.
00030 
00031 // Smooth input stage if readahead.
00032 // Determine slope from current smoothed sample to every sample in readahead area.
00033 // Once highest slope is found, count of number of samples remaining until it is
00034 // reached.  Only search after this count for the next highest slope.
00035 // Use highest slope to determine smoothed value.
00036 
00037 // Smooth input stage if not readahead.
00038 // For every sample, calculate slope needed to reach current sample from 
00039 // current smoothed value in the reaction time.  If higher than current slope,
00040 // make it the current slope and count number of samples remaining until it is
00041 // reached.  If this count is met and no higher slopes are found, base slope
00042 // on current sample when count is met.
00043 
00044 // Gain stage.
00045 // For every sample, calculate gain from smoothed input value.
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 //config.dump();
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 // Calculate linear transfer from db 
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 // Get slope required to reach current sample from smoothed sample over reaction
00292 // length.
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 // Slope greater than current slope
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 // Current smoothed sample came up without finding higher slope
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 // Update current value and store gain
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 // Start of new buffer is outside the current buffer.  Start buffer over.
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 // Shift current buffer so the buffer starts on start_position
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 // Expand buffer to handle preview size
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 // Append data to input buffer to construct readahead area.
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 // Get slope from current sample to every sample in preview_samples.
00457 // Take highest one or first one after target_samples are up.
00458 
00459 // For optimization, calculate the first slope we really need.
00460 // Assume every slope up to the end of preview_samples has been calculated and
00461 // found <= to current slope.
00462             int first_slope = preview_samples - 1;
00463 // Need new slope immediately
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 // Got equal or higher slope
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 // Hit end of current slope range without finding higher slope
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 // Update current value and multiply gain
00543                         current_value = (next_target * target_current_sample +
00544                                 previous_target * (target_samples - target_current_sample)) /
00545                                 target_samples;
00546 //buffer[0][i] = current_value;
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 //      double x_db = DB::todb(input);
00615 //      double y_db = config.calculate_db(x_db);
00616 //      double y_linear = DB::fromdb(y_db);
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->