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@397
|
61
|
tomwalters@411
|
62 const char *sGraphType = parameters_->DefaultString(_S("graph.type"), "line");
|
tomwalters@398
|
63 if (strcmp(sGraphType, _S("line"))==0)
|
tomwalters@398
|
64 m_iGraphType = GraphTypeLine;
|
tomwalters@398
|
65 else if (strcmp(sGraphType, _S("colormap"))==0)
|
tomwalters@398
|
66 m_iGraphType = GraphTypeColormap;
|
tomwalters@398
|
67 else if (strcmp(sGraphType, _S("none"))==0)
|
tomwalters@398
|
68 m_iGraphType = GraphTypeNone;
|
tomwalters@398
|
69 else {
|
tom@399
|
70 LOG_ERROR(_T("Unrecognized graph type: '%s'"), sGraphType);
|
tom@399
|
71 initialized_ = false;
|
tomwalters@398
|
72 }
|
tomwalters@397
|
73
|
tomwalters@411
|
74 if (strcmp(parameters_->DefaultString(_S("graph.mindistance"), "auto"),"auto") == 0)
|
tomwalters@398
|
75 // -1 means detect later, based on type and Fire() argument
|
tomwalters@398
|
76 m_fMinPlotDistance = -1;
|
tomwalters@398
|
77 else
|
tomwalters@410
|
78 m_fMinPlotDistance = parameters_->GetFloat(_S("graph.mindistance"));
|
tomwalters@397
|
79 }
|
tomwalters@397
|
80
|
tomwalters@397
|
81 GraphicsView::~GraphicsView() {
|
tomwalters@398
|
82 DELETE_IF_NONNULL(m_pAxisX);
|
tomwalters@398
|
83 DELETE_IF_NONNULL(m_pAxisY);
|
tomwalters@398
|
84 DELETE_IF_NONNULL(m_pAxisFreq);
|
tomwalters@397
|
85 }
|
tomwalters@397
|
86
|
tom@399
|
87 void GraphicsView::ResetInternal() {
|
tomwalters@411
|
88 if (m_pDev != NULL) {
|
tomwalters@418
|
89 m_pDev->Reset(global_parameters_);
|
tomwalters@411
|
90 }
|
tom@399
|
91 }
|
tomwalters@397
|
92
|
tomwalters@397
|
93 bool GraphicsView::InitializeInternal(const SignalBank &bank) {
|
tomwalters@398
|
94 if (!m_pDev) {
|
tomwalters@398
|
95 LOG_ERROR("Output device not connected");
|
tomwalters@398
|
96 return false;
|
tomwalters@398
|
97 }
|
tomwalters@397
|
98
|
tomwalters@397
|
99 float y_min = bank.centre_frequency(0);
|
tomwalters@397
|
100 float y_max = bank.centre_frequency(bank.channel_count() - 1);
|
tomwalters@410
|
101 if (!m_pAxisFreq->Initialize(parameters_,
|
tomwalters@397
|
102 "graph.freq",
|
tomwalters@398
|
103 y_min,
|
tomwalters@398
|
104 y_max,
|
tomwalters@398
|
105 Scale::SCALE_ERB)) {
|
tomwalters@411
|
106 LOG_ERROR("Frequency axis init failed.");
|
tomwalters@397
|
107 return false;
|
tomwalters@397
|
108 }
|
tomwalters@397
|
109
|
tomwalters@397
|
110 float x_min = 0.0;
|
tomwalters@397
|
111 float x_max = 1000.0 * bank.buffer_length() / bank.sample_rate();
|
tomwalters@410
|
112 if (!m_pAxisX->Initialize(parameters_,
|
tomwalters@397
|
113 "graph.x",
|
tomwalters@398
|
114 x_min,
|
tomwalters@397
|
115 x_max,
|
tomwalters@398
|
116 Scale::SCALE_LINEAR)) {
|
tomwalters@411
|
117 LOG_ERROR("Time axis init failed.");
|
tomwalters@397
|
118 return false;
|
tomwalters@397
|
119 }
|
tomwalters@397
|
120
|
tomwalters@398
|
121 /* Inform graphics output of maximum number of vertices between
|
tomwalters@398
|
122 * gBegin*() and gEnd(), for any type of plot. Colormap needs most.
|
tomwalters@398
|
123 */
|
tomwalters@411
|
124 LOG_INFO("Initializing graphics output device.");
|
tomwalters@411
|
125
|
tomwalters@411
|
126 if (!m_pDev->Initialize(global_parameters_)) {
|
tomwalters@411
|
127 LOG_ERROR("Graphics output device init failed.");
|
tomwalters@397
|
128 return false;
|
tomwalters@397
|
129 }
|
tomwalters@411
|
130 m_pDev->Start();
|
tomwalters@411
|
131 previous_start_time_ = 0;
|
tom@399
|
132 return true;
|
tomwalters@397
|
133 }
|
tomwalters@397
|
134
|
tomwalters@397
|
135 void GraphicsView::Process(const SignalBank &bank) {
|
tomwalters@398
|
136 float height = 1.0 / bank.channel_count();
|
tomwalters@398
|
137 float heightMinMargin = height * (1.0f - m_fMarginBottom - m_fMarginTop);
|
tomwalters@398
|
138 float xScaling = 1.0f;
|
tomwalters@397
|
139
|
tomwalters@398
|
140 m_pDev->gGrab();
|
tomwalters@398
|
141 PlotAxes(bank);
|
tomwalters@398
|
142 m_pDev->gColor3f(1.0f, 1.0f, 0.8f);
|
tomwalters@398
|
143 for (int i = 0; i < bank.channel_count(); i++) {
|
tomwalters@398
|
144 float yOffs = bank.centre_frequency(i);
|
tomwalters@398
|
145 yOffs = m_pAxisFreq->m_pScale->FromLinearScaled(yOffs) + 0.5f;
|
tomwalters@398
|
146 /* Don't plot below zero and stop when above 1, since yOffs is
|
tomwalters@397
|
147 * monotonically increasing. Because of rounding errors, we need
|
tomwalters@397
|
148 * to check for yOffs < -1e-6 instead of yOffs < 0. */
|
tomwalters@398
|
149 if (yOffs < -1e-6)
|
tomwalters@397
|
150 continue;
|
tomwalters@398
|
151 if (yOffs > 1)
|
tomwalters@397
|
152 break;
|
tomwalters@397
|
153
|
tomwalters@397
|
154 // Scale to single channel graphing.
|
tomwalters@398
|
155 yOffs = yOffs * (1.0f - height) + height / 2.0;
|
tomwalters@398
|
156 yOffs = yOffs * (1.0f - m_fMarginTop - m_fMarginBottom) + m_fMarginBottom;
|
tom@399
|
157 PlotData(bank[i], bank.sample_rate(), yOffs, heightMinMargin, xScaling);
|
tomwalters@398
|
158 }
|
tomwalters@398
|
159 m_pDev->gRelease();
|
tomwalters@411
|
160
|
tomwalters@411
|
161 frame_rate_ = bank.sample_rate()
|
tomwalters@411
|
162 / (bank.start_time() - previous_start_time_);
|
tomwalters@411
|
163 previous_start_time_ = bank.start_time();
|
tomwalters@411
|
164 global_parameters_->SetFloat("frame_rate", frame_rate_);
|
tomwalters@397
|
165 }
|
tomwalters@397
|
166
|
tomwalters@397
|
167 void GraphicsView::SetAxisScale(Scale::ScaleType iHori,
|
tomwalters@397
|
168 Scale::ScaleType iVert,
|
tomwalters@397
|
169 Scale::ScaleType iFreq) {
|
tomwalters@398
|
170 AIM_ASSERT(m_pAxisX);
|
tomwalters@398
|
171 AIM_ASSERT(m_pAxisY);
|
tomwalters@398
|
172 AIM_ASSERT(m_pAxisFreq);
|
tomwalters@398
|
173 m_pAxisX->SetDisplayScale(iHori);
|
tomwalters@398
|
174 m_pAxisY->SetDisplayScale(iVert);
|
tomwalters@398
|
175 m_pAxisFreq->SetDisplayScale(iFreq);
|
tomwalters@397
|
176 }
|
tomwalters@397
|
177
|
tomwalters@397
|
178 void GraphicsView::BeginDataStrip() {
|
tomwalters@398
|
179 m_iPrevValEqual=0;
|
tomwalters@398
|
180 m_bFirstPoint=true;
|
tomwalters@398
|
181 switch(m_iGraphType) {
|
tomwalters@398
|
182 case GraphTypeLine:
|
tomwalters@398
|
183 m_pDev->gBeginLineStrip();
|
tomwalters@398
|
184 break;
|
tomwalters@398
|
185 case GraphTypeColormap:
|
tomwalters@398
|
186 m_pDev->gBeginQuadStrip();
|
tomwalters@398
|
187 break;
|
tomwalters@398
|
188 case GraphTypeNone:
|
tomwalters@398
|
189 // Nothing: just for testing computation overhead of graphing
|
tomwalters@398
|
190 break;
|
tomwalters@398
|
191 }
|
tomwalters@397
|
192 }
|
tomwalters@397
|
193
|
tomwalters@397
|
194 void GraphicsView::PlotDataPoint(float x,
|
tomwalters@397
|
195 float y,
|
tomwalters@397
|
196 float val,
|
tomwalters@397
|
197 float height,
|
tomwalters@397
|
198 bool isLast) {
|
tomwalters@398
|
199 AIM_ASSERT(m_pDev);
|
tomwalters@397
|
200
|
tomwalters@398
|
201 /* Reduce the number of points plotted by eliminating duplicate values:
|
tomwalters@398
|
202 *
|
tomwalters@398
|
203 * oooo o--o
|
tomwalters@398
|
204 * o / o
|
tomwalters@398
|
205 * oooo ooo => o--o o--o
|
tomwalters@398
|
206 * oooo ooo o--o o-o
|
tomwalters@398
|
207 *
|
tomwalters@398
|
208 * with 'o' points that are plotted, and '-' by the graphics output
|
tomwalters@398
|
209 * device interpolated points. We could be smarter and include
|
tomwalters@398
|
210 * first-order interpolation, but we leave that as an exercise for
|
tomwalters@398
|
211 * the reader. Please send your patches :)
|
tomwalters@398
|
212 *
|
tomwalters@398
|
213 * So, we don't draw points that are too close to the previous value.
|
tomwalters@398
|
214 * But if the value changes (or it's the last point), we must draw the
|
tomwalters@398
|
215 * previous point too.
|
tomwalters@398
|
216 */
|
tomwalters@398
|
217 if (!m_bFirstPoint
|
tomwalters@397
|
218 && !isLast
|
tom@399
|
219 && abs(m_fPrevVal-val) < m_fMinPlotDistance) {
|
tomwalters@398
|
220 m_iPrevValEqual++;
|
tomwalters@398
|
221 // Don't set m_fPrevVal to avoid not catching slow changes
|
tomwalters@398
|
222 m_fPrevX = x;
|
tomwalters@398
|
223 m_fPrevY = y;
|
tomwalters@398
|
224 m_fPrevHeight = height;
|
tomwalters@398
|
225 return;
|
tomwalters@398
|
226 } else {
|
tomwalters@398
|
227 if (m_iPrevValEqual > 0) {
|
tomwalters@398
|
228 // Draw previous point
|
tomwalters@398
|
229 PlotDataPointDirect(m_fPrevX, m_fPrevY, m_fPrevVal, m_fPrevHeight);
|
tomwalters@398
|
230 }
|
tomwalters@398
|
231 m_iPrevValEqual = 0;
|
tomwalters@398
|
232 m_fPrevVal = val;
|
tomwalters@398
|
233 m_bFirstPoint = false;
|
tomwalters@398
|
234 }
|
tomwalters@398
|
235 PlotDataPointDirect(x, y, val, height);
|
tomwalters@397
|
236 }
|
tomwalters@397
|
237
|
tomwalters@397
|
238 void GraphicsView::PlotDataPointDirect(float x,
|
tomwalters@397
|
239 float y,
|
tomwalters@397
|
240 float val,
|
tomwalters@397
|
241 float height) {
|
tomwalters@398
|
242 // Draw it in the right way
|
tomwalters@398
|
243 switch(m_iGraphType) {
|
tomwalters@398
|
244 case GraphTypeLine:
|
tomwalters@398
|
245 m_pDev->gVertex2f(x, y + val * height);
|
tomwalters@398
|
246 break;
|
tomwalters@398
|
247 case GraphTypeColormap:
|
tomwalters@398
|
248 //! \todo make it a real colormap instead of grayscale
|
tomwalters@398
|
249 m_pDev->gColor3f(val + 0.5, val + 0.5, val + 0.5);
|
tomwalters@398
|
250 m_pDev->gVertex2f(x, y - height / 2);
|
tomwalters@398
|
251 m_pDev->gVertex2f(x, y + height / 2);
|
tomwalters@398
|
252 break;
|
tomwalters@398
|
253 case GraphTypeNone:
|
tomwalters@398
|
254 // Nothing: just for testing computation overhead of graphing
|
tomwalters@398
|
255 break;
|
tomwalters@398
|
256 default:
|
tomwalters@398
|
257 // Shouldn't happen
|
tomwalters@398
|
258 AIM_ASSERT(0);
|
tomwalters@398
|
259 }
|
tomwalters@397
|
260 }
|
tom@399
|
261 } // namespace aimc
|