TimeRulerLayer.cpp

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam.
00008    
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "TimeRulerLayer.h"
00017 
00018 #include "LayerFactory.h"
00019 
00020 #include "data/model/Model.h"
00021 #include "base/RealTime.h"
00022 #include "base/ColourDatabase.h"
00023 #include "view/View.h"
00024 
00025 #include <QPainter>
00026 
00027 #include <iostream>
00028 #include <cmath>
00029 
00030 //#define DEBUG_TIME_RULER_LAYER 1
00031 
00032 using std::cerr;
00033 using std::endl;
00034 
00035 TimeRulerLayer::TimeRulerLayer() :
00036     SingleColourLayer(),
00037     m_model(0),
00038     m_labelHeight(LabelTop)
00039 {
00040     
00041 }
00042 
00043 void
00044 TimeRulerLayer::setModel(Model *model)
00045 {
00046     if (m_model == model) return;
00047     m_model = model;
00048     emit modelReplaced();
00049 }
00050 
00051 bool
00052 TimeRulerLayer::snapToFeatureFrame(View *v, int &frame,
00053                                    size_t &resolution, SnapType snap) const
00054 {
00055     if (!m_model) {
00056         resolution = 1;
00057         return false;
00058     }
00059 
00060     bool q;
00061     int tick = getMajorTickSpacing(v, q);
00062     RealTime rtick = RealTime::fromMilliseconds(tick);
00063     int rate = m_model->getSampleRate();
00064     
00065     RealTime rt = RealTime::frame2RealTime(frame, rate);
00066     double ratio = rt / rtick;
00067 
00068     int rounded = int(ratio);
00069     RealTime rdrt = rtick * rounded;
00070 
00071     int left = RealTime::realTime2Frame(rdrt, rate);
00072     resolution = RealTime::realTime2Frame(rtick, rate);
00073     int right = left + resolution;
00074 
00075 //    std::cerr << "TimeRulerLayer::snapToFeatureFrame: type "
00076 //              << int(snap) << ", frame " << frame << " (time "
00077 //              << rt << ", tick " << rtick << ", rounded " << rdrt << ") ";
00078 
00079     switch (snap) {
00080 
00081     case SnapLeft:
00082         frame = left;
00083         break;
00084 
00085     case SnapRight:
00086         frame = right;
00087         break;
00088         
00089     case SnapNearest:
00090     {
00091         if (abs(frame - left) > abs(right - frame)) {
00092             frame = right;
00093         } else {
00094             frame = left;
00095         }
00096         break;
00097     }
00098 
00099     case SnapNeighbouring:
00100     {
00101         int dl = -1, dr = -1;
00102         int x = v->getXForFrame(frame);
00103 
00104         if (left > v->getStartFrame() &&
00105             left < v->getEndFrame()) {
00106             dl = abs(v->getXForFrame(left) - x);
00107         }
00108 
00109         if (right > v->getStartFrame() &&
00110             right < v->getEndFrame()) {
00111             dr = abs(v->getXForFrame(right) - x);
00112         }
00113 
00114         int fuzz = 2;
00115 
00116         if (dl >= 0 && dr >= 0) {
00117             if (dl < dr) {
00118                 if (dl <= fuzz) {
00119                     frame = left;
00120                 }
00121             } else {
00122                 if (dr < fuzz) {
00123                     frame = right;
00124                 }
00125             }
00126         } else if (dl >= 0) {
00127             if (dl <= fuzz) {
00128                 frame = left;
00129             }
00130         } else if (dr >= 0) {
00131             if (dr <= fuzz) {
00132                 frame = right;
00133             }
00134         }
00135     }
00136     }
00137 
00138 //    std::cerr << " -> " << frame << " (resolution = " << resolution << ")" << std::endl;
00139 
00140     return true;
00141 }
00142 
00143 int
00144 TimeRulerLayer::getMajorTickSpacing(View *v, bool &quarterTicks) const
00145 {
00146     // return value is in milliseconds
00147 
00148     if (!m_model || !v) return 1000;
00149 
00150     int sampleRate = m_model->getSampleRate();
00151     if (!sampleRate) return 1000;
00152 
00153     long startFrame = v->getStartFrame();
00154     long endFrame = v->getEndFrame();
00155 
00156     int minPixelSpacing = 50;
00157 
00158     RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate);
00159     RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate);
00160 
00161     int count = v->width() / minPixelSpacing;
00162     if (count < 1) count = 1;
00163     RealTime rtGap = (rtEnd - rtStart) / count;
00164 
00165     int incms;
00166     quarterTicks = false;
00167 
00168     if (rtGap.sec > 0) {
00169         incms = 1000;
00170         int s = rtGap.sec;
00171         if (s > 0) { incms *= 5; s /= 5; }
00172         if (s > 0) { incms *= 2; s /= 2; }
00173         if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; }
00174         if (s > 0) { incms *= 5; s /= 5; quarterTicks = false; }
00175         if (s > 0) { incms *= 2; s /= 2; }
00176         if (s > 0) { incms *= 6; s /= 6; quarterTicks = true; }
00177         while (s > 0) {
00178             incms *= 10;
00179             s /= 10;
00180             quarterTicks = false;
00181         }
00182     } else {
00183         incms = 1;
00184         int ms = rtGap.msec();
00185         if (ms > 0) { incms *= 10; ms /= 10; }
00186         if (ms > 0) { incms *= 10; ms /= 10; }
00187         if (ms > 0) { incms *= 5; ms /= 5; }
00188         if (ms > 0) { incms *= 2; ms /= 2; }
00189     }
00190 
00191     return incms;
00192 }
00193 
00194 void
00195 TimeRulerLayer::paint(View *v, QPainter &paint, QRect rect) const
00196 {
00197 #ifdef DEBUG_TIME_RULER_LAYER
00198     std::cerr << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y()
00199               << ") [" << rect.width() << "x" << rect.height() << "]" << std::endl;
00200 #endif
00201     
00202     if (!m_model || !m_model->isOK()) return;
00203 
00204     int sampleRate = m_model->getSampleRate();
00205     if (!sampleRate) return;
00206 
00207     long startFrame = v->getFrameForX(rect.x() - 50);
00208 
00209 #ifdef DEBUG_TIME_RULER_LAYER
00210     std::cerr << "start frame = " << startFrame << std::endl;
00211 #endif
00212 
00213     bool quarter = false;
00214     int incms = getMajorTickSpacing(v, quarter);
00215 
00216     int ms = lrint(1000.0 * (double(startFrame) / double(sampleRate)));
00217     ms = (ms / incms) * incms - incms;
00218 
00219 #ifdef DEBUG_TIME_RULER_LAYER
00220     std::cerr << "start ms = " << ms << " at step " << incms << std::endl;
00221 #endif
00222 
00223     // Calculate the number of ticks per increment -- approximate
00224     // values for x and frame counts here will do, no rounding issue.
00225     // We always use the exact incms in our calculations for where to
00226     // draw the actual ticks or lines.
00227 
00228     int minPixelSpacing = 50;
00229     long incFrame = (incms * sampleRate) / 1000;
00230     int incX = incFrame / v->getZoomLevel();
00231     int ticks = 10;
00232     if (incX < minPixelSpacing * 2) {
00233         ticks = quarter ? 4 : 5;
00234     }
00235 
00236     QColor greyColour = getPartialShades(v)[1];
00237 
00238     paint.save();
00239 
00240     while (1) {
00241 
00242         // frame is used to determine where to draw the lines, so it
00243         // needs to correspond to an exact pixel (so that we don't get
00244         // a different pixel when scrolling a small amount and
00245         // re-drawing with a different start frame).
00246 
00247         double dms = ms;
00248         long frame = lrint((dms * sampleRate) / 1000.0);
00249         frame /= v->getZoomLevel();
00250         frame *= v->getZoomLevel(); // so frame corresponds to an exact pixel
00251 
00252         ms += incms;
00253 
00254         int x = v->getXForFrame(frame);
00255 
00256 #ifdef DEBUG_TIME_RULER_LAYER
00257         std::cerr << "Considering frame = " << frame << ", x = " << x << std::endl;
00258 #endif
00259 
00260         if (x >= rect.x() + rect.width() + 50) {
00261 #ifdef DEBUG_TIME_RULER_LAYER
00262             std::cerr << "X well out of range, ending here" << std::endl;
00263 #endif
00264             break;
00265         }
00266 
00267         if (x >= rect.x() - 50) {
00268 
00269 #ifdef DEBUG_TIME_RULER_LAYER
00270             std::cerr << "X in range, drawing line here" << std::endl;
00271 #endif
00272 
00273             RealTime rt = RealTime::fromMilliseconds(ms);
00274 
00275             QString text(QString::fromStdString(rt.toText()));
00276             QFontMetrics metrics = paint.fontMetrics();
00277             int tw = metrics.width(text);
00278 
00279             if (tw < 50 &&
00280                 (x < rect.x() - tw/2 ||
00281                  x >= rect.x() + rect.width() + tw/2)) {
00282 #ifdef DEBUG_TIME_RULER_LAYER
00283                 std::cerr << "hm, maybe X isn't in range after all (x = " << x << ", tw = " << tw << ", rect.x() = " << rect.x() << ", rect.width() = " << rect.width() << ")" << std::endl;
00284 #endif
00285             }
00286 
00287             paint.setPen(greyColour);
00288             paint.drawLine(x, 0, x, v->height());
00289 
00290             paint.setPen(getBaseQColor());
00291             paint.drawLine(x, 0, x, 5);
00292             paint.drawLine(x, v->height() - 6, x, v->height() - 1);
00293 
00294             int y;
00295             switch (m_labelHeight) {
00296             default:
00297             case LabelTop:
00298                 y = 6 + metrics.ascent();
00299                 break;
00300             case LabelMiddle:
00301                 y = v->height() / 2 - metrics.height() / 2 + metrics.ascent();
00302                 break;
00303             case LabelBottom:
00304                 y = v->height() - metrics.height() + metrics.ascent() - 6;
00305             }
00306 
00307             if (v->getViewManager() && v->getViewManager()->getOverlayMode() !=
00308                 ViewManager::NoOverlays) {
00309 
00310                 if (v->getLayer(0) == this) {
00311                     // backmost layer, don't worry about outlining the text
00312                     paint.drawText(x+2 - tw/2, y, text);
00313                 } else {
00314                     v->drawVisibleText(paint, x+2 - tw/2, y, text, View::OutlinedText);
00315                 }
00316             }
00317         }
00318 
00319         paint.setPen(greyColour);
00320 
00321         for (int i = 1; i < ticks; ++i) {
00322 
00323             dms = ms - incms + (i * double(incms)) / ticks;
00324             frame = lrint((dms * sampleRate) / 1000.0);
00325             frame /= v->getZoomLevel();
00326             frame *= v->getZoomLevel(); // exact pixel as above
00327 
00328             x = v->getXForFrame(frame);
00329 
00330             if (x < rect.x() || x >= rect.x() + rect.width()) {
00331 #ifdef DEBUG_TIME_RULER_LAYER
00332 //                std::cerr << "tick " << i << ": X out of range, going on to next tick" << std::endl;
00333 #endif
00334                 continue;
00335             }
00336 
00337 #ifdef DEBUG_TIME_RULER_LAYER
00338             std::cerr << "tick " << i << " in range, drawing at " << x << std::endl;
00339 #endif
00340 
00341             int sz = 5;
00342             if (ticks == 10) {
00343                 if ((i % 2) == 1) {
00344                     if (i == 5) {
00345                         paint.drawLine(x, 0, x, v->height());
00346                     } else sz = 3;
00347                 } else {
00348                     sz = 7;
00349                 }
00350             }
00351             paint.drawLine(x, 0, x, sz);
00352             paint.drawLine(x, v->height() - sz - 1, x, v->height() - 1);
00353         }
00354     }
00355 
00356     paint.restore();
00357 }
00358 
00359 int
00360 TimeRulerLayer::getDefaultColourHint(bool darkbg, bool &impose)
00361 {
00362     impose = true;
00363     return ColourDatabase::getInstance()->getColourIndex
00364         (QString(darkbg ? "White" : "Black"));
00365 }
00366 
00367 QString TimeRulerLayer::getLayerPresentationName() const
00368 {
00369     LayerFactory *factory = LayerFactory::getInstance();
00370     QString layerName = factory->getLayerPresentationName
00371         (factory->getLayerType(this));
00372     return layerName;
00373 }
00374 
00375 void
00376 TimeRulerLayer::toXml(QTextStream &stream,
00377                       QString indent, QString extraAttributes) const
00378 {
00379     SingleColourLayer::toXml(stream, indent, extraAttributes);
00380 }
00381 
00382 void
00383 TimeRulerLayer::setProperties(const QXmlAttributes &attributes)
00384 {
00385     SingleColourLayer::setProperties(attributes);
00386 }
00387 

Generated on Wed Feb 20 15:45:29 2008 for SonicVisualiser by  doxygen 1.5.1