tomwalters@397: // Copyright 2006, Willem van Engen tomwalters@397: // tomwalters@397: // AIM-C: A C++ implementation of the Auditory Image Model tomwalters@397: // http://www.acousticscale.org/AIMC tomwalters@397: // tomwalters@397: // Licensed under the Apache License, Version 2.0 (the "License"); tomwalters@397: // you may not use this file except in compliance with the License. tomwalters@397: // You may obtain a copy of the License at tomwalters@397: // tomwalters@397: // http://www.apache.org/licenses/LICENSE-2.0 tomwalters@397: // tomwalters@397: // Unless required by applicable law or agreed to in writing, software tomwalters@397: // distributed under the License is distributed on an "AS IS" BASIS, tomwalters@397: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. tomwalters@397: // See the License for the specific language governing permissions and tomwalters@397: // limitations under the License. tomwalters@397: tom@399: #include tom@399: tomwalters@397: #include "Support/Common.h" tomwalters@397: tom@399: #include "Modules/Output/Graphics/GraphicsView.h" tom@399: #include "Modules/Output/Graphics/Devices/GraphicsOutputDevice.h" tomwalters@411: #include "Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.h" tom@399: tom@399: namespace aimc { tomwalters@397: tomwalters@397: GraphicsView::GraphicsView(Parameters *parameters) : Module(parameters) { tomwalters@402: module_description_ = "Graphics output."; tomwalters@397: module_identifier_ = "graphics"; tomwalters@397: module_type_ = "output"; tomwalters@397: module_version_ = "$Id: $"; tomwalters@443: tomwalters@443: if (parameters_->DefaultBool("output.images", false)) { tomwalters@443: m_pDev = new GraphicsOutputDeviceCairo(parameters_); tomwalters@443: } else { tomwalters@443: m_pDev = new GraphicsOutputDeviceMovie(parameters_); tomwalters@443: } tomwalters@398: m_bPlotLabels = false; tomwalters@398: m_pAxisX = new GraphAxisSpec(); tomwalters@398: AIM_ASSERT(m_pAxisX); tomwalters@398: m_pAxisY = new GraphAxisSpec(); tomwalters@398: AIM_ASSERT(m_pAxisY); tomwalters@398: m_pAxisFreq = new GraphAxisSpec(); tomwalters@398: AIM_ASSERT(m_pAxisFreq); tom@399: initialized_ = true; tomwalters@397: tomwalters@410: if (!m_pAxisY->Initialize(parameters_, tom@399: _S("graph.y"), tom@399: -1, tom@399: 1, tom@399: Scale::SCALE_LINEAR)) { tom@399: LOG_ERROR("Axis initialization failed"); tom@399: initialized_ = false; tom@399: } tomwalters@411: m_fMarginLeft = parameters_->DefaultFloat(_S("graph.margin.left"), 0.05); tomwalters@411: m_fMarginRight = parameters_->DefaultFloat(_S("graph.margin.right"), 0.005); tomwalters@411: m_fMarginTop = parameters_->DefaultFloat(_S("graph.margin.top"), 0.005); tomwalters@411: m_fMarginBottom = parameters_->DefaultFloat(_S("graph.margin.bottom"), 0.05); tomwalters@411: m_bPlotLabels = parameters_->DefaultBool(_S("graph.plotlabels"), true); tomwalters@569: plotting_strobes_ = parameters_->DefaultBool(_S("graph.plot_strobes"), false); tomwalters@397: tomwalters@411: const char *sGraphType = parameters_->DefaultString(_S("graph.type"), "line"); tomwalters@398: if (strcmp(sGraphType, _S("line"))==0) tomwalters@398: m_iGraphType = GraphTypeLine; tomwalters@398: else if (strcmp(sGraphType, _S("colormap"))==0) tomwalters@398: m_iGraphType = GraphTypeColormap; tomwalters@398: else if (strcmp(sGraphType, _S("none"))==0) tomwalters@398: m_iGraphType = GraphTypeNone; tomwalters@398: else { tom@399: LOG_ERROR(_T("Unrecognized graph type: '%s'"), sGraphType); tom@399: initialized_ = false; tomwalters@398: } tomwalters@397: tomwalters@411: if (strcmp(parameters_->DefaultString(_S("graph.mindistance"), "auto"),"auto") == 0) tomwalters@398: // -1 means detect later, based on type and Fire() argument tomwalters@398: m_fMinPlotDistance = -1; tomwalters@398: else tomwalters@410: m_fMinPlotDistance = parameters_->GetFloat(_S("graph.mindistance")); tomwalters@397: } tomwalters@397: tomwalters@397: GraphicsView::~GraphicsView() { tomwalters@398: DELETE_IF_NONNULL(m_pAxisX); tomwalters@398: DELETE_IF_NONNULL(m_pAxisY); tomwalters@398: DELETE_IF_NONNULL(m_pAxisFreq); tomwalters@397: } tomwalters@397: tom@399: void GraphicsView::ResetInternal() { tomwalters@411: if (m_pDev != NULL) { tomwalters@418: m_pDev->Reset(global_parameters_); tomwalters@411: } tom@399: } tomwalters@397: tomwalters@397: bool GraphicsView::InitializeInternal(const SignalBank &bank) { tomwalters@398: if (!m_pDev) { tomwalters@398: LOG_ERROR("Output device not connected"); tomwalters@398: return false; tomwalters@398: } tomwalters@397: tomwalters@397: float y_min = bank.centre_frequency(0); tomwalters@397: float y_max = bank.centre_frequency(bank.channel_count() - 1); tomwalters@410: if (!m_pAxisFreq->Initialize(parameters_, tomwalters@397: "graph.freq", tomwalters@398: y_min, tomwalters@398: y_max, tomwalters@398: Scale::SCALE_ERB)) { tomwalters@411: LOG_ERROR("Frequency axis init failed."); tomwalters@397: return false; tomwalters@397: } tomwalters@397: tomwalters@397: float x_min = 0.0; tomwalters@397: float x_max = 1000.0 * bank.buffer_length() / bank.sample_rate(); tomwalters@410: if (!m_pAxisX->Initialize(parameters_, tomwalters@397: "graph.x", tomwalters@398: x_min, tomwalters@397: x_max, tomwalters@398: Scale::SCALE_LINEAR)) { tomwalters@411: LOG_ERROR("Time axis init failed."); tomwalters@397: return false; tomwalters@397: } tomwalters@397: tomwalters@398: /* Inform graphics output of maximum number of vertices between tomwalters@398: * gBegin*() and gEnd(), for any type of plot. Colormap needs most. tomwalters@398: */ tomwalters@411: LOG_INFO("Initializing graphics output device."); tomwalters@411: tomwalters@411: if (!m_pDev->Initialize(global_parameters_)) { tomwalters@411: LOG_ERROR("Graphics output device init failed."); tomwalters@397: return false; tomwalters@397: } tomwalters@411: m_pDev->Start(); tomwalters@411: previous_start_time_ = 0; tom@399: return true; tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsView::Process(const SignalBank &bank) { tomwalters@398: float height = 1.0 / bank.channel_count(); tomwalters@398: float heightMinMargin = height * (1.0f - m_fMarginBottom - m_fMarginTop); tomwalters@398: float xScaling = 1.0f; tomwalters@569: float diameter = height / 5.0; tomwalters@397: tomwalters@398: m_pDev->gGrab(); tomwalters@398: PlotAxes(bank); tomwalters@398: m_pDev->gColor3f(1.0f, 1.0f, 0.8f); tomwalters@398: for (int i = 0; i < bank.channel_count(); i++) { tomwalters@398: float yOffs = bank.centre_frequency(i); tomwalters@398: yOffs = m_pAxisFreq->m_pScale->FromLinearScaled(yOffs) + 0.5f; tomwalters@398: /* Don't plot below zero and stop when above 1, since yOffs is tomwalters@397: * monotonically increasing. Because of rounding errors, we need tomwalters@397: * to check for yOffs < -1e-6 instead of yOffs < 0. */ tomwalters@398: if (yOffs < -1e-6) tomwalters@397: continue; tomwalters@398: if (yOffs > 1) tomwalters@397: break; tomwalters@397: tomwalters@397: // Scale to single channel graphing. tomwalters@398: yOffs = yOffs * (1.0f - height) + height / 2.0; tomwalters@398: yOffs = yOffs * (1.0f - m_fMarginTop - m_fMarginBottom) + m_fMarginBottom; tom@399: PlotData(bank[i], bank.sample_rate(), yOffs, heightMinMargin, xScaling); tomwalters@569: if (plotting_strobes_) { tomwalters@569: PlotStrobes(bank[i], bank.get_strobes(i), bank.sample_rate(), tomwalters@569: yOffs, heightMinMargin ,xScaling, diameter); tomwalters@569: } tomwalters@398: } tomwalters@398: m_pDev->gRelease(); tomwalters@411: tomwalters@411: frame_rate_ = bank.sample_rate() tomwalters@411: / (bank.start_time() - previous_start_time_); tomwalters@411: previous_start_time_ = bank.start_time(); tomwalters@411: global_parameters_->SetFloat("frame_rate", frame_rate_); tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsView::SetAxisScale(Scale::ScaleType iHori, tomwalters@397: Scale::ScaleType iVert, tomwalters@397: Scale::ScaleType iFreq) { tomwalters@398: AIM_ASSERT(m_pAxisX); tomwalters@398: AIM_ASSERT(m_pAxisY); tomwalters@398: AIM_ASSERT(m_pAxisFreq); tomwalters@398: m_pAxisX->SetDisplayScale(iHori); tomwalters@398: m_pAxisY->SetDisplayScale(iVert); tomwalters@398: m_pAxisFreq->SetDisplayScale(iFreq); tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsView::BeginDataStrip() { tomwalters@398: m_iPrevValEqual=0; tomwalters@398: m_bFirstPoint=true; tomwalters@398: switch(m_iGraphType) { tomwalters@398: case GraphTypeLine: tomwalters@398: m_pDev->gBeginLineStrip(); tomwalters@398: break; tomwalters@398: case GraphTypeColormap: tomwalters@398: m_pDev->gBeginQuadStrip(); tomwalters@398: break; tomwalters@398: case GraphTypeNone: tomwalters@398: // Nothing: just for testing computation overhead of graphing tomwalters@398: break; tomwalters@398: } tomwalters@397: } tomwalters@397: tomwalters@397: void GraphicsView::PlotDataPoint(float x, tomwalters@397: float y, tomwalters@397: float val, tomwalters@397: float height, tomwalters@397: bool isLast) { tomwalters@398: AIM_ASSERT(m_pDev); tomwalters@397: tomwalters@398: /* Reduce the number of points plotted by eliminating duplicate values: tomwalters@398: * tomwalters@398: * oooo o--o tomwalters@398: * o / o tomwalters@398: * oooo ooo => o--o o--o tomwalters@398: * oooo ooo o--o o-o tomwalters@398: * tomwalters@398: * with 'o' points that are plotted, and '-' by the graphics output tomwalters@398: * device interpolated points. We could be smarter and include tomwalters@398: * first-order interpolation, but we leave that as an exercise for tomwalters@398: * the reader. Please send your patches :) tomwalters@398: * tomwalters@398: * So, we don't draw points that are too close to the previous value. tomwalters@398: * But if the value changes (or it's the last point), we must draw the tomwalters@398: * previous point too. tomwalters@398: */ tomwalters@398: if (!m_bFirstPoint tomwalters@397: && !isLast tom@399: && abs(m_fPrevVal-val) < m_fMinPlotDistance) { tomwalters@398: m_iPrevValEqual++; tomwalters@398: // Don't set m_fPrevVal to avoid not catching slow changes tomwalters@398: m_fPrevX = x; tomwalters@398: m_fPrevY = y; tomwalters@398: m_fPrevHeight = height; tomwalters@398: return; tomwalters@398: } else { tomwalters@398: if (m_iPrevValEqual > 0) { tomwalters@398: // Draw previous point tomwalters@398: PlotDataPointDirect(m_fPrevX, m_fPrevY, m_fPrevVal, m_fPrevHeight); tomwalters@398: } tomwalters@398: m_iPrevValEqual = 0; tomwalters@398: m_fPrevVal = val; tomwalters@398: m_bFirstPoint = false; tomwalters@398: } tomwalters@398: PlotDataPointDirect(x, y, val, height); tomwalters@397: } tomwalters@397: tomwalters@569: void GraphicsView::PlotStrobe(float x, float y, float val, tomwalters@569: float height, tomwalters@569: float diameter) { tomwalters@569: switch(m_iGraphType) { tomwalters@569: case GraphTypeLine: tomwalters@569: m_pDev->gBeginQuadStrip(); tomwalters@569: m_pDev->gVertex2f(x + diameter, y + val * height + diameter); tomwalters@569: m_pDev->gVertex2f(x + diameter, y + val * height - diameter); tomwalters@569: m_pDev->gVertex2f(x - diameter, y + val * height - diameter); tomwalters@569: m_pDev->gVertex2f(x - diameter, y + val * height + diameter); tomwalters@569: m_pDev->gEnd(); tomwalters@569: break; tomwalters@569: case GraphTypeColormap: tomwalters@569: m_pDev->gBeginQuadStrip(); tomwalters@569: m_pDev->gVertex2f(x + diameter, y + diameter); tomwalters@569: m_pDev->gVertex2f(x + diameter, y - diameter); tomwalters@569: m_pDev->gVertex2f(x - diameter, y - diameter); tomwalters@569: m_pDev->gVertex2f(x - diameter, y + diameter); tomwalters@569: m_pDev->gEnd(); tomwalters@569: break; tomwalters@569: case GraphTypeNone: tomwalters@569: // Nothing: just for testing computation overhead of graphing tomwalters@569: break; tomwalters@569: default: tomwalters@569: // Shouldn't happen tomwalters@569: AIM_ASSERT(0); tomwalters@569: } tomwalters@569: } tomwalters@569: tomwalters@397: void GraphicsView::PlotDataPointDirect(float x, tomwalters@397: float y, tomwalters@397: float val, tomwalters@397: float height) { tomwalters@398: // Draw it in the right way tomwalters@398: switch(m_iGraphType) { tomwalters@398: case GraphTypeLine: tomwalters@398: m_pDev->gVertex2f(x, y + val * height); tomwalters@398: break; tomwalters@398: case GraphTypeColormap: tomwalters@398: //! \todo make it a real colormap instead of grayscale tomwalters@398: m_pDev->gColor3f(val + 0.5, val + 0.5, val + 0.5); tomwalters@398: m_pDev->gVertex2f(x, y - height / 2); tomwalters@398: m_pDev->gVertex2f(x, y + height / 2); tomwalters@398: break; tomwalters@398: case GraphTypeNone: tomwalters@398: // Nothing: just for testing computation overhead of graphing tomwalters@398: break; tomwalters@398: default: tomwalters@398: // Shouldn't happen tomwalters@398: AIM_ASSERT(0); tomwalters@398: } tomwalters@397: } tom@399: } // namespace aimc