Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
480 lines (363 sloc) 11.6 KB
/* Copyright 2012 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. */
#include <math.h>
#include <QWidget>
#include <QGraphicsItem>
#include <QTimer>
#include <QSlider>
#include <QTimerEvent>
#include "EditorWidget.h"
#include "Qt_SliderPainter_proc.h"
#include "Qt_SliderPainterPainter.h"
const int k_timer_interval = 50;
static int scale_int(int x, int x1, int x2, int y1, int y2){
return (int)scale((float)x,(float)x1,(float)x2,(float)y1,(float)y2);
}
static float gain2db(float val){
if(val<=0.0f)
return -100.0f;
return 20*log10(val);
}
static float db2linear(float db){
if(db<-70)
return 0.0f;
else if(db>40)
return 1.0f;
else
return scale(db,-70.0f,40.0f,0.0f,1.0f);
}
#if 0
// Function iec_scale picked from meterbridge by Steve Harris.
// db is a value between 0 and 1.
static float iec_scale(float db) {
db = 20.0f * log10f(db);
float def = 0.0f; /* Meter deflection %age */
if (db < -70.0f) {
def = 0.0f;
} else if (db < -60.0f) {
def = (db + 70.0f) * 0.25f;
} else if (db < -50.0f) {
def = (db + 60.0f) * 0.5f + 5.0f;
} else if (db < -40.0f) {
def = (db + 50.0f) * 0.75f + 7.5;
} else if (db < -30.0f) {
def = (db + 40.0f) * 1.5f + 15.0f;
} else if (db < -20.0f) {
def = (db + 30.0f) * 2.0f + 30.0f;
} else if (db < 0.0f) {
def = (db + 20.0f) * 2.5f + 50.0f;
} else {
def = 100.0f;
}
return def * 2.0f / 200.0f;
}
#endif
#define MAX_CHANNELS 8
//namespace{ // stupid c++ with all this useless syntax necessary to make a program appear to work. C++ is only great if you like a challenge and don't want things done.
namespace{
struct AutomationOrPeakData{
float *value;
int requested_pos;
int last_drawn_pos;
int *color;
int ch;
int num_ch;
bool is_automation;
};
}
static int DATA_get_y1(AutomationOrPeakData *data, int height){
return scale_int(data->ch,0,data->num_ch,1,height-1);
}
static int DATA_get_y2(AutomationOrPeakData *data, int height){
return scale_int(data->ch+1,0,data->num_ch,1,height-1);
}
struct SliderPainter{
struct Timer : public QTimer{
SliderPainter *_painter;
void timerEvent(QTimerEvent * e){
//if(_painter->isVisible())
// printf("oh yeah. value: %f. last_drawn: %d, visible? %d\n",*data->value, data->last_drawn_pos,_painter->isVisible());
if(_painter->isVisible()==false)
return;
for(unsigned int i=0;i<_painter->_data.size();i++){
AutomationOrPeakData *data = _painter->_data.at(i);
float gain;
if(data->is_automation)
gain = *data->value;
else{
float db = gain2db(*data->value);
if(db>4.0f)
_painter->_peak_color = 14;
else if(db>0.0f)
_painter->_peak_color = 6;
else
_painter->_peak_color = 13;
gain = db2linear(db);
}
data->requested_pos = scale(gain,0.0f,1.0f,
0.0f,(float)_painter->width())
- 1;
if(data->last_drawn_pos != data->requested_pos){
//printf("Painting. Last drawn: %d. requested: %d\n",data->last_drawn_pos,data->requested_pos);
int y1 = DATA_get_y1(data,_painter->height());
int y2 = DATA_get_y2(data,_painter->height());
int height = y2-y1;
//printf("y1: %d, y2: %d, height: %d. req: %d, last: %d\n",y1,y2,height,data->requested_pos,data->last_drawn_pos);
_painter->update(data->requested_pos,
y1,4,height+1);
_painter->update(data->last_drawn_pos,
y1,4,height+1);
//printf("%d - %d\n",data->requested_pos,data->last_drawn_pos);
}
}
//e->accept();
}
}; // struct Timer
std::vector<AutomationOrPeakData*>_data;
QSlider *_qslider;
QGraphicsItem *_graphics_item; // Either widget or _graphics_item must be set.
int _x1;
int _y1;
int _x2;
int _y2;
float *_peak_values;
bool _local_peak_values;
float _automation_value;
int _automation_color;
int _peak_color;
bool _alternative_color;
int _value;
int _num_automations;
Timer _timer;
QString _display_string;
int _num_channels;
int value(){
if(_qslider!=NULL)
return _qslider->value();
else
return _value;
}
int minimum(){
if(_qslider!=NULL)
return _qslider->minimum();
else
return 0;
}
int maximum(){
if(_qslider!=NULL)
return _qslider->maximum();
else
return 10000;
}
int orientation(){
if(_qslider!=NULL)
return _qslider->orientation();
else
return Qt::Horizontal;
}
bool isEnabled(){
if(_qslider!=NULL)
return _qslider->isEnabled();
else
return _num_channels>0;
}
bool isVisible(){
if(_qslider!=NULL)
return _qslider->isVisible() && !_qslider->isHidden();
else
return _graphics_item->isVisible();
}
void update(int x, int y, int width, int height){
//fprintf(stderr,"x: %d. _qslider: %p\n",x,_qslider);
if(x<0)
x=0;
if(y<0)
y=0;
//fprintf(stderr,"y: %d. _qslider: %p\n",y,_qslider);
if(_qslider!=NULL){
return _qslider->update(x,y,width,height);
}else
return _graphics_item->update(_x1+x, _y1+y, width, height);
}
int width(){
if(_qslider!=NULL)
return _qslider->width();
else
return _x2-_x1;
}
int height(){
if(_qslider!=NULL)
return _qslider->height();
else
return _y2-_y1;
}
void init(){
_value = 0;
_num_automations = 0;
_num_channels = 0;
_peak_values = NULL;
_local_peak_values=false;
_automation_color = 13;
_peak_color = 13;
_alternative_color = false;
}
SliderPainter(QSlider *widget)
: _qslider(widget)
, _graphics_item(NULL)
, _automation_value(0.0f)
{
init();
}
SliderPainter(QGraphicsItem *graphics_item)
: _qslider(NULL)
, _graphics_item(graphics_item)
{
init();
}
~SliderPainter(){
if(_local_peak_values==true)
free(_peak_values);
for(unsigned int i=0;i<_data.size();i++) // Don't like iterators. Rather free memory manually than using them.
delete _data.at(i);
}
AutomationOrPeakData *create_automation_data(){
AutomationOrPeakData *data = new AutomationOrPeakData;
data->value = &_automation_value;
data->requested_pos = 0;
data->last_drawn_pos = 0;
data->color = &_automation_color;
data->ch = 0;
data->num_ch = 1;
data->is_automation = true;
_data.push_back(data);
if(_num_automations==0){
_timer._painter = this;
_timer.setInterval(k_timer_interval);
_timer.start();
}
_num_automations++;
return data;
}
float *obtain_automation_value_pointer(){
create_automation_data();
return &_automation_value;
}
int *obtain_automation_color_pointer(){
return &_automation_color;
}
float *obtain_peak_value_pointers(int num_channels, float *peak_values){
bool is_replacing = _peak_values!=NULL;
if(peak_values==NULL) {
_peak_values = (float*)calloc(sizeof(float*),num_channels);
_local_peak_values = true;
} else {
_peak_values = peak_values;
}
if(is_replacing){
while(_num_automations>0)
release_automation_pointers(); // Warning! This works only since _is_replacing==true just for the bottom bar volume slider (which doesn't have automations).
_data.clear();
}
for(int ch=0;ch<num_channels;ch++){
AutomationOrPeakData *data = create_automation_data();
data->ch = ch;
data->num_ch = num_channels;
data->is_automation = false;
data->value = &_peak_values[ch];
data->color = &_peak_color;
}
return _peak_values;
}
void set_peak_value_pointers(int num_channels, float *pointers){
obtain_peak_value_pointers(num_channels, pointers);
}
void release_automation_pointers(){
_num_automations--;
if(_num_automations==0)
_timer.stop();
}
void paint(QPainter *p){
#ifdef COMPILING_RADIUM
QColor *colors = static_cast<EditorWidget*>(root->song->tracker_windows->os_visual.widget)->colors;
#else
QColor *colors = g_colors;
#endif
SLIDERPAINTERPAINTER_paint(p,0,0,width(),height(),
isEnabled(),
scale(value(),minimum(),maximum(),0.0f,1.0f),
_display_string,
_alternative_color);
for(unsigned int i=0;i<_data.size();i++){
AutomationOrPeakData *data = _data.at(i);
int y1 = DATA_get_y1(data,height());
int y2 = DATA_get_y2(data,height());
int height = y2-y1;
p->fillRect(data->requested_pos+1 ,y1+1,
2, height-1,
colors[*data->color]);
p->setPen(QPen(colors[11].light(120),1));
p->drawRect(data->requested_pos, y1,
3, height);
data->last_drawn_pos = data->requested_pos;
}
}
};
//}
SliderPainter *SLIDERPAINTER_create(QSlider *qslider){
return new SliderPainter(qslider);
}
SliderPainter *SLIDERPAINTER_create(QGraphicsItem *graphics_item, int x1, int y1, int x2, int y2){
SliderPainter *painter = new SliderPainter(graphics_item);
painter->_x1=x1;
painter->_y1=y1;
painter->_x2=x2;
painter->_y2=y2;
return painter;
}
void SLIDERPAINTER_delete(SliderPainter *painter){
delete painter;
}
void SLIDERPAINTER_setValue(SliderPainter *painter, int value){
painter->_value = value;
}
void SLIDERPAINTER_set_num_channels(SliderPainter *painter, int num_channels){
painter->_num_channels = num_channels;
}
void SLIDERPAINTER_paint(SliderPainter *painter, QPainter *p){
painter->paint(p);
}
float *SLIDERPAINTER_obtain_peak_value_pointers(SliderPainter *painter, int num_channels){
return painter->obtain_peak_value_pointers(num_channels,NULL);
}
void SLIDERPAINTER_set_peak_value_pointers(SliderPainter *painter, int num_channels, float *pointers){
painter->set_peak_value_pointers(num_channels, pointers);
}
float *SLIDERPAINTER_obtain_automation_value_pointer(SliderPainter *painter){
return painter->obtain_automation_value_pointer();
}
int *SLIDERPAINTER_obtain_automation_color_pointer(SliderPainter *painter){
return painter->obtain_automation_color_pointer();
}
void SLIDERPAINTER_release_automation_pointers(SliderPainter *painter){
painter->release_automation_pointers();
}
// Used for chips where the slider controls input volume instead of output volume.
void SLIDERPAINTER_set_alternative_color(SliderPainter *painter){
painter->_alternative_color = true;
}
void SLIDERPAINTER_set_string(SliderPainter *painter,QString string){
painter->_display_string = string;
}
QString SLIDERPAINTER_get_string(SliderPainter *painter){
return painter->_display_string;
}