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@1742
|
228 * executed but not yet added to the history. The id must be that
|
Chris@1742
|
229 * of a type that can be retrieved from the AnyById store and
|
Chris@1742
|
230 * dynamic_cast to EventEditable.
|
Chris@1291
|
231 */
|
Chris@1742
|
232 Command *labelAll(int editableId,
|
Chris@1652
|
233 MultiSelection *ms,
|
Chris@1652
|
234 const EventVector &allEvents) {
|
Chris@305
|
235
|
Chris@1742
|
236 auto command = new ChangeEventsCommand
|
Chris@1742
|
237 (editableId, tr("Label Points"));
|
Chris@305
|
238
|
Chris@1652
|
239 Event prev;
|
Chris@1652
|
240 bool havePrev = false;
|
Chris@305
|
241
|
Chris@1652
|
242 for (auto p: allEvents) {
|
Chris@305
|
243
|
Chris@305
|
244 if (ms) {
|
Chris@1652
|
245 Selection s(ms->getContainingSelection(p.getFrame(), false));
|
Chris@1652
|
246 if (!s.contains(p.getFrame())) {
|
Chris@1652
|
247 prev = p;
|
Chris@1652
|
248 havePrev = true;
|
Chris@1293
|
249 continue;
|
Chris@305
|
250 }
|
Chris@305
|
251 }
|
Chris@305
|
252
|
Chris@1652
|
253 auto labelling = label(p, havePrev ? &prev : nullptr);
|
Chris@1652
|
254
|
Chris@1652
|
255 if (labelling.first == AppliesToThisEvent) {
|
Chris@1652
|
256 command->remove(p);
|
Chris@305
|
257 } else {
|
Chris@1652
|
258 command->remove(prev);
|
Chris@305
|
259 }
|
Chris@305
|
260
|
Chris@1652
|
261 command->add(labelling.second);
|
Chris@1652
|
262
|
Chris@1652
|
263 prev = p;
|
Chris@1652
|
264 havePrev = true;
|
Chris@305
|
265 }
|
Chris@305
|
266
|
Chris@1291
|
267 return command->finish();
|
Chris@1291
|
268 }
|
Chris@1291
|
269
|
Chris@1291
|
270 /**
|
Chris@1652
|
271 * For each event in the given event vector (except the last), if
|
Chris@1652
|
272 * that event lies within the given multi-selection, add n-1 new
|
Chris@1652
|
273 * events at equally spaced intervals between it and the following
|
Chris@1652
|
274 * event. Return a command that has been executed but not yet
|
Chris@1742
|
275 * added to the history. The id must be that of a type that can
|
Chris@1742
|
276 * be retrieved from the AnyById store and dynamic_cast to
|
Chris@1742
|
277 * EventEditable.
|
Chris@1291
|
278 */
|
Chris@1742
|
279 Command *subdivide(int editableId,
|
Chris@1652
|
280 MultiSelection *ms,
|
Chris@1652
|
281 const EventVector &allEvents,
|
Chris@1652
|
282 int n) {
|
Chris@1291
|
283
|
Chris@1742
|
284 auto command = new ChangeEventsCommand
|
Chris@1742
|
285 (editableId, tr("Subdivide Points"));
|
Chris@1652
|
286
|
Chris@1652
|
287 for (auto i = allEvents.begin(); i != allEvents.end(); ++i) {
|
Chris@1291
|
288
|
Chris@1291
|
289 auto j = i;
|
Chris@1291
|
290 // require a "next point" even if it's not in selection
|
Chris@1652
|
291 if (++j == allEvents.end()) {
|
Chris@1291
|
292 break;
|
Chris@1291
|
293 }
|
Chris@1291
|
294
|
Chris@1291
|
295 if (ms) {
|
Chris@1652
|
296 Selection s(ms->getContainingSelection(i->getFrame(), false));
|
Chris@1652
|
297 if (!s.contains(i->getFrame())) {
|
Chris@1293
|
298 continue;
|
Chris@1291
|
299 }
|
Chris@1291
|
300 }
|
Chris@1291
|
301
|
Chris@1652
|
302 Event p(*i);
|
Chris@1652
|
303 Event nextP(*j);
|
Chris@1291
|
304
|
Chris@1291
|
305 // n is the number of subdivisions, so we add n-1 new
|
Chris@1291
|
306 // points equally spaced between p and nextP
|
Chris@1291
|
307
|
Chris@1291
|
308 for (int m = 1; m < n; ++m) {
|
Chris@1652
|
309 sv_frame_t f = p.getFrame() +
|
Chris@1652
|
310 (m * (nextP.getFrame() - p.getFrame())) / n;
|
Chris@1652
|
311 Event newPoint = p
|
Chris@1652
|
312 .withFrame(f)
|
Chris@1652
|
313 .withLabel(tr("%1.%2").arg(p.getLabel()).arg(m+1));
|
Chris@1652
|
314 command->add(newPoint);
|
Chris@1291
|
315 }
|
Chris@1291
|
316 }
|
Chris@1291
|
317
|
Chris@1291
|
318 return command->finish();
|
Chris@305
|
319 }
|
Chris@305
|
320
|
Chris@1292
|
321 /**
|
Chris@1652
|
322 * The opposite of subdivide. Given an event vector, a
|
Chris@1652
|
323 * multi-selection, and a number n, remove all but every nth event
|
Chris@1652
|
324 * from the vector within the extents of the multi-selection.
|
Chris@1292
|
325 * Return a command that has been executed but not yet added to
|
Chris@1742
|
326 * the history. The id must be that of a type that can be
|
Chris@1742
|
327 * retrieved from the AnyById store and dynamic_cast to
|
Chris@1742
|
328 * EventEditable.
|
Chris@1292
|
329 */
|
Chris@1742
|
330 Command *winnow(int editableId,
|
Chris@1652
|
331 MultiSelection *ms,
|
Chris@1652
|
332 const EventVector &allEvents,
|
Chris@1652
|
333 int n) {
|
Chris@1652
|
334
|
Chris@1742
|
335 auto command = new ChangeEventsCommand
|
Chris@1742
|
336 (editableId, tr("Winnow Points"));
|
Chris@1292
|
337
|
Chris@1292
|
338 int counter = 0;
|
Chris@1292
|
339
|
Chris@1652
|
340 for (auto p: allEvents) {
|
Chris@1292
|
341
|
Chris@1292
|
342 if (ms) {
|
Chris@1652
|
343 Selection s(ms->getContainingSelection(p.getFrame(), false));
|
Chris@1652
|
344 if (!s.contains(p.getFrame())) {
|
Chris@1293
|
345 counter = 0;
|
Chris@1293
|
346 continue;
|
Chris@1292
|
347 }
|
Chris@1292
|
348 }
|
Chris@1292
|
349
|
Chris@1292
|
350 ++counter;
|
Chris@1292
|
351
|
Chris@1292
|
352 if (counter == n+1) counter = 1;
|
Chris@1292
|
353 if (counter == 1) {
|
Chris@1292
|
354 // this is an Nth instant, don't remove it
|
Chris@1292
|
355 continue;
|
Chris@1292
|
356 }
|
Chris@1292
|
357
|
Chris@1652
|
358 command->remove(p);
|
Chris@1292
|
359 }
|
Chris@1292
|
360
|
Chris@1292
|
361 return command->finish();
|
Chris@1292
|
362 }
|
Chris@1292
|
363
|
Chris@355
|
364 bool requiresPrevPoint() const {
|
Chris@355
|
365 return (m_type == ValueFromDurationFromPrevious ||
|
Chris@355
|
366 m_type == ValueFromDurationToNext ||
|
Chris@355
|
367 m_type == ValueFromTempoFromPrevious ||
|
Chris@355
|
368 m_type == ValueFromDurationToNext);
|
Chris@355
|
369 }
|
Chris@355
|
370
|
Chris@1652
|
371 bool actingOnPrevEvent() const {
|
Chris@355
|
372 return (m_type == ValueFromDurationToNext ||
|
Chris@355
|
373 m_type == ValueFromTempoToNext);
|
Chris@305
|
374 }
|
Chris@305
|
375
|
Chris@305
|
376 protected:
|
Chris@1652
|
377 float getValueFor(Event p, const Event *prev) {
|
Chris@1652
|
378
|
Chris@305
|
379 float value = 0.f;
|
Chris@305
|
380
|
Chris@305
|
381 switch (m_type) {
|
Chris@305
|
382
|
Chris@305
|
383 case ValueNone:
|
Chris@305
|
384 value = 0;
|
Chris@305
|
385 break;
|
Chris@305
|
386
|
Chris@305
|
387 case ValueFromSimpleCounter:
|
Chris@305
|
388 case ValueFromCyclicalCounter:
|
Chris@1046
|
389 value = float(m_counter);
|
Chris@305
|
390 incrementCounter();
|
Chris@305
|
391 break;
|
Chris@305
|
392
|
Chris@305
|
393 case ValueFromTwoLevelCounter:
|
Chris@1046
|
394 value = float(m_counter2 + double(m_counter) / double(m_dp));
|
Chris@305
|
395 incrementCounter();
|
Chris@305
|
396 break;
|
Chris@305
|
397
|
Chris@305
|
398 case ValueFromFrameNumber:
|
Chris@1652
|
399 value = float(p.getFrame());
|
Chris@305
|
400 break;
|
Chris@305
|
401
|
Chris@305
|
402 case ValueFromRealTime:
|
Chris@1046
|
403 if (m_rate == 0.0) {
|
Chris@305
|
404 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
|
Chris@305
|
405 } else {
|
Chris@1652
|
406 value = float(double(p.getFrame()) / m_rate);
|
Chris@305
|
407 }
|
Chris@305
|
408 break;
|
Chris@305
|
409
|
Chris@355
|
410 case ValueFromDurationToNext:
|
Chris@355
|
411 case ValueFromTempoToNext:
|
Chris@355
|
412 case ValueFromDurationFromPrevious:
|
Chris@355
|
413 case ValueFromTempoFromPrevious:
|
Chris@1046
|
414 if (m_rate == 0.0) {
|
Chris@305
|
415 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
|
Chris@1652
|
416 } else if (!prev) {
|
Chris@305
|
417 std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl;
|
Chris@305
|
418 } else {
|
Chris@1652
|
419 sv_frame_t f0 = prev->getFrame(), f1 = p.getFrame();
|
Chris@355
|
420 if (m_type == ValueFromDurationToNext ||
|
Chris@355
|
421 m_type == ValueFromDurationFromPrevious) {
|
Chris@1046
|
422 value = float(double(f1 - f0) / m_rate);
|
Chris@305
|
423 } else {
|
Chris@305
|
424 if (f1 > f0) {
|
Chris@1046
|
425 value = float((60.0 * m_rate) / double(f1 - f0));
|
Chris@305
|
426 }
|
Chris@305
|
427 }
|
Chris@305
|
428 }
|
Chris@305
|
429 break;
|
Chris@305
|
430
|
Chris@305
|
431 case ValueFromExistingNeighbour:
|
Chris@305
|
432 // need to deal with this in the calling function, as this
|
Chris@305
|
433 // function must handle points that don't have values to
|
Chris@305
|
434 // read from
|
Chris@305
|
435 break;
|
Chris@305
|
436
|
Chris@305
|
437 case ValueFromLabel:
|
Chris@1652
|
438 if (p.getLabel() != "") {
|
Chris@305
|
439 // more forgiving than QString::toFloat()
|
Chris@1652
|
440 value = float(atof(p.getLabel().toLocal8Bit()));
|
Chris@305
|
441 } else {
|
Chris@305
|
442 value = 0.f;
|
Chris@305
|
443 }
|
Chris@305
|
444 break;
|
Chris@305
|
445 }
|
Chris@305
|
446
|
Chris@305
|
447 return value;
|
Chris@305
|
448 }
|
Chris@305
|
449
|
Chris@305
|
450 ValueType m_type;
|
Chris@305
|
451 int m_counter;
|
Chris@305
|
452 int m_counter2;
|
Chris@305
|
453 int m_cycle;
|
Chris@305
|
454 int m_dp;
|
Chris@1046
|
455 sv_samplerate_t m_rate;
|
Chris@305
|
456 };
|
Chris@305
|
457
|
Chris@305
|
458 #endif
|