ColourScale.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006-2016 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "ColourScale.h"
17 
18 #include "base/AudioLevel.h"
19 #include "base/LogRange.h"
20 
21 #include <cmath>
22 #include <iostream>
23 
24 using namespace std;
25 
26 int ColourScale::m_maxPixel = 255;
27 
29  m_params(parameters),
30  m_mapper(m_params.colourMap, m_params.inverted, 1.f, double(m_maxPixel))
31 {
33  SVCERR << "ERROR: ColourScale::ColourScale: minValue = "
34  << m_params.minValue << ", maxValue = " << m_params.maxValue << endl;
35  throw std::logic_error("maxValue must be greater than minValue");
36  }
37 
40 
43  }
44 
46 
47  // When used in e.g. spectrogram, we have a range with a min
48  // value of zero. The LogRange converts that to a threshold
49  // value of -10, so for a range of e.g. (0,1) we end up with
50  // (-10,0) as the mapped range.
51  //
52  // But in other contexts we could end up with a mapped range
53  // much larger than that if we have a small non-zero minimum
54  // value (less than 1e-10), or a particularly large
55  // maximum. That's unlikely to give us good results, so let's
56  // insist that the mapped log range has no more than 10
57  // difference between min and max, to match the behaviour when
58  // min == 0 at the input.
59  //
60  double threshold = -10.0;
61  LogRange::mapRange(m_mappedMin, m_mappedMax, threshold);
62  if (m_mappedMin < m_mappedMax + threshold) {
63  m_mappedMin = m_mappedMax + threshold;
64  }
65 
67 
68  m_mappedMin = -1.0;
69  m_mappedMax = 1.0;
70 
72 
73  m_mappedMin = fabs(m_mappedMin);
74  m_mappedMax = fabs(m_mappedMax);
75  if (m_mappedMin >= m_mappedMax) {
76  std::swap(m_mappedMin, m_mappedMax);
77  }
78  }
79 
80  if (m_mappedMin >= m_mappedMax) {
81  SVCERR << "ERROR: ColourScale::ColourScale: minValue = " << m_params.minValue
82  << ", maxValue = " << m_params.maxValue
83  << ", threshold = " << m_params.threshold
84  << ", scale = " << int(m_params.scaleType)
85  << " resulting in mapped minValue = " << m_mappedMin
86  << ", mapped maxValue = " << m_mappedMax << endl;
87  throw std::logic_error("maxValue must be greater than minValue [after mapping]");
88  }
89 }
90 
92 {
93 }
94 
97 {
98  return m_params.scaleType;
99 }
100 
101 int
102 ColourScale::getPixel(double value) const
103 {
104  double maxPixF = m_maxPixel;
105 
107  double half = (maxPixF - 1.f) / 2.f;
108  int pixel = 1 + int((value * half) / M_PI + half);
109 // SVCERR << "phase = " << value << " pixel = " << pixel << endl;
110  return pixel;
111  }
112 
113  value *= m_params.gain;
114 
115  if (value < m_params.threshold) return 0;
116 
117  double mapped = value;
118 
120  mapped = LogRange::map(value);
122  if (mapped < -1.f) mapped = -1.f;
123  if (mapped > 1.f) mapped = 1.f;
125  if (mapped < 0.f) mapped = -mapped;
126  }
127 
128  mapped *= m_params.multiple;
129 
130  if (mapped < m_mappedMin) {
131  mapped = m_mappedMin;
132  }
133  if (mapped > m_mappedMax) {
134  mapped = m_mappedMax;
135  }
136 
137  double proportion = (mapped - m_mappedMin) / (m_mappedMax - m_mappedMin);
138 
139  int pixel = 0;
140 
142  pixel = AudioLevel::multiplier_to_preview(proportion, m_maxPixel-1) + 1;
143  } else {
144  pixel = int(proportion * maxPixF) + 1;
145  }
146 
147  if (pixel < 0) {
148  pixel = 0;
149  }
150  if (pixel > m_maxPixel) {
151  pixel = m_maxPixel;
152  }
153  return pixel;
154 }
155 
156 QColor
157 ColourScale::getColourForPixel(int pixel, int rotation) const
158 {
159  if (pixel < 0) {
160  pixel = 0;
161  }
162  if (pixel > m_maxPixel) {
163  pixel = m_maxPixel;
164  }
165  if (pixel == 0) {
167  return Qt::white;
168  } else {
169  return Qt::black;
170  }
171  } else {
172  int target = int(pixel) + rotation;
173  while (target < 1) target += m_maxPixel;
174  while (target > m_maxPixel) target -= m_maxPixel;
175  return m_mapper.map(double(target));
176  }
177 }
double threshold
Threshold below which every value is mapped to background pixel 0.
Definition: ColourScale.h:59
static int m_maxPixel
Definition: ColourScale.h:120
ColourMapper m_mapper
Definition: ColourScale.h:117
QColor getColourForPixel(int pixel, int rotation) const
Return the colour for the given pixel number (which must be in the range 0-255).
double maxValue
Maximum value in source range.
Definition: ColourScale.h:52
double m_mappedMax
Definition: ColourScale.h:119
double gain
Gain to apply before thresholding, mapping, and clamping.
Definition: ColourScale.h:62
ColourScale(Parameters parameters)
Create a ColourScale with the given parameters.
Definition: ColourScale.cpp:28
double minValue
Minimum value in source range.
Definition: ColourScale.h:49
bool hasLightBackground() const
Return true if the colour map is intended to be placed over a light background, false otherwise...
ColourScaleType getScale() const
Return the general type of scale this is.
Definition: ColourScale.cpp:96
QColor map(double value) const
Map the given value to a colour.
double m_mappedMin
Definition: ColourScale.h:118
ColourScaleType scaleType
Distribution for the scale.
Definition: ColourScale.h:46
ColourScaleType
Definition: ColourScale.h:21
int getPixel(double value) const
Return a pixel number (in the range 0-255 inclusive) corresponding to the given value.
double multiple
Multiple to apply after thresholding and mapping.
Definition: ColourScale.h:69
Parameters m_params
Definition: ColourScale.h:116