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