svcore  1.9
Labeller.h
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-2007 Chris Cannam and QMUL.
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 #ifndef _LABELLER_H_
00017 #define _LABELLER_H_
00018 
00019 #include "SparseModel.h"
00020 #include "SparseValueModel.h"
00021 
00022 #include "base/Selection.h"
00023 
00024 #include <QObject>
00025 
00026 #include <map>
00027 #include <iostream>
00028 
00029 class Labeller : public QObject
00030 {
00031     Q_OBJECT
00032 
00033 public:
00034     enum ValueType {
00035         ValueNone,
00036         ValueFromSimpleCounter,
00037         ValueFromCyclicalCounter,
00038         ValueFromTwoLevelCounter,
00039         ValueFromFrameNumber,
00040         ValueFromRealTime,
00041         ValueFromDurationFromPrevious,
00042         ValueFromDurationToNext,
00043         ValueFromTempoFromPrevious,
00044         ValueFromTempoToNext,
00045         ValueFromExistingNeighbour,
00046         ValueFromLabel
00047     };
00048 
00049     // uses:
00050     //
00051     // 1. when adding points to a time-value model, generate values
00052     // for those points based on their times or labels or a counter
00053     //
00054     // 2. when adding a single point to a time-instant model, generate
00055     // a label for it based on its time and that of the previous point
00056     // or a counter
00057     //
00058     // 3. when adding a single point to a time-instant model, generate
00059     // a label for the previous point based on its time and that of
00060     // the point just added (as tempo is based on time to the next
00061     // point, not the previous one)
00062     //
00063     // 4. re-label a set of points that have already been added to a
00064     // model
00065 
00066     Labeller(ValueType type = ValueNone) :
00067         m_type(type),
00068         m_counter(1),
00069         m_counter2(1),
00070         m_cycle(4),
00071         m_dp(10),
00072         m_rate(0) { }
00073 
00074     Labeller(const Labeller &l) :
00075         QObject(),
00076         m_type(l.m_type),
00077         m_counter(l.m_counter),
00078         m_counter2(l.m_counter2),
00079         m_cycle(l.m_cycle),
00080         m_dp(l.m_dp),
00081         m_rate(l.m_rate) { }
00082 
00083     virtual ~Labeller() { }
00084 
00085     typedef std::map<ValueType, QString> TypeNameMap;
00086     TypeNameMap getTypeNames() const {
00087         TypeNameMap m;
00088         m[ValueNone]
00089             = tr("No numbering");
00090         m[ValueFromSimpleCounter]
00091             = tr("Simple counter");
00092         m[ValueFromCyclicalCounter]
00093             = tr("Cyclical counter");
00094         m[ValueFromTwoLevelCounter]
00095             = tr("Cyclical two-level counter (bar/beat)");
00096         m[ValueFromFrameNumber]
00097             = tr("Audio sample frame number");
00098         m[ValueFromRealTime]
00099             = tr("Time in seconds");
00100         m[ValueFromDurationToNext]
00101             = tr("Duration to the following item");
00102         m[ValueFromTempoToNext]
00103             = tr("Tempo (bpm) based on duration to following item");
00104         m[ValueFromDurationFromPrevious]
00105             = tr("Duration since the previous item");
00106         m[ValueFromTempoFromPrevious]
00107             = tr("Tempo (bpm) based on duration since previous item");
00108         m[ValueFromExistingNeighbour]
00109             = tr("Same as the nearest previous item");
00110         m[ValueFromLabel]
00111             = tr("Value extracted from the item's label (where possible)");
00112         return m;
00113     }
00114 
00115     ValueType getType() const { return m_type; }
00116     void setType(ValueType type) { m_type = type; }
00117 
00118     int getCounterValue() const { return m_counter; }
00119     void setCounterValue(int v) { m_counter = v; }
00120 
00121     int getSecondLevelCounterValue() const { return m_counter2; }
00122     void setSecondLevelCounterValue(int v) { m_counter2 = v; }
00123 
00124     int getCounterCycleSize() const { return m_cycle; }
00125     void setCounterCycleSize(int s) {
00126         m_cycle = s;
00127         m_dp = 1;
00128         while (s > 0) {
00129             s /= 10;
00130             m_dp *= 10;
00131         }
00132         if (m_counter > m_cycle) m_counter = 1;
00133     }
00134 
00135     void setSampleRate(float rate) { m_rate = rate; }
00136 
00137     void resetCounters() {
00138         m_counter = 1;
00139         m_counter2 = 1;
00140         m_cycle = 4;
00141     }
00142 
00143     void incrementCounter() {
00144         m_counter++;
00145         if (m_type == ValueFromCyclicalCounter ||
00146             m_type == ValueFromTwoLevelCounter) {
00147             if (m_counter > m_cycle) {
00148                 m_counter = 1;
00149                 m_counter2++;
00150             }
00151         }
00152     }
00153 
00154     template <typename PointType>
00155     void label(PointType &newPoint, PointType *prevPoint = 0) {
00156         if (m_type == ValueNone) {
00157             newPoint.label = "";
00158         } else if (m_type == ValueFromTwoLevelCounter) {
00159             newPoint.label = tr("%1.%2").arg(m_counter2).arg(m_counter);
00160             incrementCounter();
00161         } else if (m_type == ValueFromFrameNumber) {
00162             // avoid going through floating-point value
00163             newPoint.label = tr("%1").arg(newPoint.frame);
00164         } else {
00165             float value = getValueFor<PointType>(newPoint, prevPoint);
00166             if (actingOnPrevPoint() && prevPoint) {
00167                 prevPoint->label = QString("%1").arg(value);
00168             } else {
00169                 newPoint.label = QString("%1").arg(value);
00170             }
00171         }
00172     }
00173         
00174     template <typename PointType>
00175     void labelAll(SparseModel<PointType> &model, MultiSelection *ms) {
00176 
00177         typename SparseModel<PointType>::PointList::iterator i;
00178         typename SparseModel<PointType>::PointList pl(model.getPoints());
00179 
00180         typename SparseModel<PointType>::EditCommand *command =
00181             new typename SparseModel<PointType>::EditCommand
00182             (&model, tr("Label Points"));
00183 
00184         PointType prevPoint(0);
00185 
00186         for (i = pl.begin(); i != pl.end(); ++i) {
00187 
00188             bool inRange = true;
00189             if (ms) {
00190                 Selection s(ms->getContainingSelection(i->frame, false));
00191                 if (s.isEmpty() || !s.contains(i->frame)) {
00192                     inRange = false;
00193                 }
00194             }
00195 
00196             PointType p(*i);
00197 
00198             if (!inRange) {
00199                 prevPoint = p;
00200                 continue;
00201             }
00202 
00203             if (actingOnPrevPoint()) {
00204                 if (i != pl.begin()) {
00205                     command->deletePoint(prevPoint);
00206                     label<PointType>(p, &prevPoint);
00207                     command->addPoint(prevPoint);
00208                 }
00209             } else {
00210                 command->deletePoint(p);
00211                 label<PointType>(p, &prevPoint);
00212                 command->addPoint(p);
00213             }
00214 
00215             prevPoint = p;
00216         }
00217 
00218         command->finish();
00219     }
00220 
00221     template <typename PointType>
00222     void setValue(PointType &newPoint, PointType *prevPoint = 0) {
00223         if (m_type == ValueFromExistingNeighbour) {
00224             if (!prevPoint) {
00225                 std::cerr << "ERROR: Labeller::setValue: Previous point required but not provided" << std::endl;
00226             } else {
00227                 newPoint.value = prevPoint->value;
00228             }
00229         } else {
00230             float value = getValueFor<PointType>(newPoint, prevPoint);
00231             if (actingOnPrevPoint() && prevPoint) {
00232                 prevPoint->value = value;
00233             } else {
00234                 newPoint.value = value;
00235             }
00236         }
00237     }
00238 
00239     bool requiresPrevPoint() const {
00240         return (m_type == ValueFromDurationFromPrevious ||
00241                 m_type == ValueFromDurationToNext ||
00242                 m_type == ValueFromTempoFromPrevious ||
00243                 m_type == ValueFromDurationToNext);
00244     }
00245 
00246     bool actingOnPrevPoint() const {
00247         return (m_type == ValueFromDurationToNext ||
00248                 m_type == ValueFromTempoToNext);
00249     }
00250 
00251 protected:
00252     template <typename PointType>
00253     float getValueFor(PointType &newPoint, PointType *prevPoint)
00254     {
00255         float value = 0.f;
00256 
00257         switch (m_type) {
00258 
00259         case ValueNone:
00260             value = 0;
00261             break;
00262 
00263         case ValueFromSimpleCounter:
00264         case ValueFromCyclicalCounter:
00265             value = m_counter;
00266             incrementCounter();
00267             break;
00268 
00269         case ValueFromTwoLevelCounter:
00270             value = m_counter2 + double(m_counter) / double(m_dp);
00271             incrementCounter();
00272             break;
00273 
00274         case ValueFromFrameNumber:
00275             value = newPoint.frame;
00276             break;
00277             
00278         case ValueFromRealTime: 
00279             if (m_rate == 0.f) {
00280                 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
00281             } else {
00282                 value = float(newPoint.frame) / float(m_rate);
00283             }
00284             break;
00285 
00286         case ValueFromDurationToNext:
00287         case ValueFromTempoToNext:
00288         case ValueFromDurationFromPrevious:
00289         case ValueFromTempoFromPrevious:
00290             if (m_rate == 0.f) {
00291                 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
00292             } else if (!prevPoint) {
00293                 std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl;
00294             } else {
00295                 int f0 = prevPoint->frame, f1 = newPoint.frame;
00296                 if (m_type == ValueFromDurationToNext ||
00297                     m_type == ValueFromDurationFromPrevious) {
00298                     value = float(f1 - f0) / m_rate;
00299                 } else {
00300                     if (f1 > f0) {
00301                         value = (60.f * m_rate) / (f1 - f0);
00302                     }
00303                 }
00304             }
00305             break;
00306 
00307         case ValueFromExistingNeighbour:
00308             // need to deal with this in the calling function, as this
00309             // function must handle points that don't have values to
00310             // read from
00311             break;
00312 
00313         case ValueFromLabel:
00314             if (newPoint.label != "") {
00315                 // more forgiving than QString::toFloat()
00316                 value = atof(newPoint.label.toLocal8Bit());
00317             } else {
00318                 value = 0.f;
00319             }
00320             break;
00321         }
00322 
00323         return value;
00324     }
00325 
00326     ValueType m_type;
00327     int m_counter;
00328     int m_counter2;
00329     int m_cycle;
00330     int m_dp;
00331     float m_rate;
00332 };
00333 
00334 #endif