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
|
tom@399
|
18 #include <algorithm>
|
tom@399
|
19
|
tomwalters@397
|
20 #include "Support/Common.h"
|
tomwalters@397
|
21
|
tom@399
|
22 #include "Modules/Output/Graphics/GraphicsView.h"
|
tom@399
|
23 #include "Modules/Output/Graphics/Devices/GraphicsOutputDevice.h"
|
tomwalters@411
|
24 #include "Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.h"
|
tom@399
|
25
|
tom@399
|
26 namespace aimc {
|
tomwalters@397
|
27
|
tomwalters@397
|
28 GraphicsView::GraphicsView(Parameters *parameters) : Module(parameters) {
|
tomwalters@402
|
29 module_description_ = "Graphics output.";
|
tomwalters@397
|
30 module_identifier_ = "graphics";
|
tomwalters@397
|
31 module_type_ = "output";
|
tomwalters@397
|
32 module_version_ = "$Id: $";
|
tomwalters@443
|
33
|
tomwalters@443
|
34 if (parameters_->DefaultBool("output.images", false)) {
|
tomwalters@443
|
35 m_pDev = new GraphicsOutputDeviceCairo(parameters_);
|
tomwalters@443
|
36 } else {
|
tomwalters@443
|
37 m_pDev = new GraphicsOutputDeviceMovie(parameters_);
|
tomwalters@443
|
38 }
|
tomwalters@398
|
39 m_bPlotLabels = false;
|
tomwalters@398
|
40 m_pAxisX = new GraphAxisSpec();
|
tomwalters@398
|
41 AIM_ASSERT(m_pAxisX);
|
tomwalters@398
|
42 m_pAxisY = new GraphAxisSpec();
|
tomwalters@398
|
43 AIM_ASSERT(m_pAxisY);
|
tomwalters@398
|
44 m_pAxisFreq = new GraphAxisSpec();
|
tomwalters@398
|
45 AIM_ASSERT(m_pAxisFreq);
|
tom@399
|
46 initialized_ = true;
|
tomwalters@397
|
47
|
tomwalters@410
|
48 if (!m_pAxisY->Initialize(parameters_,
|
tom@399
|
49 _S("graph.y"),
|
tom@399
|
50 -1,
|
tom@399
|
51 1,
|
tom@399
|
52 Scale::SCALE_LINEAR)) {
|
tom@399
|
53 LOG_ERROR("Axis initialization failed");
|
tom@399
|
54 initialized_ = false;
|
tom@399
|
55 }
|
tomwalters@411
|
56 m_fMarginLeft = parameters_->DefaultFloat(_S("graph.margin.left"), 0.05);
|
tomwalters@411
|
57 m_fMarginRight = parameters_->DefaultFloat(_S("graph.margin.right"), 0.005);
|
tomwalters@411
|
58 m_fMarginTop = parameters_->DefaultFloat(_S("graph.margin.top"), 0.005);
|
tomwalters@411
|
59 m_fMarginBottom = parameters_->DefaultFloat(_S("graph.margin.bottom"), 0.05);
|
tomwalters@411
|
60 m_bPlotLabels = parameters_->DefaultBool(_S("graph.plotlabels"), true);
|
tomwalters@569
|
61 plotting_strobes_ = parameters_->DefaultBool(_S("graph.plot_strobes"), false);
|
tomwalters@397
|
62
|
tomwalters@411
|
63 const char *sGraphType = parameters_->DefaultString(_S("graph.type"), "line");
|
tomwalters@398
|
64 if (strcmp(sGraphType, _S("line"))==0)
|
tomwalters@398
|
65 m_iGraphType = GraphTypeLine;
|
tomwalters@398
|
66 else if (strcmp(sGraphType, _S("colormap"))==0)
|
tomwalters@398
|
67 m_iGraphType = GraphTypeColormap;
|
tomwalters@398
|
68 else if (strcmp(sGraphType, _S("none"))==0)
|
tomwalters@398
|
69 m_iGraphType = GraphTypeNone;
|
tomwalters@398
|
70 else {
|
tom@399
|
71 LOG_ERROR(_T("Unrecognized graph type: '%s'"), sGraphType);
|
tom@399
|
72 initialized_ = false;
|
tomwalters@398
|
73 }
|
tomwalters@397
|
74
|
tomwalters@411
|
75 if (strcmp(parameters_->DefaultString(_S("graph.mindistance"), "auto"),"auto") == 0)
|
tomwalters@398
|
76 // -1 means detect later, based on type and Fire() argument
|
tomwalters@398
|
77 m_fMinPlotDistance = -1;
|
tomwalters@398
|
78 else
|
tomwalters@410
|
79 m_fMinPlotDistance = parameters_->GetFloat(_S("graph.mindistance"));
|
tomwalters@397
|
80 }
|
tomwalters@397
|
81
|
tomwalters@397
|
82 GraphicsView::~GraphicsView() {
|
tomwalters@398
|
83 DELETE_IF_NONNULL(m_pAxisX);
|
tomwalters@398
|
84 DELETE_IF_NONNULL(m_pAxisY);
|
tomwalters@398
|
85 DELETE_IF_NONNULL(m_pAxisFreq);
|
tomwalters@397
|
86 }
|
tomwalters@397
|
87
|
tom@399
|
88 void GraphicsView::ResetInternal() {
|
tomwalters@411
|
89 if (m_pDev != NULL) {
|
tomwalters@418
|
90 m_pDev->Reset(global_parameters_);
|
tomwalters@411
|
91 }
|
tom@399
|
92 }
|
tomwalters@397
|
93
|
tomwalters@397
|
94 bool GraphicsView::InitializeInternal(const SignalBank &bank) {
|
tomwalters@398
|
95 if (!m_pDev) {
|
tomwalters@398
|
96 LOG_ERROR("Output device not connected");
|
tomwalters@398
|
97 return false;
|
tomwalters@398
|
98 }
|
tomwalters@397
|
99
|
tomwalters@397
|
100 float y_min = bank.centre_frequency(0);
|
tomwalters@397
|
101 float y_max = bank.centre_frequency(bank.channel_count() - 1);
|
tomwalters@410
|
102 if (!m_pAxisFreq->Initialize(parameters_,
|
tomwalters@397
|
103 "graph.freq",
|
tomwalters@398
|
104 y_min,
|
tomwalters@398
|
105 y_max,
|
tomwalters@398
|
106 Scale::SCALE_ERB)) {
|
tomwalters@411
|
107 LOG_ERROR("Frequency axis init failed.");
|
tomwalters@397
|
108 return false;
|
tomwalters@397
|
109 }
|
tomwalters@397
|
110
|
tomwalters@397
|
111 float x_min = 0.0;
|
tomwalters@397
|
112 float x_max = 1000.0 * bank.buffer_length() / bank.sample_rate();
|
tomwalters@410
|
113 if (!m_pAxisX->Initialize(parameters_,
|
tomwalters@397
|
114 "graph.x",
|
tomwalters@398
|
115 x_min,
|
tomwalters@397
|
116 x_max,
|
tomwalters@398
|
117 Scale::SCALE_LINEAR)) {
|
tomwalters@411
|
118 LOG_ERROR("Time axis init failed.");
|
tomwalters@397
|
119 return false;
|
tomwalters@397
|
120 }
|
tomwalters@397
|
121
|
tomwalters@398
|
122 /* Inform graphics output of maximum number of vertices between
|
tomwalters@398
|
123 * gBegin*() and gEnd(), for any type of plot. Colormap needs most.
|
tomwalters@398
|
124 */
|
tomwalters@411
|
125 LOG_INFO("Initializing graphics output device.");
|
tomwalters@411
|
126
|
tomwalters@411
|
127 if (!m_pDev->Initialize(global_parameters_)) {
|
tomwalters@411
|
128 LOG_ERROR("Graphics output device init failed.");
|
tomwalters@397
|
129 return false;
|
tomwalters@397
|
130 }
|
tomwalters@411
|
131 m_pDev->Start();
|
tomwalters@411
|
132 previous_start_time_ = 0;
|
tom@399
|
133 return true;
|
tomwalters@397
|
134 }
|
tomwalters@397
|
135
|
tomwalters@397
|
136 void GraphicsView::Process(const SignalBank &bank) {
|
tomwalters@398
|
137 float height = 1.0 / bank.channel_count();
|
tomwalters@398
|
138 float heightMinMargin = height * (1.0f - m_fMarginBottom - m_fMarginTop);
|
tomwalters@398
|
139 float xScaling = 1.0f;
|
tomwalters@569
|
140 float diameter = height / 5.0;
|
tomwalters@397
|
141
|
tomwalters@398
|
142 m_pDev->gGrab();
|
tomwalters@398
|
143 PlotAxes(bank);
|
tomwalters@398
|
144 m_pDev->gColor3f(1.0f, 1.0f, 0.8f);
|
tomwalters@398
|
145 for (int i = 0; i < bank.channel_count(); i++) {
|
tomwalters@398
|
146 float yOffs = bank.centre_frequency(i);
|
tomwalters@398
|
147 yOffs = m_pAxisFreq->m_pScale->FromLinearScaled(yOffs) + 0.5f;
|
tomwalters@398
|
148 /* Don't plot below zero and stop when above 1, since yOffs is
|
tomwalters@397
|
149 * monotonically increasing. Because of rounding errors, we need
|
tomwalters@397
|
150 * to check for yOffs < -1e-6 instead of yOffs < 0. */
|
tomwalters@398
|
151 if (yOffs < -1e-6)
|
tomwalters@397
|
152 continue;
|
tomwalters@398
|
153 if (yOffs > 1)
|
tomwalters@397
|
154 break;
|
tomwalters@397
|
155
|
tomwalters@397
|
156 // Scale to single channel graphing.
|
tomwalters@398
|
157 yOffs = yOffs * (1.0f - height) + height / 2.0;
|
tomwalters@398
|
158 yOffs = yOffs * (1.0f - m_fMarginTop - m_fMarginBottom) + m_fMarginBottom;
|
tom@399
|
159 PlotData(bank[i], bank.sample_rate(), yOffs, heightMinMargin, xScaling);
|
tomwalters@569
|
160 if (plotting_strobes_) {
|
tomwalters@569
|
161 PlotStrobes(bank[i], bank.get_strobes(i), bank.sample_rate(),
|
tomwalters@569
|
162 yOffs, heightMinMargin ,xScaling, diameter);
|
tomwalters@569
|
163 }
|
tomwalters@398
|
164 }
|
tomwalters@398
|
165 m_pDev->gRelease();
|
tomwalters@411
|
166
|
tomwalters@411
|
167 frame_rate_ = bank.sample_rate()
|
tomwalters@411
|
168 / (bank.start_time() - previous_start_time_);
|
tomwalters@411
|
169 previous_start_time_ = bank.start_time();
|
tomwalters@411
|
170 global_parameters_->SetFloat("frame_rate", frame_rate_);
|
tomwalters@397
|
171 }
|
tomwalters@397
|
172
|
tomwalters@397
|
173 void GraphicsView::SetAxisScale(Scale::ScaleType iHori,
|
tomwalters@397
|
174 Scale::ScaleType iVert,
|
tomwalters@397
|
175 Scale::ScaleType iFreq) {
|
tomwalters@398
|
176 AIM_ASSERT(m_pAxisX);
|
tomwalters@398
|
177 AIM_ASSERT(m_pAxisY);
|
tomwalters@398
|
178 AIM_ASSERT(m_pAxisFreq);
|
tomwalters@398
|
179 m_pAxisX->SetDisplayScale(iHori);
|
tomwalters@398
|
180 m_pAxisY->SetDisplayScale(iVert);
|
tomwalters@398
|
181 m_pAxisFreq->SetDisplayScale(iFreq);
|
tomwalters@397
|
182 }
|
tomwalters@397
|
183
|
tomwalters@397
|
184 void GraphicsView::BeginDataStrip() {
|
tomwalters@398
|
185 m_iPrevValEqual=0;
|
tomwalters@398
|
186 m_bFirstPoint=true;
|
tomwalters@398
|
187 switch(m_iGraphType) {
|
tomwalters@398
|
188 case GraphTypeLine:
|
tomwalters@398
|
189 m_pDev->gBeginLineStrip();
|
tomwalters@398
|
190 break;
|
tomwalters@398
|
191 case GraphTypeColormap:
|
tomwalters@398
|
192 m_pDev->gBeginQuadStrip();
|
tomwalters@398
|
193 break;
|
tomwalters@398
|
194 case GraphTypeNone:
|
tomwalters@398
|
195 // Nothing: just for testing computation overhead of graphing
|
tomwalters@398
|
196 break;
|
tomwalters@398
|
197 }
|
tomwalters@397
|
198 }
|
tomwalters@397
|
199
|
tomwalters@397
|
200 void GraphicsView::PlotDataPoint(float x,
|
tomwalters@397
|
201 float y,
|
tomwalters@397
|
202 float val,
|
tomwalters@397
|
203 float height,
|
tomwalters@397
|
204 bool isLast) {
|
tomwalters@398
|
205 AIM_ASSERT(m_pDev);
|
tomwalters@397
|
206
|
tomwalters@398
|
207 /* Reduce the number of points plotted by eliminating duplicate values:
|
tomwalters@398
|
208 *
|
tomwalters@398
|
209 * oooo o--o
|
tomwalters@398
|
210 * o / o
|
tomwalters@398
|
211 * oooo ooo => o--o o--o
|
tomwalters@398
|
212 * oooo ooo o--o o-o
|
tomwalters@398
|
213 *
|
tomwalters@398
|
214 * with 'o' points that are plotted, and '-' by the graphics output
|
tomwalters@398
|
215 * device interpolated points. We could be smarter and include
|
tomwalters@398
|
216 * first-order interpolation, but we leave that as an exercise for
|
tomwalters@398
|
217 * the reader. Please send your patches :)
|
tomwalters@398
|
218 *
|
tomwalters@398
|
219 * So, we don't draw points that are too close to the previous value.
|
tomwalters@398
|
220 * But if the value changes (or it's the last point), we must draw the
|
tomwalters@398
|
221 * previous point too.
|
tomwalters@398
|
222 */
|
tomwalters@398
|
223 if (!m_bFirstPoint
|
tomwalters@397
|
224 && !isLast
|
tom@399
|
225 && abs(m_fPrevVal-val) < m_fMinPlotDistance) {
|
tomwalters@398
|
226 m_iPrevValEqual++;
|
tomwalters@398
|
227 // Don't set m_fPrevVal to avoid not catching slow changes
|
tomwalters@398
|
228 m_fPrevX = x;
|
tomwalters@398
|
229 m_fPrevY = y;
|
tomwalters@398
|
230 m_fPrevHeight = height;
|
tomwalters@398
|
231 return;
|
tomwalters@398
|
232 } else {
|
tomwalters@398
|
233 if (m_iPrevValEqual > 0) {
|
tomwalters@398
|
234 // Draw previous point
|
tomwalters@398
|
235 PlotDataPointDirect(m_fPrevX, m_fPrevY, m_fPrevVal, m_fPrevHeight);
|
tomwalters@398
|
236 }
|
tomwalters@398
|
237 m_iPrevValEqual = 0;
|
tomwalters@398
|
238 m_fPrevVal = val;
|
tomwalters@398
|
239 m_bFirstPoint = false;
|
tomwalters@398
|
240 }
|
tomwalters@398
|
241 PlotDataPointDirect(x, y, val, height);
|
tomwalters@397
|
242 }
|
tomwalters@397
|
243
|
tomwalters@569
|
244 void GraphicsView::PlotStrobe(float x, float y, float val,
|
tomwalters@569
|
245 float height,
|
tomwalters@569
|
246 float diameter) {
|
tomwalters@569
|
247 switch(m_iGraphType) {
|
tomwalters@569
|
248 case GraphTypeLine:
|
tomwalters@569
|
249 m_pDev->gBeginQuadStrip();
|
tomwalters@569
|
250 m_pDev->gVertex2f(x + diameter, y + val * height + diameter);
|
tomwalters@569
|
251 m_pDev->gVertex2f(x + diameter, y + val * height - diameter);
|
tomwalters@569
|
252 m_pDev->gVertex2f(x - diameter, y + val * height - diameter);
|
tomwalters@569
|
253 m_pDev->gVertex2f(x - diameter, y + val * height + diameter);
|
tomwalters@569
|
254 m_pDev->gEnd();
|
tomwalters@569
|
255 break;
|
tomwalters@569
|
256 case GraphTypeColormap:
|
tomwalters@569
|
257 m_pDev->gBeginQuadStrip();
|
tomwalters@569
|
258 m_pDev->gVertex2f(x + diameter, y + diameter);
|
tomwalters@569
|
259 m_pDev->gVertex2f(x + diameter, y - diameter);
|
tomwalters@569
|
260 m_pDev->gVertex2f(x - diameter, y - diameter);
|
tomwalters@569
|
261 m_pDev->gVertex2f(x - diameter, y + diameter);
|
tomwalters@569
|
262 m_pDev->gEnd();
|
tomwalters@569
|
263 break;
|
tomwalters@569
|
264 case GraphTypeNone:
|
tomwalters@569
|
265 // Nothing: just for testing computation overhead of graphing
|
tomwalters@569
|
266 break;
|
tomwalters@569
|
267 default:
|
tomwalters@569
|
268 // Shouldn't happen
|
tomwalters@569
|
269 AIM_ASSERT(0);
|
tomwalters@569
|
270 }
|
tomwalters@569
|
271 }
|
tomwalters@569
|
272
|
tomwalters@397
|
273 void GraphicsView::PlotDataPointDirect(float x,
|
tomwalters@397
|
274 float y,
|
tomwalters@397
|
275 float val,
|
tomwalters@397
|
276 float height) {
|
tomwalters@398
|
277 // Draw it in the right way
|
tomwalters@398
|
278 switch(m_iGraphType) {
|
tomwalters@398
|
279 case GraphTypeLine:
|
tomwalters@398
|
280 m_pDev->gVertex2f(x, y + val * height);
|
tomwalters@398
|
281 break;
|
tomwalters@398
|
282 case GraphTypeColormap:
|
tomwalters@398
|
283 //! \todo make it a real colormap instead of grayscale
|
tomwalters@398
|
284 m_pDev->gColor3f(val + 0.5, val + 0.5, val + 0.5);
|
tomwalters@398
|
285 m_pDev->gVertex2f(x, y - height / 2);
|
tomwalters@398
|
286 m_pDev->gVertex2f(x, y + height / 2);
|
tomwalters@398
|
287 break;
|
tomwalters@398
|
288 case GraphTypeNone:
|
tomwalters@398
|
289 // Nothing: just for testing computation overhead of graphing
|
tomwalters@398
|
290 break;
|
tomwalters@398
|
291 default:
|
tomwalters@398
|
292 // Shouldn't happen
|
tomwalters@398
|
293 AIM_ASSERT(0);
|
tomwalters@398
|
294 }
|
tomwalters@397
|
295 }
|
tom@399
|
296 } // namespace aimc
|