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

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