| Chris@305 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@305 | 2 | 
| Chris@305 | 3 /* | 
| Chris@305 | 4     Sonic Visualiser | 
| Chris@305 | 5     An audio file viewer and annotation editor. | 
| Chris@305 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@305 | 7     This file copyright 2006-2007 Chris Cannam and QMUL. | 
| Chris@305 | 8 | 
| Chris@305 | 9     This program is free software; you can redistribute it and/or | 
| Chris@305 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@305 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@305 | 12     License, or (at your option) any later version.  See the file | 
| Chris@305 | 13     COPYING included with this distribution for more information. | 
| Chris@305 | 14 */ | 
| Chris@305 | 15 | 
| Chris@1581 | 16 #ifndef SV_LABELLER_H | 
| Chris@1581 | 17 #define SV_LABELLER_H | 
| Chris@305 | 18 | 
| Chris@1652 | 19 #include "base/Selection.h" | 
| Chris@1652 | 20 #include "base/Event.h" | 
| Chris@305 | 21 | 
| Chris@1652 | 22 #include "EventCommands.h" | 
| Chris@305 | 23 | 
| Chris@305 | 24 #include <QObject> | 
| Chris@305 | 25 | 
| Chris@305 | 26 #include <map> | 
| Chris@305 | 27 #include <iostream> | 
| Chris@305 | 28 | 
| Chris@305 | 29 class Labeller : public QObject | 
| Chris@305 | 30 { | 
| Chris@423 | 31     Q_OBJECT | 
| Chris@423 | 32 | 
| Chris@305 | 33 public: | 
| Chris@305 | 34     enum ValueType { | 
| Chris@305 | 35         ValueNone, | 
| Chris@305 | 36         ValueFromSimpleCounter, | 
| Chris@305 | 37         ValueFromCyclicalCounter, | 
| Chris@305 | 38         ValueFromTwoLevelCounter, | 
| Chris@305 | 39         ValueFromFrameNumber, | 
| Chris@305 | 40         ValueFromRealTime, | 
| Chris@355 | 41         ValueFromDurationFromPrevious, | 
| Chris@355 | 42         ValueFromDurationToNext, | 
| Chris@355 | 43         ValueFromTempoFromPrevious, | 
| Chris@355 | 44         ValueFromTempoToNext, | 
| Chris@305 | 45         ValueFromExistingNeighbour, | 
| Chris@305 | 46         ValueFromLabel | 
| Chris@305 | 47     }; | 
| Chris@305 | 48 | 
| Chris@305 | 49     // uses: | 
| Chris@305 | 50     // | 
| Chris@305 | 51     // 1. when adding points to a time-value model, generate values | 
| Chris@305 | 52     // for those points based on their times or labels or a counter | 
| Chris@305 | 53     // | 
| Chris@305 | 54     // 2. when adding a single point to a time-instant model, generate | 
| Chris@305 | 55     // a label for it based on its time and that of the previous point | 
| Chris@305 | 56     // or a counter | 
| Chris@305 | 57     // | 
| Chris@305 | 58     // 3. when adding a single point to a time-instant model, generate | 
| Chris@305 | 59     // a label for the previous point based on its time and that of | 
| Chris@305 | 60     // the point just added (as tempo is based on time to the next | 
| Chris@305 | 61     // point, not the previous one) | 
| Chris@305 | 62     // | 
| Chris@305 | 63     // 4. re-label a set of points that have already been added to a | 
| Chris@305 | 64     // model | 
| Chris@1652 | 65     // | 
| Chris@1652 | 66     // 5. generate new labelled points in the gaps between other | 
| Chris@1652 | 67     // points (subdivide), or remove them (winnow) | 
| Chris@305 | 68 | 
| Chris@355 | 69     Labeller(ValueType type = ValueNone) : | 
| Chris@305 | 70         m_type(type), | 
| Chris@305 | 71         m_counter(1), | 
| Chris@305 | 72         m_counter2(1), | 
| Chris@305 | 73         m_cycle(4), | 
| Chris@305 | 74         m_dp(10), | 
| Chris@305 | 75         m_rate(0) { } | 
| Chris@305 | 76 | 
| Chris@305 | 77     Labeller(const Labeller &l) : | 
| Chris@305 | 78         QObject(), | 
| Chris@305 | 79         m_type(l.m_type), | 
| Chris@380 | 80         m_counter(l.m_counter), | 
| Chris@380 | 81         m_counter2(l.m_counter2), | 
| Chris@305 | 82         m_cycle(l.m_cycle), | 
| Chris@305 | 83         m_dp(l.m_dp), | 
| Chris@305 | 84         m_rate(l.m_rate) { } | 
| Chris@305 | 85 | 
| Chris@305 | 86     virtual ~Labeller() { } | 
| Chris@305 | 87 | 
| Chris@305 | 88     typedef std::map<ValueType, QString> TypeNameMap; | 
| Chris@305 | 89     TypeNameMap getTypeNames() const { | 
| Chris@305 | 90         TypeNameMap m; | 
| Chris@355 | 91         m[ValueNone] | 
| Chris@355 | 92             = tr("No numbering"); | 
| Chris@355 | 93         m[ValueFromSimpleCounter] | 
| Chris@355 | 94             = tr("Simple counter"); | 
| Chris@355 | 95         m[ValueFromCyclicalCounter] | 
| Chris@355 | 96             = tr("Cyclical counter"); | 
| Chris@355 | 97         m[ValueFromTwoLevelCounter] | 
| Chris@355 | 98             = tr("Cyclical two-level counter (bar/beat)"); | 
| Chris@355 | 99         m[ValueFromFrameNumber] | 
| Chris@355 | 100             = tr("Audio sample frame number"); | 
| Chris@355 | 101         m[ValueFromRealTime] | 
| Chris@355 | 102             = tr("Time in seconds"); | 
| Chris@355 | 103         m[ValueFromDurationToNext] | 
| Chris@355 | 104             = tr("Duration to the following item"); | 
| Chris@355 | 105         m[ValueFromTempoToNext] | 
| Chris@355 | 106             = tr("Tempo (bpm) based on duration to following item"); | 
| Chris@355 | 107         m[ValueFromDurationFromPrevious] | 
| Chris@355 | 108             = tr("Duration since the previous item"); | 
| Chris@355 | 109         m[ValueFromTempoFromPrevious] | 
| Chris@355 | 110             = tr("Tempo (bpm) based on duration since previous item"); | 
| Chris@355 | 111         m[ValueFromExistingNeighbour] | 
| Chris@355 | 112             = tr("Same as the nearest previous item"); | 
| Chris@355 | 113         m[ValueFromLabel] | 
| Chris@355 | 114             = tr("Value extracted from the item's label (where possible)"); | 
| Chris@305 | 115         return m; | 
| Chris@305 | 116     } | 
| Chris@305 | 117 | 
| Chris@305 | 118     ValueType getType() const { return m_type; } | 
| Chris@305 | 119     void setType(ValueType type) { m_type = type; } | 
| Chris@305 | 120 | 
| Chris@305 | 121     int getCounterValue() const { return m_counter; } | 
| Chris@305 | 122     void setCounterValue(int v) { m_counter = v; } | 
| Chris@305 | 123 | 
| Chris@305 | 124     int getSecondLevelCounterValue() const { return m_counter2; } | 
| Chris@305 | 125     void setSecondLevelCounterValue(int v) { m_counter2 = v; } | 
| Chris@305 | 126 | 
| Chris@305 | 127     int getCounterCycleSize() const { return m_cycle; } | 
| Chris@305 | 128     void setCounterCycleSize(int s) { | 
| Chris@305 | 129         m_cycle = s; | 
| Chris@305 | 130         m_dp = 1; | 
| Chris@305 | 131         while (s > 0) { | 
| Chris@305 | 132             s /= 10; | 
| Chris@305 | 133             m_dp *= 10; | 
| Chris@305 | 134         } | 
| Chris@307 | 135         if (m_counter > m_cycle) m_counter = 1; | 
| Chris@305 | 136     } | 
| Chris@305 | 137 | 
| Chris@1046 | 138     void setSampleRate(sv_samplerate_t rate) { m_rate = rate; } | 
| Chris@305 | 139 | 
| Chris@830 | 140     void resetCounters() { | 
| Chris@830 | 141         m_counter = 1; | 
| Chris@830 | 142         m_counter2 = 1; | 
| Chris@830 | 143         m_cycle = 4; | 
| Chris@830 | 144     } | 
| Chris@830 | 145 | 
| Chris@305 | 146     void incrementCounter() { | 
| Chris@305 | 147         m_counter++; | 
| Chris@305 | 148         if (m_type == ValueFromCyclicalCounter || | 
| Chris@305 | 149             m_type == ValueFromTwoLevelCounter) { | 
| Chris@305 | 150             if (m_counter > m_cycle) { | 
| Chris@305 | 151                 m_counter = 1; | 
| Chris@305 | 152                 m_counter2++; | 
| Chris@305 | 153             } | 
| Chris@305 | 154         } | 
| Chris@305 | 155     } | 
| Chris@305 | 156 | 
| Chris@1652 | 157     enum Application { | 
| Chris@1652 | 158         AppliesToThisEvent, | 
| Chris@1652 | 159         AppliesToPreviousEvent | 
| Chris@1652 | 160     }; | 
| Chris@1652 | 161     typedef std::pair<Application, Event> Relabelling; | 
| Chris@1652 | 162     typedef std::pair<Application, Event> Revaluing; | 
| Chris@1652 | 163 | 
| Chris@1652 | 164     /** | 
| Chris@1652 | 165      * Return a labelled event based on the given event, previous | 
| Chris@1652 | 166      * event if supplied, and internal labeller state. The returned | 
| Chris@1652 | 167      * event replaces either the event provided or the previous event, | 
| Chris@1652 | 168      * depending on the Application value in the returned pair. | 
| Chris@1652 | 169      */ | 
| Chris@1652 | 170     Relabelling | 
| Chris@1652 | 171     label(Event e, const Event *prev = nullptr) { | 
| Chris@1652 | 172 | 
| Chris@1652 | 173         QString label = e.getLabel(); | 
| Chris@1652 | 174 | 
| Chris@305 | 175         if (m_type == ValueNone) { | 
| Chris@1652 | 176             label = ""; | 
| Chris@305 | 177         } else if (m_type == ValueFromTwoLevelCounter) { | 
| Chris@1652 | 178             label = tr("%1.%2").arg(m_counter2).arg(m_counter); | 
| Chris@305 | 179             incrementCounter(); | 
| Chris@306 | 180         } else if (m_type == ValueFromFrameNumber) { | 
| Chris@306 | 181             // avoid going through floating-point value | 
| Chris@1652 | 182             label = tr("%1").arg(e.getFrame()); | 
| Chris@305 | 183         } else { | 
| Chris@1652 | 184             float value = getValueFor(e, prev); | 
| Chris@1652 | 185             label = QString("%1").arg(value); | 
| Chris@1652 | 186         } | 
| Chris@1652 | 187 | 
| Chris@1652 | 188         if (actingOnPrevEvent() && prev) { | 
| Chris@1652 | 189             return { AppliesToPreviousEvent, prev->withLabel(label) }; | 
| Chris@1652 | 190         } else { | 
| Chris@1652 | 191             return { AppliesToThisEvent, e.withLabel(label) }; | 
| Chris@305 | 192         } | 
| Chris@305 | 193     } | 
| Chris@1652 | 194 | 
| Chris@1652 | 195     /** | 
| Chris@1652 | 196      * Return an event with a value following the labelling scheme, | 
| Chris@1652 | 197      * based on the given event, previous event if supplied, and | 
| Chris@1652 | 198      * internal labeller state. The returned event replaces either the | 
| Chris@1652 | 199      * event provided or the previous event, depending on the | 
| Chris@1652 | 200      * Application value in the returned pair. | 
| Chris@1652 | 201      */ | 
| Chris@1652 | 202     Revaluing | 
| Chris@1652 | 203     revalue(Event e, const Event *prev = nullptr) { | 
| Chris@1652 | 204 | 
| Chris@1652 | 205         float value = e.getValue(); | 
| Chris@305 | 206 | 
| Chris@1652 | 207         if (m_type == ValueFromExistingNeighbour) { | 
| Chris@1652 | 208             if (!prev) { | 
| Chris@1652 | 209                 std::cerr << "ERROR: Labeller::setValue: Previous point required but not provided" << std::endl; | 
| Chris@1652 | 210             } else { | 
| Chris@1652 | 211                 return { AppliesToThisEvent, e.withValue(prev->getValue()) }; | 
| Chris@1652 | 212             } | 
| Chris@1652 | 213         } else { | 
| Chris@1652 | 214             value = getValueFor(e, prev); | 
| Chris@1652 | 215         } | 
| Chris@1652 | 216 | 
| Chris@1652 | 217         if (actingOnPrevEvent() && prev) { | 
| Chris@1652 | 218             return { AppliesToPreviousEvent, prev->withValue(value) }; | 
| Chris@1652 | 219         } else { | 
| Chris@1652 | 220             return { AppliesToThisEvent, e.withValue(value) }; | 
| Chris@1652 | 221         } | 
| Chris@1652 | 222     } | 
| Chris@1652 | 223 | 
| Chris@1291 | 224     /** | 
| Chris@1652 | 225      * Relabel all events in the given event vector that lie within | 
| Chris@1652 | 226      * the given multi-selection, according to the labelling | 
| Chris@1652 | 227      * properties of this labeller.  Return a command that has been | 
| Chris@1652 | 228      * executed but not yet added to the history. | 
| Chris@1291 | 229      */ | 
| Chris@1652 | 230     Command *labelAll(EventEditable *editable, | 
| Chris@1652 | 231                       MultiSelection *ms, | 
| Chris@1652 | 232                       const EventVector &allEvents) { | 
| Chris@305 | 233 | 
| Chris@1652 | 234         ChangeEventsCommand *command = new ChangeEventsCommand | 
| Chris@1652 | 235             (editable, tr("Label Points")); | 
| Chris@305 | 236 | 
| Chris@1652 | 237         Event prev; | 
| Chris@1652 | 238         bool havePrev = false; | 
| Chris@305 | 239 | 
| Chris@1652 | 240         for (auto p: allEvents) { | 
| Chris@305 | 241 | 
| Chris@305 | 242             if (ms) { | 
| Chris@1652 | 243                 Selection s(ms->getContainingSelection(p.getFrame(), false)); | 
| Chris@1652 | 244                 if (!s.contains(p.getFrame())) { | 
| Chris@1652 | 245                     prev = p; | 
| Chris@1652 | 246                     havePrev = true; | 
| Chris@1293 | 247                     continue; | 
| Chris@305 | 248                 } | 
| Chris@305 | 249             } | 
| Chris@305 | 250 | 
| Chris@1652 | 251             auto labelling = label(p, havePrev ? &prev : nullptr); | 
| Chris@1652 | 252 | 
| Chris@1652 | 253             if (labelling.first == AppliesToThisEvent) { | 
| Chris@1652 | 254                 command->remove(p); | 
| Chris@305 | 255             } else { | 
| Chris@1652 | 256                 command->remove(prev); | 
| Chris@305 | 257             } | 
| Chris@305 | 258 | 
| Chris@1652 | 259             command->add(labelling.second); | 
| Chris@1652 | 260 | 
| Chris@1652 | 261             prev = p; | 
| Chris@1652 | 262             havePrev = true; | 
| Chris@305 | 263         } | 
| Chris@305 | 264 | 
| Chris@1291 | 265         return command->finish(); | 
| Chris@1291 | 266     } | 
| Chris@1291 | 267 | 
| Chris@1291 | 268     /** | 
| Chris@1652 | 269      * For each event in the given event vector (except the last), if | 
| Chris@1652 | 270      * that event lies within the given multi-selection, add n-1 new | 
| Chris@1652 | 271      * events at equally spaced intervals between it and the following | 
| Chris@1652 | 272      * event.  Return a command that has been executed but not yet | 
| Chris@1652 | 273      * added to the history. | 
| Chris@1291 | 274      */ | 
| Chris@1652 | 275     Command *subdivide(EventEditable *editable, | 
| Chris@1652 | 276                        MultiSelection *ms, | 
| Chris@1652 | 277                        const EventVector &allEvents, | 
| Chris@1652 | 278                        int n) { | 
| Chris@1291 | 279 | 
| Chris@1652 | 280         ChangeEventsCommand *command = new ChangeEventsCommand | 
| Chris@1652 | 281             (editable, tr("Subdivide Points")); | 
| Chris@1652 | 282 | 
| Chris@1652 | 283         for (auto i = allEvents.begin(); i != allEvents.end(); ++i) { | 
| Chris@1291 | 284 | 
| Chris@1291 | 285             auto j = i; | 
| Chris@1291 | 286             // require a "next point" even if it's not in selection | 
| Chris@1652 | 287             if (++j == allEvents.end()) { | 
| Chris@1291 | 288                 break; | 
| Chris@1291 | 289             } | 
| Chris@1291 | 290 | 
| Chris@1291 | 291             if (ms) { | 
| Chris@1652 | 292                 Selection s(ms->getContainingSelection(i->getFrame(), false)); | 
| Chris@1652 | 293                 if (!s.contains(i->getFrame())) { | 
| Chris@1293 | 294                     continue; | 
| Chris@1291 | 295                 } | 
| Chris@1291 | 296             } | 
| Chris@1291 | 297 | 
| Chris@1652 | 298             Event p(*i); | 
| Chris@1652 | 299             Event nextP(*j); | 
| Chris@1291 | 300 | 
| Chris@1291 | 301             // n is the number of subdivisions, so we add n-1 new | 
| Chris@1291 | 302             // points equally spaced between p and nextP | 
| Chris@1291 | 303 | 
| Chris@1291 | 304             for (int m = 1; m < n; ++m) { | 
| Chris@1652 | 305                 sv_frame_t f = p.getFrame() + | 
| Chris@1652 | 306                     (m * (nextP.getFrame() - p.getFrame())) / n; | 
| Chris@1652 | 307                 Event newPoint = p | 
| Chris@1652 | 308                     .withFrame(f) | 
| Chris@1652 | 309                     .withLabel(tr("%1.%2").arg(p.getLabel()).arg(m+1)); | 
| Chris@1652 | 310                 command->add(newPoint); | 
| Chris@1291 | 311             } | 
| Chris@1291 | 312         } | 
| Chris@1291 | 313 | 
| Chris@1291 | 314         return command->finish(); | 
| Chris@305 | 315     } | 
| Chris@305 | 316 | 
| Chris@1292 | 317     /** | 
| Chris@1652 | 318      * The opposite of subdivide. Given an event vector, a | 
| Chris@1652 | 319      * multi-selection, and a number n, remove all but every nth event | 
| Chris@1652 | 320      * from the vector within the extents of the multi-selection. | 
| Chris@1292 | 321      * Return a command that has been executed but not yet added to | 
| Chris@1292 | 322      * the history. | 
| Chris@1292 | 323      */ | 
| Chris@1652 | 324     Command *winnow(EventEditable *editable, | 
| Chris@1652 | 325                     MultiSelection *ms, | 
| Chris@1652 | 326                     const EventVector &allEvents, | 
| Chris@1652 | 327                     int n) { | 
| Chris@1652 | 328 | 
| Chris@1652 | 329         ChangeEventsCommand *command = new ChangeEventsCommand | 
| Chris@1652 | 330             (editable, tr("Winnow Points")); | 
| Chris@1292 | 331 | 
| Chris@1292 | 332         int counter = 0; | 
| Chris@1292 | 333 | 
| Chris@1652 | 334         for (auto p: allEvents) { | 
| Chris@1292 | 335 | 
| Chris@1292 | 336             if (ms) { | 
| Chris@1652 | 337                 Selection s(ms->getContainingSelection(p.getFrame(), false)); | 
| Chris@1652 | 338                 if (!s.contains(p.getFrame())) { | 
| Chris@1293 | 339                     counter = 0; | 
| Chris@1293 | 340                     continue; | 
| Chris@1292 | 341                 } | 
| Chris@1292 | 342             } | 
| Chris@1292 | 343 | 
| Chris@1292 | 344             ++counter; | 
| Chris@1292 | 345 | 
| Chris@1292 | 346             if (counter == n+1) counter = 1; | 
| Chris@1292 | 347             if (counter == 1) { | 
| Chris@1292 | 348                 // this is an Nth instant, don't remove it | 
| Chris@1292 | 349                 continue; | 
| Chris@1292 | 350             } | 
| Chris@1292 | 351 | 
| Chris@1652 | 352             command->remove(p); | 
| Chris@1292 | 353         } | 
| Chris@1292 | 354 | 
| Chris@1292 | 355         return command->finish(); | 
| Chris@1292 | 356     } | 
| Chris@1292 | 357 | 
| Chris@355 | 358     bool requiresPrevPoint() const { | 
| Chris@355 | 359         return (m_type == ValueFromDurationFromPrevious || | 
| Chris@355 | 360                 m_type == ValueFromDurationToNext || | 
| Chris@355 | 361                 m_type == ValueFromTempoFromPrevious || | 
| Chris@355 | 362                 m_type == ValueFromDurationToNext); | 
| Chris@355 | 363     } | 
| Chris@355 | 364 | 
| Chris@1652 | 365     bool actingOnPrevEvent() const { | 
| Chris@355 | 366         return (m_type == ValueFromDurationToNext || | 
| Chris@355 | 367                 m_type == ValueFromTempoToNext); | 
| Chris@305 | 368     } | 
| Chris@305 | 369 | 
| Chris@305 | 370 protected: | 
| Chris@1652 | 371     float getValueFor(Event p, const Event *prev) { | 
| Chris@1652 | 372 | 
| Chris@305 | 373         float value = 0.f; | 
| Chris@305 | 374 | 
| Chris@305 | 375         switch (m_type) { | 
| Chris@305 | 376 | 
| Chris@305 | 377         case ValueNone: | 
| Chris@305 | 378             value = 0; | 
| Chris@305 | 379             break; | 
| Chris@305 | 380 | 
| Chris@305 | 381         case ValueFromSimpleCounter: | 
| Chris@305 | 382         case ValueFromCyclicalCounter: | 
| Chris@1046 | 383             value = float(m_counter); | 
| Chris@305 | 384             incrementCounter(); | 
| Chris@305 | 385             break; | 
| Chris@305 | 386 | 
| Chris@305 | 387         case ValueFromTwoLevelCounter: | 
| Chris@1046 | 388             value = float(m_counter2 + double(m_counter) / double(m_dp)); | 
| Chris@305 | 389             incrementCounter(); | 
| Chris@305 | 390             break; | 
| Chris@305 | 391 | 
| Chris@305 | 392         case ValueFromFrameNumber: | 
| Chris@1652 | 393             value = float(p.getFrame()); | 
| Chris@305 | 394             break; | 
| Chris@305 | 395 | 
| Chris@305 | 396         case ValueFromRealTime: | 
| Chris@1046 | 397             if (m_rate == 0.0) { | 
| Chris@305 | 398                 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl; | 
| Chris@305 | 399             } else { | 
| Chris@1652 | 400                 value = float(double(p.getFrame()) / m_rate); | 
| Chris@305 | 401             } | 
| Chris@305 | 402             break; | 
| Chris@305 | 403 | 
| Chris@355 | 404         case ValueFromDurationToNext: | 
| Chris@355 | 405         case ValueFromTempoToNext: | 
| Chris@355 | 406         case ValueFromDurationFromPrevious: | 
| Chris@355 | 407         case ValueFromTempoFromPrevious: | 
| Chris@1046 | 408             if (m_rate == 0.0) { | 
| Chris@305 | 409                 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl; | 
| Chris@1652 | 410             } else if (!prev) { | 
| Chris@305 | 411                 std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl; | 
| Chris@305 | 412             } else { | 
| Chris@1652 | 413                 sv_frame_t f0 = prev->getFrame(), f1 = p.getFrame(); | 
| Chris@355 | 414                 if (m_type == ValueFromDurationToNext || | 
| Chris@355 | 415                     m_type == ValueFromDurationFromPrevious) { | 
| Chris@1046 | 416                     value = float(double(f1 - f0) / m_rate); | 
| Chris@305 | 417                 } else { | 
| Chris@305 | 418                     if (f1 > f0) { | 
| Chris@1046 | 419                         value = float((60.0 * m_rate) / double(f1 - f0)); | 
| Chris@305 | 420                     } | 
| Chris@305 | 421                 } | 
| Chris@305 | 422             } | 
| Chris@305 | 423             break; | 
| Chris@305 | 424 | 
| Chris@305 | 425         case ValueFromExistingNeighbour: | 
| Chris@305 | 426             // need to deal with this in the calling function, as this | 
| Chris@305 | 427             // function must handle points that don't have values to | 
| Chris@305 | 428             // read from | 
| Chris@305 | 429             break; | 
| Chris@305 | 430 | 
| Chris@305 | 431         case ValueFromLabel: | 
| Chris@1652 | 432             if (p.getLabel() != "") { | 
| Chris@305 | 433                 // more forgiving than QString::toFloat() | 
| Chris@1652 | 434                 value = float(atof(p.getLabel().toLocal8Bit())); | 
| Chris@305 | 435             } else { | 
| Chris@305 | 436                 value = 0.f; | 
| Chris@305 | 437             } | 
| Chris@305 | 438             break; | 
| Chris@305 | 439         } | 
| Chris@305 | 440 | 
| Chris@305 | 441         return value; | 
| Chris@305 | 442     } | 
| Chris@305 | 443 | 
| Chris@305 | 444     ValueType m_type; | 
| Chris@305 | 445     int m_counter; | 
| Chris@305 | 446     int m_counter2; | 
| Chris@305 | 447     int m_cycle; | 
| Chris@305 | 448     int m_dp; | 
| Chris@1046 | 449     sv_samplerate_t m_rate; | 
| Chris@305 | 450 }; | 
| Chris@305 | 451 | 
| Chris@305 | 452 #endif |