Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1129 lines (892 sloc) 29.3 KB
/* Copyright 2013 Kjetil S. Matheussen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifdef INCLUDE_MYLINE_AND_MYRECT
namespace cvs{
struct MyLine{
int x1,y1,x2,y2;
MyLine(int x1,int y1, int x2, int y2)
: x1(x1),y1(y1),x2(x2),y2(y2)
{}
};
struct MyRect{
int x1,y1,x2,y2;
MyRect(int x1,int y1, int x2, int y2)
: x1(x1),y1(y1),x2(x2),y2(y2)
{}
int width() const {return x2-x1;}
int height() const {return y2-y1;}
};
}
#else // INCLUDE_MYLINE_AND_MYRECT
#include <string>
#define GLIDING_PEAK_AREA 0 // Set to 1 to get gliding peaks area, which most meters use. Looks good, but I think it is slightly more confusing than the array used now.
const int min_db = -40;
const int max_db = 40;
const int k_timer_interval = 25;
const int max_attack_release = 500;
const float def_threshold = 0.7f;
const float def_ratio = 0.3;
const float def_makeupgain = 0.3f;
namespace cvs{
struct Comp : public MyWidget, public MyTimer{
struct Patch *_patch;
// All values have the value 0 at top, and 1 at bottom, and:
// 0 <= val1 <= val2 <= 1
// (sometimes < instead of <=)
// slider 1
double in_val1; // 0
double in_val2; // threshold
// slider 2
double out_val1; // used to calculate ratio ( ratio = in_val2/(out_val1-in_val2) )
double out_val2; // threshold
// slider 3
double vol_val1; // used to calculate volume ( volume = out_val1/vol_val1 )
double vol_val2; // threshold * volume
// attack/release sliders
double attack_val;
double release_val;
int curr_slider;
struct Box{
int x1,y1,x2,y2;
int p1,p2;
Box(int _x1, int _y1, int _x2, int _y2, float val1,float val2)
: x1(_x1)
, y1(_y1)
, x2(_x2)
, y2(_y2)
{
p1 = scale(val1,0,1,y1,y2);
p2 = scale(val2,0,1,y1,y2);
}
bool inside(int x,int y){
return x>=x1 && x<x2 && y>y1 && y<y2;
}
bool in_val1(int y){
int p=(p1+p2)/2;
return y<p;
}
bool in_val2(int y){
int p=(p1+p2)/2;
return y>=p;
}
};
struct Slider : public Box{
Slider(int _x1, int _y1, int _x2, int _y2, float val1,float val2)
: Box(_x1,_y1,_x2,_y2,val1,val2)
{
p1 = scale(val1,0,1,x1,x2);
}
};
struct Peaks{
const static int num_peaks = 30;//24;
float peaks[num_peaks]; // in dB
int peak_pos;
float last_peak;
#if GLIDING_PEAK_AREA
float min_peak_db, max_peak_db; // in dB
#endif
int min_peak, max_peak; // in pixels
Peaks()
: peak_pos(0)
, last_peak(0)
#if GLIDING_PEAK_AREA
, min_peak_db(0), max_peak_db(0)
#endif
, min_peak(0)
, max_peak(0)
{
for(int i=0;i<num_peaks;i++)
peaks[i]=0.0f;
}
float get_min_peak(){
float ret=max_db;
for(int i=0;i<num_peaks;i++)
if(peaks[i]<ret)
ret=peaks[i];
return ret;
}
float get_max_peak(){
float ret=min_db;
for(int i=0;i<num_peaks;i++)
if(peaks[i]>ret)
ret=peaks[i];
return ret;
}
#if GLIDING_PEAK_AREA
// Trying gliding area instead of array. IMO it is less clear, and it also uses more CPU because of more gfx updates.
void update_peaks(const Box &box, float peak){
const float inc = 0.1;
if(peak>max_peak_db)
max_peak_db = peak;
else
max_peak_db = max_peak_db - inc;
if(peak<min_peak_db)
min_peak_db = peak;
else
min_peak_db = min_peak_db + inc;
if(min_peak_db < min_db)
min_peak_db = min_db;
if(max_peak_db > max_db)
max_peak_db = max_db;
if(min_peak_db>max_peak_db)
min_peak_db=max_peak_db;
//if(box.x1<50)
// printf("min_peak_db: %f\n",min_peak_db);
min_peak = scale(min_peak_db,min_db,max_db, box.y2, box.y1);
max_peak = scale(max_peak_db,min_db,max_db, box.y2, box.y1);
}
#else
void set_min_max(const Box &box){
min_peak = scale(get_min_peak(),min_db,max_db, box.y2, box.y1);
max_peak = scale(get_max_peak(),min_db,max_db, box.y2, box.y1);
}
#endif
const int get_y_pixel(const Box &box){
return scale(last_peak,min_db,max_db, box.y2, box.y1);
}
const MyLine get_line_line(const Box &box){
const int peak = get_y_pixel(box);
return MyLine(box.x1,peak,box.x2,peak);
}
const MyLine get_line_to_next_box(const Box &box1, Peaks *peaks2, const Box &box2){
Peaks *peaks1 = this;
int x1=box1.x2;
int y1=peaks1->get_y_pixel(box1);
int x2=box2.x1;
int y2=peaks2->get_y_pixel(box2);
if(y1 < box1.y1){
x1=scale(box1.y1, y1,y2, x1,x2);
y1=box1.y1 - 10; // FIX. why is - 10 needed?
}
if(y2 < box2.y1){
x2=scale(box2.y1, y1,y2, x1,x2);
y2=box2.y1 - 10;
}
if(y1 > box1.y2){
x1=scale(box1.y2, y1,y2, x1,x2);
y1=box1.y2 + 10;
}
if(y2 > box2.y2){
x2=scale(box2.y2, y1,y2, x1,x2);
y2=box2.y2 + 10;
}
if(x1<box1.x2)
x1=10000;
if(x1>box2.x1)
x1=10000;
if(x2<box1.x2)
x1=10000;
if(x2>box2.x1)
x1=10000;
return MyLine(x1,y1,x2,y2);
}
// For lines in boxes (those lines are always horizontal)
const MyRect get_line_rect(const Box &box){
const int peak = get_y_pixel(box);
return MyRect(box.x1,peak-2,box.x2,peak+3);
}
void update_line_rect(MyWidget *widget, const Box &box){
widget->update(get_line_rect(box));
}
// For the lines between boxes.
void update_line_region(MyWidget *widget, const Box &box1, Peaks *peaks2, const Box &box2){
const MyLine line = get_line_to_next_box(box1,peaks2,box2);
int x1=line.x1;
int y1=line.y1;
int x2=line.x2;
int y2=line.y2;
if(x1==10000)
return; // Line is not painted.
int h_square = y2 - y1;
h_square *= h_square;
int w_square = x2 - x1;
w_square *= w_square;
int h = ceil(4.0/2.0 * sqrt((double)h_square/(double)w_square + 1.0)); // "4.0" is the pen width.
int y1_1 = y1-h;
int y1_2 = y1+h;
int y2_1 = y2-h;
int y2_2 = y2+h;
const int dx = 8;
if(y1_1 < y2_1)
for(int x=x1;x<x2;x+=dx){
int y1=scale(x,x1,x2,y1_1,y2_1);
int y2=scale(x+dx,x1,x2,y1_2,y2_2)+1;
widget->update(x,y1,x+dx,y2);
}
else
for(int x=x1;x<x2;x+=dx){
int y1=scale(x+dx,x1,x2,y1_1,y2_1);
int y2=scale(x,x1,x2,y1_2,y2_2)+1;
widget->update(x,y1,x+dx,y2);
}
}
const MyRect get_area_rect(const Box &box){
return MyRect(box.x1,max_peak,
box.x2, min_peak);
}
void paint_peak_area(MyPainter *p, const Box &box){
MyColor col(40,90,140);
col = col.lighter(80);
col.setAlpha(120);
p->fillRect(get_area_rect(box), col);
}
void paint_peak_line(MyPainter *p, const Box &box, const MyColor &col){
p->drawLine(get_line_line(box),col);
}
void paint_peak_line_between_boxes(MyPainter *p, const Box &box1, Peaks *peaks2, const Box &box2, const MyColor &col){
const MyLine line = get_line_to_next_box(box1, peaks2, box2);
if(line.x1==10000)
return;
p->drawLine(line,col);
}
// Only works if rect1.x1==rect2.x1 && rect2.x2==rect2.x2
void update_xored_rects(MyWidget *widget, const MyRect &rect1, const MyRect &rect2){
if(rect1.y1==rect2.y1 && rect1.y2==rect2.y2)
return;
MyRect r1=rect1;
MyRect r2=rect2;
int x1=rect1.x1;
int x2=rect1.x2;
if(r1.y1 > r2.y1){
MyRect temp=r1;
r1=r2;
r2=temp;
}
#if 0 // for testing
widget->update(x1,r1.y1,x2,r1.y2);
widget->update(x1,r2.y1,x2,r2.y2);
return;
#endif
if(r1.y1==r2.y1 && r2.y2<r1.y2){
widget->update(x1, r2.y2, x2, r1.y2);
return;
}
if(r1.y1==r2.y1){
widget->update(x1, r1.y2, x2, r2.y2);
return;
}
if(r1.y2==r2.y2){
widget->update(x1, r1.y1, x2, r2.y1);
return;
}
if(r2.y2 < r1.y2){
widget->update(x1, r1.y1, x2, r2.y1);
widget->update(x1, r2.y2, x2, r1.y2);
return;
}
if(r2.y1 < r1.y2){
widget->update(x1, r1.y1, x2, r2.y1);
widget->update(x1, r1.y2, x2, r2.y2);
}
// i.e. they are non-overlapping
widget->update(x1, r1.y1, x2, r1.y2);
widget->update(x1, r2.y1, x2, r2.y2);
}
void add_peaks_and_update_area(MyWidget *widget, const Box &box, float peak){
MyRect rect_before = get_area_rect(box);
if(peak<-1000)
peak=-1000;
if(peak>1000)
peak=1000;
last_peak = peak;
//printf("adding peak %.2f. ",peak);
peaks[peak_pos] = peak;
peak_pos++;
if(peak_pos==num_peaks)
peak_pos=0;
#if GLIDING_PEAK_AREA
update_peaks(box,peak);
#else
set_min_max(box);
#endif
MyRect rect_after = get_area_rect(box);
update_xored_rects(widget, rect_before, rect_after);
}
};
Peaks peaks_in;
Peaks peaks_out;
Peaks peaks_vol;
float in_peak_value;
float out_peak_value;
float vol_peak_value;
bool background_image_must_be_updated;
MyImage *background_image;
void timer(){ // virtual method from MyTimer
if(isVisible())
update_peaks();
}
~Comp(){
delete background_image;
}
Comp(struct Patch *patch, void *parent)
: MyWidget(parent)
, _patch(patch)
, in_val1(0.0)
, in_val2(def_threshold)
, out_val1(def_ratio)
, out_val2(def_threshold)
, vol_val1(def_makeupgain)
, vol_val2(def_threshold)
, curr_slider(0)
, background_image_must_be_updated(true)
, background_image(NULL)
{
createBackgroundImage();
startTimer(k_timer_interval); // MyTimer
set_threshold(def_threshold);
set_compressor_parameters();
update_peaks();
}
void createBackgroundImage(){
delete background_image;
background_image = new MyImage(width(),height());
updateBackgroundImage();
}
void resized(){
createBackgroundImage();
}
int get_box_width(){
return (width()-1) / 4;
}
int get_slider_height(){
return 20;
}
int get_box_height(){
//return height()-((get_slider_height()+1)*2);
return height()-1;
}
int get_width_between_boxes(){
return get_box_width() / 2;
}
Box get_slider1_parms(){
return Box(
0,0,
get_box_width(),
get_box_height(),
in_val1,in_val2);
}
Box get_slider2_parms(){
return Box(
get_box_width() + get_width_between_boxes(), 0,
get_box_width() + get_width_between_boxes() + get_box_width(),
get_box_height(),
out_val1,out_val2);
}
Box get_slider3_parms(){
return Box(
get_box_width() + get_width_between_boxes() + get_box_width() + get_width_between_boxes(), 0,
get_box_width() + get_width_between_boxes() + get_box_width() + get_width_between_boxes() + get_box_width(),
get_box_height(),
vol_val1, vol_val2);
}
#if 0
Slider get_attack_slider_parms(){
return Box(
0,get_box_height() + 1,
width(), get_box_height() + get_slider_height()
attack_val,0.0f);
}
Slider get_release_slider_parms(){
return Box(
0,get_box_height() + 1,
width(), get_box_height() + get_slider_height()
release_val,0.0f);
}
#endif
double get_threshold(){
return scale(in_val2,
in_val1,1,
max_db,min_db);
}
void set_threshold_gui(){
set_threshold(scale(get_compressor_parameter(_patch, COMP_EFF_THRESHOLD),max_db,min_db,in_val1,1));
}
double get_ratio(){
double len_in = in_val2-in_val1;
double len_out = out_val2-out_val1;
if(len_in==0.0 && len_out==0.0)
return 1.0;
if(len_out==0.0)
return 60;
double ratio = fabs(len_in-len_out)<0.00001
? 1.0
: len_in / len_out;
if(ratio>60)
ratio=60;
return ratio;
}
void set_ratio_gui(){
double ratio = get_compressor_parameter(_patch, COMP_EFF_RATIO);
if(ratio<1.0){
set_ratio(scale(ratio,1,0,0,-1));
}else
set_ratio(out_val2 - out_val2/ratio);
}
double get_makeup_gain(){
return scale(vol_val1,0,1,max_db,min_db) - scale(out_val1,0,1,max_db,min_db);
}
// set_ratio must be called first.
void set_makeup_gain_gui(){
float vol = get_compressor_parameter(_patch, COMP_EFF_OUTPUT_VOLUME);
float addvol = scale(out_val1,0,1,max_db,min_db) + vol;
set_makeupgain(scale(addvol,max_db,min_db,0,1));
}
// After loading.
void set_gui_parameters(){
set_threshold_gui();
set_ratio_gui();
set_makeup_gain_gui();
}
void set_compressor_parameters(){
//double input_volume = 1.0;
double threshold = get_threshold();
double ratio = get_ratio();
double output_volume = get_makeup_gain();
set_compressor_parameter(_patch, COMP_EFF_RATIO,ratio);
set_compressor_parameter(_patch, COMP_EFF_THRESHOLD, threshold);
//set_compressor_parameter(2,attack);
//set_compressor_parameter(3,release);
//set_compressor_parameter(4,input_volume);
set_compressor_parameter(_patch, COMP_EFF_OUTPUT_VOLUME,output_volume);
#if 0
printf("%f / %f\n%f / %f\n%f / %f\ninput_volume:\t\t %.2f\n"
"threshold:\t\t %f.2f\n"
"ratio:\t\t\t %f.2f\n"
"output_volume:\t\t %.2f\n\n",in_val2,out_val2,scale(vol_val1,0,1,max_db,min_db),scale(out_val1,0,1,max_db,min_db),vol_val1,out_val1,
input_volume,
threshold,
ratio,
output_volume);
#endif
}
enum{
THRESHOLD_SLIDER = 1,
RATIO_SLIDER,
MAKEUPGAIN_SLIDER
};
double p_startpos;
int y_startpos;
void set_makeupgain(float val){
//double old = vol_val2;
vol_val1=val;
#if 0 // allow this.
if(vol_val1<0.0)
vol_val1=0.0;
#endif
if(vol_val1>0.9999)
vol_val1=0.9999;
vol_val2 = vol_val1 + (out_val2-out_val1);// scale(out_val2, out_val1, 1.0, vol_val1, 1.0);
//printf("setting makeupgain to %f. old/new vol_val2: %f / %f\n",val,vol_val2,old);
}
void set_ratio(float val){
double vol_diff = vol_val1 - out_val1;
out_val1=val;
// if(out_val1<0.0)
// out_val1=0.0;
if(out_val1<0){
double threshold=get_compressor_parameter(_patch, COMP_EFF_THRESHOLD); //get_threshold();
double threshold_scaled=scale(threshold,max_db,min_db,0,1);
double ratio = scale(val,0,-1,1,0); //1.0 + val; // val is the out slider value, which goes from 0 to -1 when we are in expanding mode.
if(ratio<0.001)
ratio=0.001;
in_val1 = ((ratio-1)*threshold_scaled) / (ratio*threshold_scaled - ratio - threshold_scaled); // From: i1 = i2*(1-ratio), i2=scale(threshold_scaled,0,1,i1,1), i1=in_va1, i2=in_val2
out_val1=0.0;
if(in_val1>0.999)
in_val1=0.999;
in_val2=scale(threshold,max_db,min_db,in_val1,1);
out_val2=in_val2;
}else{
in_val1=0.0;
}
if(out_val1>in_val2-0.0001)
out_val1=in_val2-0.0001;
set_makeupgain(vol_diff + out_val1);
}
void set_threshold(float val){
//double bef=in_val2;
double old_inval1_scaled = scale(in_val1,0,in_val2,0,1);
double old_ratio_factor = out_val1 / (in_val2-in_val1);
in_val2 = val;
if(in_val2<=0.0)
in_val2=0.00001;
if(in_val2>0.9999)
in_val2=0.9999;
out_val2 = in_val2;
if(in_val1<=0.0){
set_ratio(old_ratio_factor*in_val2);
}else{
in_val1 = scale(old_inval1_scaled,0,1,0,in_val2);
set_makeupgain(vol_val1);
//printf("b %.2f - %.2f. Bef: %.2f, now: %.2f\n",in_val1,old_inval1_scaled,bef,in_val2);
}
}
void handle_mouse_event (int x, int y){
//printf("Got mouse press event %d / %d\n",(int)event->x(),(int)event->y());
double new_val = p_startpos + scale(y-y_startpos,0,height(),0,1);
//printf("p_startpos: %f\n",p_startpos);
switch(curr_slider){
case THRESHOLD_SLIDER:
set_threshold(new_val);
break;
case RATIO_SLIDER:
set_ratio(new_val);
break;
case MAKEUPGAIN_SLIDER:
set_makeupgain(new_val);
break;
}
set_compressor_parameters();
background_image_must_be_updated=true;
update();
}
bool mousePress(int x, int y){
Box in_box = get_slider1_parms();
Box out_box = get_slider2_parms();
Box vol_box = get_slider3_parms();
if(in_box.inside(x,y)){
#ifdef COMPILING_RADIUM
SoundPlugin *plugin = (SoundPlugin*)_patch->patchdata;
Undo_AudioEffect_CurrPos(_patch, plugin->type->num_effects+EFFNUM_COMP_THRESHOLD);
#endif
curr_slider = THRESHOLD_SLIDER;
p_startpos = in_box.p2;
}else if(out_box.inside(x,y)){
#ifdef COMPILING_RADIUM
SoundPlugin *plugin = (SoundPlugin*)_patch->patchdata;
Undo_AudioEffect_CurrPos(_patch, plugin->type->num_effects+EFFNUM_COMP_RATIO);
#endif
curr_slider = RATIO_SLIDER;
if(in_box.p1>0){
p_startpos = scale(in_box.p1,0,in_box.p2,0,-height());
}else{
p_startpos = out_box.p1;
}
}else if(vol_box.inside(x,y)){
#ifdef COMPILING_RADIUM
SoundPlugin *plugin = (SoundPlugin*)_patch->patchdata;
Undo_AudioEffect_CurrPos(_patch, plugin->type->num_effects+EFFNUM_COMP_OUTPUT_VOLUME);
#endif
curr_slider = MAKEUPGAIN_SLIDER;
p_startpos = vol_box.p1;
} else {
return false;
}
p_startpos = scale(p_startpos,0,height(),0,1);
y_startpos = y;
return true;
}
bool mouseMove (int x, int y)
{
handle_mouse_event(x,y);
return true;
}
bool mouseRelease (int x, int y)
{
handle_mouse_event(x,y);
curr_slider = 0;
return true;
}
void paint_box(MyPainter *p, const Box &b, const MyColor &col){
p->drawRect(b.x1, b.y1,
b.x2, b.y2,
col);
p->drawLine(b.x1,b.p1,
b.x2,b.p1,
col);
p->drawLine(b.x1,b.p2,
b.x2,b.p2,
col);
}
#if 0
void paint_slider(MyPainter *p, const Slider &s, const MyColor &col){
p->drawRect(s.x1, s.y1,
s.x2, s.y2);
p->drawLine(s.x1,s.p1,
s.x2,s.p1);
}
#endif
void paint_gradient_rectangle(MyPainter *p, const int x1, const int y1, const int x2, const int y2, const MyColor &c1, const MyColor &c2){
#if 0
for(int y=y1;y<y2;y++){
p->setPen(mix_colors(c1,c2,scale(y, y1, y2, 1.0f,0.0f)));
p->drawLine(x1,y,x2,y);
}
#else
p->setGradient(0,y1,0,y2,c1,c2);
p->fillRect(x1, y1, x2 ,y2, c1);
p->unsetGradient();
#endif
}
void paint_gradient_trapezoid(MyPainter *p, const int x1, const int y1_1, const int y1_2, const int x2, const int y2_1, const int y2_2, const int min_y, const int max_y, const MyColor &c1, const MyColor &c2){
#if 0
if(y1_2-y1_1 >= y2_2-y2_1)
for(int y1=y1_1;y1<y1_2;y1++){
int y2=scale(y1,y1_1,y1_2,y2_1,y2_2);
p->setPen(mix_colors(c1,c2,scale(y1, y1_1,y1_2, 1.0f,0.0f)));
p->drawLine(x1,y1,x2,y2);
}
else
for(int y2=y2_1;y2<y2_2;y2++){
int y1=scale(y2,y2_1,y2_2,y1_1,y1_2);
p->setPen(mix_colors(c1,c2,scale(y2, y2_1,y2_2, 1.0f,0.0f)));
p->drawLine(x1,y1,x2,y2);
}
#else
for(int x=x1;x<x2;x++){
int y1=scale(x,x1,x2,y1_1,y2_1);
int y2=scale(x,x1,x2,y1_2,y2_2);
p->setGradient(x,y1,x,y2,c1,c2);
p->drawLine(x,R_BOUNDARIES(min_y,y1,max_y),x,R_BOUNDARIES(min_y,y2,max_y),c1);
}
p->unsetGradient();
#endif
}
void paint_gradient(MyPainter *p, const Box &in, const Box &out, const Box &vol){
#if 0 // for testing if clipping is accurate
QColor col1(qrand()%255,qrand()%255,qrand()%255);
QColor col2(qrand()%255,qrand()%255,qrand()%255);
#else
MyColor col1(30,170,33);
MyColor col2(159,58,33);
#endif
col2=col2.lighter(80);
col1 = col1.lighter(80);
col2 = col2.lighter(60);
//col1.setAlpha(120);
//col2.setAlpha(180);
if(!isEnabled()){
//col1.setAlpha(40);
//col2.setAlpha(40);
col1 = mix_colors(col1, MyColor(), 0.4);
col2 = mix_colors(col2, MyColor(), 0.4);
}else{
col1 = mix_colors(col1, MyColor(), 0.8);
col2 = mix_colors(col2, MyColor(), 0.8);
}
// in slider
paint_gradient_rectangle(p, in.x1,in.p1, in.x2,in.y2, col1, col2);
float in_val = scale(in_val2,in_val1,1.0f,0.0f,1.0f);
MyColor col = mix_colors(col1,col2,1.0f-in_val);
// out slider top
paint_gradient_rectangle(p, out.x1,out.p1, out.x2,out.p2, col1,col);
// out slider bot.
paint_gradient_rectangle(p, out.x1,out.p2, out.x2,out.y2, col, col2);
// area between in slider and out slider, below threshold
paint_gradient_rectangle(p, in.x2,in.p2, out.x1,out.y2, col, col2);
// area between in slider and out slider, above threshold
paint_gradient_trapezoid(p, in.x2,in.p1,in.p2, out.x1,out.p1,out.p2, out.y1, out.y2, col1, col);
// volume slider
{
int p1 = vol.p1;
int p2 = vol.p2;//scale(out.p2, out.p1, out.y2, vol.p1, vol.y2);
int p3 = p2 + (out.y2-out.p2);
// top
paint_gradient_rectangle(p, vol.x1,p1, vol.x2,p2, col1, col);
// middle.
paint_gradient_rectangle(p, vol.x1,p2, vol.x2,p3, col, col2);
//QColor col3=col2.light(scale(p3,vol.y2,vol.y1,100,40));
MyColor col3=mix_colors(col2,MyColor(1,1,1),R_BOUNDARIES(0.0f, scale(p3,vol.y2,vol.y1,1.0f,0.0f), 1.0f));
// bot.
paint_gradient_rectangle(p, vol.x1,p3, vol.x2,vol.y2, col2, col3);
// area between out slider and vol slider, above threshold
paint_gradient_trapezoid(p,
out.x2, out.p1, out.p2,
vol.x1, vol.p1, vol.p2,
vol.y1, vol.y2,
col1,col);
// area between out slider and vol slider, below threshold, colors in the col-col2 range.
paint_gradient_trapezoid(p,
out.x2, out.p2,out.y2,
vol.x1, p2,p3,
vol.y1, vol.y2,
col,col2);
// area between out slider and vol slider, further below threshold, colors in the col2-col3 range.
if(p3 < vol.y2)
paint_gradient_trapezoid(p,
out.x2, out.y2, out.y2 + (vol.y2-p2),
vol.x1, p3, vol.y2,
vol.y1, vol.y2,
col2,col3);
}
}
// Called regularly
void update_peaks(){
Box in_box = get_slider1_parms();
Box out_box = get_slider2_parms();
Box vol_box = get_slider3_parms();
in_peak_value = get_graph_value(_patch, 0);
out_peak_value = in_peak_value + get_graph_value(_patch, 1);
vol_peak_value = out_peak_value + get_makeup_gain();
//printf("in_peak_value: %f. out: %f, vol: %f\n",in_peak_value,out_peak_value,vol_peak_value);
// update old line boxes
peaks_in.update_line_rect(this,in_box);
peaks_out.update_line_rect(this,out_box);
peaks_vol.update_line_rect(this,vol_box);
// update old lines between boxes
peaks_in.update_line_region(this,in_box,&peaks_out,out_box);
peaks_out.update_line_region(this,out_box,&peaks_vol,vol_box);
// Add new peaks, and update areas in boxes (both old and new)
peaks_in.add_peaks_and_update_area(this,in_box, scale(scale(in_peak_value,max_db,min_db,in_val1,1),0,1,max_db,min_db));
peaks_out.add_peaks_and_update_area(this,out_box,scale(scale(out_peak_value,max_db,min_db,in_val1,1),0,1,max_db,min_db));
peaks_vol.add_peaks_and_update_area(this,vol_box,scale(scale(vol_peak_value,max_db,min_db,in_val1,1),0,1,max_db,min_db));
// update new lines between boxes
peaks_in.update_line_region(this,in_box,&peaks_out,out_box);
peaks_out.update_line_region(this,out_box,&peaks_vol,vol_box);
// update new line boxes
peaks_in.update_line_rect(this,in_box);
peaks_out.update_line_rect(this,out_box);
peaks_vol.update_line_rect(this,vol_box);
}
void paintPeaks(MyPainter *p, const Box &in_box, const Box &out_box, const Box &vol_box){
// peak lines
{
MyColor col(0,90,180);
col = col.lighter(60);
col.setAlpha(130);
p->setThickness(4);
// paint lines
{
// in
peaks_in.paint_peak_line(p, in_box, col);
// between in and out
peaks_in.paint_peak_line_between_boxes(p, in_box, &peaks_out, out_box, col);
// out
peaks_out.paint_peak_line(p, out_box, col);
// between out and vol
peaks_out.paint_peak_line_between_boxes(p, out_box, &peaks_vol, vol_box, col);
// vol
peaks_vol.paint_peak_line(p, vol_box, col);
}
p->setThickness(1);
}
// peak areas
{
peaks_in.paint_peak_area(p,in_box);
peaks_out.paint_peak_area(p,out_box);
peaks_vol.paint_peak_area(p,vol_box);
}
#if 0 // for checking that get_line_region returns correct value.
p->setPen(QPen());
p->drawPolygon(peaks_in.get_line_region(in_box,&peaks_out,out_box));
p->drawPolygon(peaks_out.get_line_region(out_box,&peaks_vol,vol_box));
#endif
}
void paintSliderText(MyPainter *p, const Box &box, std::string text, double value, std::string unit){
MyColor c(1);
c.setAlpha(160);
if(!isEnabled())
c.setAlpha(80);
char temp[32];
sprintf(temp,"%.1f",value);
std::string value_text = temp + unit;
std::string one_line = text + " " + value_text;
MyRect rect(box.x1+2,box.y1+2,box.x2-4,box.y2-4);
if(p->getTextWidth(text) < rect.width() && p->getTextWidth(value_text) < rect.width()){
// horizontal
p->drawText(rect, p->getTextWidth(one_line)<rect.width()
? one_line
: text+"\n"+value_text,
c);
}else{
p->drawVerticalText(box.x1,box.y1,one_line,c);
}
//p->drawText(box.x1,box.y2-20,QString::number(value));
}
void paint_box_fill(MyPainter *p, const Box &box){
MyColor col(150,190,10);
col.setAlpha(10);
if(box.p1>box.y1)
p->fillRect(box.x1, box.y1,
box.x2, box.p1,
col);
if(box.p2<box.y2)
p->fillRect(box.x1, box.y2,
box.x2, box.p2,
col);
}
void paintBackgroundImage(MyPainter *p){
p->fillRect(0,0,width(),height(),MyColor());
//printf("Paint\n");
Box in_box = get_slider1_parms();
Box out_box = get_slider2_parms();
Box vol_box = get_slider3_parms();
paint_gradient(p,in_box,out_box,vol_box);
MyColor black(1,1,1);
black.setAlpha(80);
paint_box(p, in_box, black);
paint_box(p, out_box, black);
paint_box(p, vol_box, black);
// bottom line between sliders
if(1){
MyColor c = mix_colors(MyColor(1,1,1), MyColor(), 0.4); // Alpha color will not look correct here since the color it's drawn over differs.
p->drawLine(in_box.x2,in_box.y2,vol_box.x1,out_box.y2,c);
p->drawLine(out_box.x2,out_box.y2,vol_box.x1,vol_box.y2,c);
}
// threshold line between sliders
if(1){
// between in and out
p->drawLine(in_box.x2,in_box.p2,
out_box.x1,out_box.p2,
black
);
// between out and vol
p->drawLine(out_box.x2,out_box.p2,
vol_box.x1,vol_box.p2,
black);
}
paint_box_fill(p,in_box);
paint_box_fill(p,out_box);
paint_box_fill(p,vol_box);
// //
// border line between sliders
if(1){
black.setAlpha(80);
if(in_box.p1!=out_box.p1)
p->setThickness(1);
// between in and out
p->drawLine(in_box.x2,in_box.p1,
out_box.x1,out_box.p1,
black);
if(out_box.p1!=vol_box.p1)
p->setThickness(1);
else
p->setThickness(1);
// between out and vol
p->drawLine(out_box.x2,out_box.p1,
vol_box.x1,vol_box.p1,
black);
p->setThickness(1);
}
paintSliderText(p,in_box,"Threshold: ",get_threshold(), "dB");
paintSliderText(p,out_box,"Ratio: ",get_ratio(),":1");
paintSliderText(p,vol_box,"Makeup Gain: ",get_makeup_gain(),"dB");
background_image_must_be_updated = false;
}
void updateBackgroundImage(){
MyPainter p(background_image);
//background_image->fill(colors[11]);
paintBackgroundImage(&p);
}
void repaint(MyPainter *p){
if(background_image_must_be_updated==true)
updateBackgroundImage();
Box in_box = get_slider1_parms();
Box out_box = get_slider2_parms();
Box vol_box = get_slider3_parms();
//paintBackgroundImage(&p);
p->drawImage(0,0,background_image);
paintPeaks(p, in_box, out_box, vol_box);
}
};
}
#endif // INCLUDE_MYLINE_AND_MYRECT