"Post Window" -> "Post window" prevents it being seen as two separate
[supercollider.git] / QtCollider / widgets / QcScopeShm.cpp
blob6e894d2f1b61bc5dbab1462082dce17e4369bb8b
1 /************************************************************************
3 * Copyright 2010-2012 Jakob Leben (jakob.leben@gmail.com)
5 * This file is part of Qt GUI for SuperCollider.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ************************************************************************/
22 #include "QcScopeShm.h"
23 #include "scope_shm_interface.hpp"
24 #include "../QcWidgetFactory.h"
25 #include "../debug.h"
27 #include <QPainter>
28 #include <QTimer>
29 #include <QResizeEvent>
31 QC_DECLARE_QWIDGET_FACTORY(QcScopeShm);
33 QcScopeShm::QcScopeShm() :
34 _srvPort(-1),
35 _scopeIndex(-1),
36 _shm(new ScopeShm(this)),
37 _running(false),
38 _data(0),
39 _availableFrames(0),
40 xOffset( 0.f ),
41 yOffset( 0.f ),
42 xZoom( 1.f ),
43 yZoom( 1.f ),
44 _style( 0 ),
45 _bkg( QColor(0,0,0) )
47 setAttribute( Qt::WA_OpaquePaintEvent, true );
48 setAutoFillBackground(false);
50 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
52 timer = new QTimer( this );
53 timer->setInterval( 50 );
54 connect( timer, SIGNAL( timeout() ), this, SLOT( updateScope() ) );
57 QcScopeShm::~QcScopeShm()
59 stop();
62 void QcScopeShm::setServerPort( int port )
64 if( _running ) {
65 qcWarningMsg( "QScope: Can not change server port while running!" );
66 return;
69 _srvPort = port;
72 void QcScopeShm::setBufferNumber( int n )
74 if( _running ) {
75 // TODO: release used reader?
76 initScopeReader( _shm, n );
78 _scopeIndex = n;
81 void QcScopeShm::setWaveColors( const VariantList & newColors )
83 colors.clear();
84 Q_FOREACH( QVariant var, newColors.data ) {
85 QColor color = var.value<QColor>();
86 if( !color.isValid() )
87 colors.append( QColor( 0,0,0 ) );
88 else
89 colors.append( color );
93 int QcScopeShm::updateInterval() const {
94 return timer->interval();
97 void QcScopeShm::setUpdateInterval( int interval ) {
98 timer->setInterval( qMax(0, interval) );
101 void QcScopeShm::start()
103 if( _running ) return;
104 if( _srvPort < 0 || _scopeIndex < 0 ) return;
106 connectSharedMemory( _srvPort );
107 if( !_shm->client ) {
108 stop();
109 return;
112 initScopeReader( _shm, _scopeIndex );
114 timer->start();
116 _running = true;
119 void QcScopeShm::stop()
121 // TODO: release used reader?
123 delete _shm->client;
124 _shm->client = 0;
126 timer->stop();
128 _running = false;
131 void QcScopeShm::updateScope()
133 bool valid = _shm->reader.valid();
134 //qcDebugMsg(1, tr("valid = %1").arg(valid));
135 if(!valid) return;
137 bool ok = _shm->reader.pull( _availableFrames );
138 //qcDebugMsg(1, tr("Got %1 frames").arg(_availableFrames) );
139 if(ok) {
140 _data = _shm->reader.data();
141 update();
145 void QcScopeShm::resizeEvent ( QResizeEvent * ev )
147 _pixmap = QPixmap(ev->size());
150 void QcScopeShm::paintEvent ( QPaintEvent * event )
152 Q_UNUSED( event );
154 QPainter p;
156 _pixmap.fill( _bkg );
158 if( _running && _availableFrames ) {
160 int chanCount = _shm->reader.channels();
161 int maxFrames = _shm->reader.max_frames();
162 QRect area (_pixmap.rect());
163 p.begin(&_pixmap);
165 switch (_style) {
166 case 0:
167 paint1D( false, chanCount, maxFrames, _availableFrames, area, p );
168 break;
169 case 1:
170 paint1D( true, chanCount, maxFrames, _availableFrames, area, p );
171 break;
172 case 2:
173 paint2D( chanCount, maxFrames, _availableFrames, area, p );
174 break;
177 p.end();
180 p.begin(this);
181 p.drawPixmap(0, 0, _pixmap);
184 void QcScopeShm::paint1D( bool overlapped, int chanCount, int maxFrames, int frameCount,
185 const QRect &area, QPainter & painter )
187 //qcDebugMsg( 0, tr("Drawing: data %1 / channels %2 / max-size %3").arg(_data!=0).arg(chanCount).arg(maxFrames) );
189 if( frameCount < 2 || area.width() < 1 || area.height() < 1 ) return;
191 float yRatio = - yZoom * area.height() * 0.5;
192 if( !overlapped ) yRatio /= chanCount;
193 float yHeight = area.height();
194 if( !overlapped ) yHeight /= chanCount;
196 if( frameCount < area.width() )
198 float xRatio = xZoom * area.width() / (frameCount-1);
200 for( int ch = 0; ch < chanCount; ch++ ) {
201 float *frameData = _data + (ch * maxFrames); //frame vector
202 float yOrigin = yHeight * (overlapped ? 0.5 : ch + 0.5);
203 QColor color = ( ch < colors.count() ? colors[ch] : QColor(255,255,255) );
205 painter.save();
206 painter.translate( area.x(), area.y() + yOrigin );
207 painter.scale( xRatio, yRatio );
208 painter.setPen(color);
210 QPainterPath path;
211 path.moveTo( xOffset, frameData[0] );
212 for( int f = 1; f < frameCount; ++f )
213 path.lineTo( xOffset + f, frameData[f] );
215 painter.drawPath(path);
217 painter.restore();
220 else
222 int w = area.width();
223 float fpp = frameCount / (float) w; // frames per x pixel
224 float ypix = yRatio != 0.f ? -1/yRatio : 0.f; // value per y pixel;
226 for( int ch = 0; ch < chanCount; ch++ )
228 float *frameData = _data + (ch * maxFrames); //frame vector
229 float yOrigin = yHeight * (overlapped ? 0.5 : ch + 0.5);
230 QColor color = ( ch < colors.count() ? colors[ch] : QColor(255,255,255) );
232 painter.save();
233 painter.translate( area.x(), area.y() + yOrigin );
234 QPen pen(color);
235 pen.setCapStyle( Qt::FlatCap );
236 painter.setPen(pen);
238 QPainterPath path;
240 int p=0, f=1; // pixel, frame
241 float min, max;
242 min = max = frameData[0];
244 while( p++ < w )
246 int f_max = fpp * p;
248 for(; f < f_max; ++f)
250 float d = frameData[f];
251 if( d < min ) min = d;
252 if( d > max ) max = d;
255 qreal x = p-1;
256 float y = max * yRatio;
257 path.moveTo( x, y );
258 y = qMax( min * yRatio, y+1 );
259 path.lineTo( x, y );
261 // flip min/max to ensure continuity
262 float val = min;
263 min = max;
264 max = val;
267 painter.drawPath(path);
269 painter.restore();
274 void QcScopeShm::paint2D( int chanCount, int maxFrames, int frameCount,
275 const QRect &area, QPainter & painter )
277 QColor color = colors.count() ? colors[0] : QColor(255,255,255);
279 int minSize = qMin( area.width(), area.height() );
280 // NOTE: use yZoom for both axis, since both represent value, as opposed to index
281 float xRatio = yZoom * minSize * 0.5;
282 float yRatio = -yZoom * minSize * 0.5;
283 QPoint center = area.center();
285 painter.setPen(color);
286 painter.translate( center.x(), center.y() );
287 painter.scale( xRatio, yRatio );
289 QPainterPath path;
291 if( chanCount >= 2 )
293 float *data1 = _data;
294 float *data2 = _data + maxFrames;
296 path.moveTo( data1[0], data2[0] );
297 for( int f = 1; f < frameCount; ++f )
298 path.lineTo( data1[f], data2[f] );
300 else
302 float *data1 = _data;
303 path.moveTo( data1[0], 0.f );
304 for( int f = 1; f < frameCount; ++f )
305 path.lineTo( data1[f], 0.f );
308 painter.drawPath(path);
311 void QcScopeShm::connectSharedMemory( int port )
313 try {
314 server_shared_memory_client * client = new server_shared_memory_client(port);
315 _shm->client = client;
316 qcDebugMsg(1,"Shared memory connected");
317 } catch (std::exception & e) {
318 _shm->client = 0;
319 qcErrorMsg(QString("Cannot connect to shared memory: %1").arg(e.what()) );
323 void QcScopeShm::initScopeReader( ScopeShm *shm, int index )
325 shm->reader = shm->client->get_scope_buffer_reader( index );
326 qcDebugMsg(1,QString("Initialized scope buffer reader for index %1.").arg(index));