tomwalters@397
|
1 // Copyright 2006, Willem van Engen
|
tomwalters@397
|
2 //
|
tomwalters@397
|
3 // AIM-C: A C++ implementation of the Auditory Image Model
|
tomwalters@397
|
4 // http://www.acousticscale.org/AIMC
|
tomwalters@397
|
5 //
|
tomwalters@397
|
6 // Licensed under the Apache License, Version 2.0 (the "License");
|
tomwalters@397
|
7 // you may not use this file except in compliance with the License.
|
tomwalters@397
|
8 // You may obtain a copy of the License at
|
tomwalters@397
|
9 //
|
tomwalters@397
|
10 // http://www.apache.org/licenses/LICENSE-2.0
|
tomwalters@397
|
11 //
|
tomwalters@397
|
12 // Unless required by applicable law or agreed to in writing, software
|
tomwalters@397
|
13 // distributed under the License is distributed on an "AS IS" BASIS,
|
tomwalters@397
|
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
tomwalters@397
|
15 // See the License for the specific language governing permissions and
|
tomwalters@397
|
16 // limitations under the License.
|
tomwalters@397
|
17
|
tomwalters@397
|
18 #include "Support/Common.h"
|
tomwalters@397
|
19
|
tomwalters@397
|
20 #include "Output/GraphicsView.h"
|
tomwalters@397
|
21 #include "Output/GraphicsOutputDevice.h"
|
tomwalters@397
|
22
|
tomwalters@397
|
23 GraphicsView::GraphicsView(Parameters *parameters) : Module(parameters) {
|
tomwalters@398
|
24 module_description_ = "";
|
tomwalters@397
|
25 module_identifier_ = "graphics";
|
tomwalters@397
|
26 module_type_ = "output";
|
tomwalters@397
|
27 module_version_ = "$Id: $";
|
tomwalters@397
|
28
|
tomwalters@398
|
29 m_pDev = NULL;
|
tomwalters@398
|
30 m_bPlotLabels = false;
|
tomwalters@398
|
31 m_pAxisX = new GraphAxisSpec();
|
tomwalters@398
|
32 AIM_ASSERT(m_pAxisX);
|
tomwalters@398
|
33 m_pAxisY = new GraphAxisSpec();
|
tomwalters@398
|
34 AIM_ASSERT(m_pAxisY);
|
tomwalters@398
|
35 m_pAxisFreq = new GraphAxisSpec();
|
tomwalters@398
|
36 AIM_ASSERT(m_pAxisFreq);
|
tomwalters@397
|
37
|
tomwalters@397
|
38 m_pAxisY->Initialize(m_pParam,
|
tomwalters@397
|
39 _S("graph.y"),
|
tomwalters@397
|
40 -1,
|
tomwalters@397
|
41 1,
|
tomwalters@397
|
42 Scale::SCALE_LINEAR);
|
tomwalters@398
|
43 m_fMarginLeft = m_pParam->GetFloat(_S("graph.margin.left"));
|
tomwalters@398
|
44 m_fMarginRight = m_pParam->GetFloat(_S("graph.margin.right"));
|
tomwalters@398
|
45 m_fMarginTop = m_pParam->GetFloat(_S("graph.margin.top"));
|
tomwalters@398
|
46 m_fMarginBottom = m_pParam->GetFloat(_S("graph.margin.bottom"));
|
tomwalters@398
|
47 m_bPlotLabels = m_pParam->GetBool(_S("graph.plotlabels"));
|
tomwalters@397
|
48
|
tomwalters@398
|
49 const char *sGraphType = m_pParam->GetString(_S("graph.type"));
|
tomwalters@398
|
50 if (strcmp(sGraphType, _S("line"))==0)
|
tomwalters@398
|
51 m_iGraphType = GraphTypeLine;
|
tomwalters@398
|
52 else if (strcmp(sGraphType, _S("colormap"))==0)
|
tomwalters@398
|
53 m_iGraphType = GraphTypeColormap;
|
tomwalters@398
|
54 else if (strcmp(sGraphType, _S("none"))==0)
|
tomwalters@398
|
55 m_iGraphType = GraphTypeNone;
|
tomwalters@398
|
56 else {
|
tomwalters@398
|
57 ret = false;
|
tomwalters@398
|
58 AIM_ERROR(_T("Unrecognized graph type: '%s'"), sGraphType);
|
tomwalters@398
|
59 }
|
tomwalters@397
|
60
|
tomwalters@398
|
61 if (strcmp(m_pParam->GetString(_S("graph.mindistance")),"auto") == 0)
|
tomwalters@398
|
62 // -1 means detect later, based on type and Fire() argument
|
tomwalters@398
|
63 m_fMinPlotDistance = -1;
|
tomwalters@398
|
64 else
|
tomwalters@398
|
65 m_fMinPlotDistance = m_pParam->GetFloat(_S("graph.mindistance"));
|
tomwalters@397
|
66
|
tomwalters@397
|
67 }
|
tomwalters@397
|
68
|
tomwalters@397
|
69 GraphicsView::~GraphicsView() {
|
tomwalters@398
|
70 DELETE_IF_NONNULL(m_pAxisX);
|
tomwalters@398
|
71 DELETE_IF_NONNULL(m_pAxisY);
|
tomwalters@398
|
72 DELETE_IF_NONNULL(m_pAxisFreq);
|
tomwalters@397
|
73 }
|
tomwalters@397
|
74
|
tomwalters@397
|
75 bool
|
tomwalters@397
|
76
|
tomwalters@397
|
77 bool GraphicsView::InitializeInternal(const SignalBank &bank) {
|
tomwalters@398
|
78 if (!m_pDev) {
|
tomwalters@398
|
79 LOG_ERROR("Output device not connected");
|
tomwalters@398
|
80 return false;
|
tomwalters@398
|
81 }
|
tomwalters@397
|
82
|
tomwalters@397
|
83 float y_min = bank.centre_frequency(0);
|
tomwalters@397
|
84 float y_max = bank.centre_frequency(bank.channel_count() - 1);
|
tomwalters@398
|
85 if (!m_pAxisFreq->Initialize(m_pParam,
|
tomwalters@397
|
86 "graph.freq",
|
tomwalters@398
|
87 y_min,
|
tomwalters@398
|
88 y_max,
|
tomwalters@398
|
89 Scale::SCALE_ERB)) {
|
tomwalters@397
|
90 LOG_ERROR("");
|
tomwalters@397
|
91 return false;
|
tomwalters@397
|
92 }
|
tomwalters@397
|
93
|
tomwalters@397
|
94 float x_min = 0.0;
|
tomwalters@397
|
95 float x_max = 1000.0 * bank.buffer_length() / bank.sample_rate();
|
tomwalters@398
|
96 if (!m_pAxisX->Initialize(m_pParam,
|
tomwalters@397
|
97 "graph.x",
|
tomwalters@398
|
98 x_min,
|
tomwalters@397
|
99 x_max,
|
tomwalters@398
|
100 Scale::SCALE_LINEAR)) {
|
tomwalters@397
|
101 LOG_ERROR("");
|
tomwalters@397
|
102 return false;
|
tomwalters@397
|
103 }
|
tomwalters@397
|
104
|
tomwalters@398
|
105 /* Inform graphics output of maximum number of vertices between
|
tomwalters@398
|
106 * gBegin*() and gEnd(), for any type of plot. Colormap needs most.
|
tomwalters@398
|
107 */
|
tomwalters@398
|
108 if (!m_pDev->Initialize(MAX(10, bank.buffer_length() * 2 + 2))) {
|
tomwalters@397
|
109 LOG_ERROR("");
|
tomwalters@397
|
110 return false;
|
tomwalters@397
|
111 }
|
tomwalters@397
|
112 }
|
tomwalters@397
|
113
|
tomwalters@397
|
114 void GraphicsView::Process(const SignalBank &bank) {
|
tomwalters@398
|
115 float height = 1.0 / bank.channel_count();
|
tomwalters@398
|
116 float heightMinMargin = height * (1.0f - m_fMarginBottom - m_fMarginTop);
|
tomwalters@398
|
117 float xScaling = 1.0f;
|
tomwalters@397
|
118
|
tomwalters@398
|
119 m_pDev->gGrab();
|
tomwalters@398
|
120 PlotAxes(bank);
|
tomwalters@398
|
121 m_pDev->gColor3f(1.0f, 1.0f, 0.8f);
|
tomwalters@398
|
122 for (int i = 0; i < bank.channel_count(); i++) {
|
tomwalters@398
|
123 float yOffs = bank.centre_frequency(i);
|
tomwalters@398
|
124 yOffs = m_pAxisFreq->m_pScale->FromLinearScaled(yOffs) + 0.5f;
|
tomwalters@398
|
125 /* Don't plot below zero and stop when above 1, since yOffs is
|
tomwalters@397
|
126 * monotonically increasing. Because of rounding errors, we need
|
tomwalters@397
|
127 * to check for yOffs < -1e-6 instead of yOffs < 0. */
|
tomwalters@398
|
128 if (yOffs < -1e-6)
|
tomwalters@397
|
129 continue;
|
tomwalters@398
|
130 if (yOffs > 1)
|
tomwalters@397
|
131 break;
|
tomwalters@397
|
132
|
tomwalters@397
|
133 // Scale to single channel graphing.
|
tomwalters@398
|
134 yOffs = yOffs * (1.0f - height) + height / 2.0;
|
tomwalters@398
|
135 yOffs = yOffs * (1.0f - m_fMarginTop - m_fMarginBottom) + m_fMarginBottom;
|
tomwalters@398
|
136 PlotData(bank[i], yOffs, heightMinMargin, xScaling);
|
tomwalters@398
|
137 }
|
tomwalters@398
|
138 m_pDev->gRelease();
|
tomwalters@397
|
139 }
|
tomwalters@397
|
140
|
tomwalters@397
|
141 void GraphicsView::SetAxisScale(Scale::ScaleType iHori,
|
tomwalters@397
|
142 Scale::ScaleType iVert,
|
tomwalters@397
|
143 Scale::ScaleType iFreq) {
|
tomwalters@398
|
144 AIM_ASSERT(m_pAxisX);
|
tomwalters@398
|
145 AIM_ASSERT(m_pAxisY);
|
tomwalters@398
|
146 AIM_ASSERT(m_pAxisFreq);
|
tomwalters@398
|
147 m_pAxisX->SetDisplayScale(iHori);
|
tomwalters@398
|
148 m_pAxisY->SetDisplayScale(iVert);
|
tomwalters@398
|
149 m_pAxisFreq->SetDisplayScale(iFreq);
|
tomwalters@397
|
150 }
|
tomwalters@397
|
151
|
tomwalters@397
|
152 void GraphicsView::BeginDataStrip() {
|
tomwalters@398
|
153 m_iPrevValEqual=0;
|
tomwalters@398
|
154 m_bFirstPoint=true;
|
tomwalters@398
|
155 switch(m_iGraphType) {
|
tomwalters@398
|
156 case GraphTypeLine:
|
tomwalters@398
|
157 m_pDev->gBeginLineStrip();
|
tomwalters@398
|
158 break;
|
tomwalters@398
|
159 case GraphTypeColormap:
|
tomwalters@398
|
160 m_pDev->gBeginQuadStrip();
|
tomwalters@398
|
161 break;
|
tomwalters@398
|
162 case GraphTypeNone:
|
tomwalters@398
|
163 // Nothing: just for testing computation overhead of graphing
|
tomwalters@398
|
164 break;
|
tomwalters@398
|
165 }
|
tomwalters@397
|
166 }
|
tomwalters@397
|
167
|
tomwalters@397
|
168 void GraphicsView::PlotDataPoint(float x,
|
tomwalters@397
|
169 float y,
|
tomwalters@397
|
170 float val,
|
tomwalters@397
|
171 float height,
|
tomwalters@397
|
172 bool isLast) {
|
tomwalters@398
|
173 AIM_ASSERT(m_pDev);
|
tomwalters@397
|
174
|
tomwalters@398
|
175 /* Reduce the number of points plotted by eliminating duplicate values:
|
tomwalters@398
|
176 *
|
tomwalters@398
|
177 * oooo o--o
|
tomwalters@398
|
178 * o / o
|
tomwalters@398
|
179 * oooo ooo => o--o o--o
|
tomwalters@398
|
180 * oooo ooo o--o o-o
|
tomwalters@398
|
181 *
|
tomwalters@398
|
182 * with 'o' points that are plotted, and '-' by the graphics output
|
tomwalters@398
|
183 * device interpolated points. We could be smarter and include
|
tomwalters@398
|
184 * first-order interpolation, but we leave that as an exercise for
|
tomwalters@398
|
185 * the reader. Please send your patches :)
|
tomwalters@398
|
186 *
|
tomwalters@398
|
187 * So, we don't draw points that are too close to the previous value.
|
tomwalters@398
|
188 * But if the value changes (or it's the last point), we must draw the
|
tomwalters@398
|
189 * previous point too.
|
tomwalters@398
|
190 */
|
tomwalters@398
|
191 if (!m_bFirstPoint
|
tomwalters@397
|
192 && !isLast
|
tomwalters@397
|
193 && fabs(m_fPrevVal-val) < m_fMinPlotDistance) {
|
tomwalters@398
|
194 m_iPrevValEqual++;
|
tomwalters@398
|
195 // Don't set m_fPrevVal to avoid not catching slow changes
|
tomwalters@398
|
196 m_fPrevX = x;
|
tomwalters@398
|
197 m_fPrevY = y;
|
tomwalters@398
|
198 m_fPrevHeight = height;
|
tomwalters@398
|
199 return;
|
tomwalters@398
|
200 } else {
|
tomwalters@398
|
201 if (m_iPrevValEqual > 0) {
|
tomwalters@398
|
202 // Draw previous point
|
tomwalters@398
|
203 PlotDataPointDirect(m_fPrevX, m_fPrevY, m_fPrevVal, m_fPrevHeight);
|
tomwalters@398
|
204 }
|
tomwalters@398
|
205 m_iPrevValEqual = 0;
|
tomwalters@398
|
206 m_fPrevVal = val;
|
tomwalters@398
|
207 m_bFirstPoint = false;
|
tomwalters@398
|
208 }
|
tomwalters@398
|
209 PlotDataPointDirect(x, y, val, height);
|
tomwalters@397
|
210 }
|
tomwalters@397
|
211
|
tomwalters@397
|
212 void GraphicsView::PlotDataPointDirect(float x,
|
tomwalters@397
|
213 float y,
|
tomwalters@397
|
214 float val,
|
tomwalters@397
|
215 float height) {
|
tomwalters@398
|
216 // Draw it in the right way
|
tomwalters@398
|
217 switch(m_iGraphType) {
|
tomwalters@398
|
218 case GraphTypeLine:
|
tomwalters@398
|
219 m_pDev->gVertex2f(x, y + val * height);
|
tomwalters@398
|
220 break;
|
tomwalters@398
|
221 case GraphTypeColormap:
|
tomwalters@398
|
222 //! \todo make it a real colormap instead of grayscale
|
tomwalters@398
|
223 m_pDev->gColor3f(val + 0.5, val + 0.5, val + 0.5);
|
tomwalters@398
|
224 m_pDev->gVertex2f(x, y - height / 2);
|
tomwalters@398
|
225 m_pDev->gVertex2f(x, y + height / 2);
|
tomwalters@398
|
226 break;
|
tomwalters@398
|
227 case GraphTypeNone:
|
tomwalters@398
|
228 // Nothing: just for testing computation overhead of graphing
|
tomwalters@398
|
229 break;
|
tomwalters@398
|
230 default:
|
tomwalters@398
|
231 // Shouldn't happen
|
tomwalters@398
|
232 AIM_ASSERT(0);
|
tomwalters@398
|
233 }
|
tomwalters@397
|
234 }
|