root/branches/transport_redesign_2/test/t_TransportPosition.cpp @ 1151

Revision 1151, 26.3 KB (checked in by gabriel@…, 4 years ago)

Add tests and some fixes to SimpleTransportMaster?.

* Modified behavior of normalize() to *NOT* adjust

the frame. After further thought, this was the
wrong thing to do.

* Change SimpleTransportMaster::processed_frames() to

calculate more stabily... but this is still some
scaffolding code to get things going.

* add a TransportPosition::normalize(target_frame)

method to do a normalization, but also make sure that
we end up on the specified beat.

Status: Hydrogen will scroll when you press play, but still no audio
output. Also, the GUI seems to be off-by-one when it comes to bars
and beats. Fails on a vector bounds check in the GUI thread.

Also, The new tests have some tests that fail even though things are
working as expected. Need to go in and fix up those tests.

Line 
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 *
5 * http://www.hydrogen-music.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY, without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 *
21 */
22
23#include <hydrogen/TransportPosition.h>
24#include <cmath>
25
26#define THIS_NAMESPACE t_TransportPosition
27#include "test_macros.h"
28
29using namespace H2Core;
30
31namespace THIS_NAMESPACE
32{
33    struct Fixture
34    {
35        TransportPosition p;  // This is the "normal" one.
36        TransportPosition x;  // This one has an odd setup.
37
38        Fixture() : p() {
39            p.frame_rate = 48000;
40            p.beats_per_bar = 4;
41            p.beat_type = 4;
42            p.ticks_per_beat = 192;
43            p.beats_per_minute = 120.0;
44            // MEMO: p.frames_per_tick() == 125.0
45
46            // x init.
47            x.state = TransportPosition::ROLLING;
48            x.new_position = true;
49            x.frame = 8273901;
50            x.frame_rate = 196123;
51            x.bar = 349;
52            x.beat = 5;
53            x.tick = 18;
54            x.bbt_offset = 115;
55            x.bar_start_tick = 349;
56            x.beats_per_bar = 7;
57            x.beat_type = 8;
58            x.ticks_per_beat = 99;
59            x.beats_per_minute = 543.2;
60            // MEMO: x.frames_per_tick() == 218.81889588075154
61        }
62
63        ~Fixture() {}
64    };
65
66/**********************************************************************
67 * NOTE ON COMPARING FRAMES AND FRAME ADJUSTMENT
68 * =============================================
69 *
70 * Suppose you are summing x(n+1) = x(n) + e where x(i) and e are real
71 * numbers, and x(0) is known.
72 *
73 * If you calculate like this: x(n+1) = round(x(n) + e) you will
74 * consistantly drift away from the actual answer.  However, this is
75 * common to do in frame adjustment calculations where the tempo does
76 * not align with the frame rate.  The maximum error in this situation
77 * is 0.5 * n.
78 *
79 * To mitigate this error (without keeping track of decimal
80 * bbt_offsets), TransportPosition uses dithering.  A random number
81 * between [-.5, .5) is added to the number before rounding.  So:
82 *
83 * x(n+1) = round( x(n) + e + dither() )
84 *
85 * Theoretically, all of the dither()'s should sum up to zero and you
86 * would (usually) have no error over large 'n'.  However, it's not
87 * exactly the case.  In order to have tests that always pass, it
88 * would be nice to know what the tolerance is for error when
89 * comparing expected frame values to calculated frame values.
90 *
91 * To find a boundary for the error introduced by dithering (as
92 * opposed to the error introduced by rounding), I ran about 5000 sets
93 * with e between 0 and 11, and max n = 100000... taking the maximum
94 * errors for various slots.  (E.g. max error for n = [90, 100])
95 *
96 * I hand-fit [1] a couple of equations that always bounded the data.
97 * They are as follows:
98 *
99 *     max_err = 2.1 * Nops^.50   { Nops in [0, 400] }
100 *     max_err = 4.3 * Nops^.435  { Nops in (400, 100000] }
101 *
102 * Tabulated, here's what this function evaluates to:
103 *
104 *     MAXIMUM FRAME DRIFT (+/- frames)
105 *     ================================
106 *      Nops  | dither | no dither
107 *     -------+--------+-----------
108 *          1 |    2.1 |        1
109 *         10 |    6.6 |        5
110 *        100 |   21.0 |       50
111 *       1000 |   86.8 |      500
112 *      10000 |  236.3 |     5000
113 *     100000 | 1751.7 |    50000
114 *
115 * Note that these are WORST CASE.  It is typically much better.
116 * (E.g. over 100 ops I usually get 4.5 frames of drift). [2]
117 *
118 * [1] "Chi-by-eye."
119 *
120 * [2] As it happens, the fractional part of 'e' is the biggest factor
121 *     the determines how much error you will get.  If e is close to a
122 *     whole number, you will get very little error by dithering.
123 *     When e is midway between two numbers, you are more likely to
124 *     get larger errors.
125 *
126 **********************************************************************
127 */
128
129    static inline bool check_frame_drift(double TrueVal,
130                                         uint32_t Frame,
131                                         size_t Nops,
132                                         int Line)
133    {
134        double max_drift = 0.0;
135        bool rv;
136
137        double ActDrift = TrueVal - double(Frame);
138        if( Nops <= 1 ) {
139            max_drift = 1.0;
140        } else if( Nops <= 400 ) {
141            max_drift = 2.1 * sqrt(double(Nops));
142        } else {
143            max_drift = 4.3 * pow(double(Nops), .435);
144        }
145
146        rv = (fabs(ActDrift) <= max_drift);
147
148        if( ! rv ) {
149            BOOST_MESSAGE("In " << __FILE__ << "(" << Line << ") "
150                          << "Too much drift: True(" << TrueVal << ") "
151                          << "- Frame(" << Frame << ") = " << ActDrift
152                          << " [Limit is +/- " << max_drift
153                          << " for " << Nops << " ops]");
154        }
155
156        return rv;
157    }
158
159#define DRIFT(t, f, n) check_frame_drift(t, f, n, __LINE__)
160
161} // namespace THIS_NAMESPACE
162
163using namespace THIS_NAMESPACE;
164
165TEST_BEGIN( Fixture );
166
167TEST_CASE( 000_defaults )
168{
169    // Test the defaults
170    CK( p.state == TransportPosition::STOPPED );
171    CK( p.new_position == true );
172    CK( p.frame == 0 );
173    CK( p.bar == 1 );
174    CK( p.beat == 1 );
175    CK( p.tick == 0 );
176    CK( p.bbt_offset == 0 );
177    CK( p.bar_start_tick == 0 );
178}
179
180TEST_CASE( 010_copy_constructor )
181{
182    TransportPosition y, z;
183
184    CK( y.state == TransportPosition::STOPPED );
185    CK( y.new_position == true );
186    CK( y.frame == 0 );
187    CK( y.bar == 1 );
188    CK( y.beat == 1 );
189    CK( y.tick == 0 );
190    CK( y.bbt_offset == 0 );
191    CK( y.bar_start_tick == 0 );
192
193    CK( z.state == TransportPosition::STOPPED );
194    CK( z.new_position == true );
195    CK( z.frame == 0 );
196    CK( z.bar == 1 );
197    CK( z.beat == 1 );
198    CK( z.tick == 0 );
199    CK( z.bbt_offset == 0 );
200    CK( z.bar_start_tick == 0 );
201
202    p.frame = 12345;
203    p.bar = 21;
204    p.beat = 39;
205    p.tick = -1;
206    y = p;
207    CK( 12345 == y.frame );
208    CK( 21 == y.bar );
209    CK( -1 == y.tick );
210    p.tick = 5;
211    y.tick = 123;
212    CK( 5 == p.tick );
213    CK( 123 == y.tick );
214
215    z = x;
216    CK( TransportPosition::ROLLING == z.state );
217    CK( true == z.new_position );
218    CK( 8273901 == z.frame );
219    CK( 196123 == z.frame_rate );
220    CK( 349 == z.bar );
221    CK( 5 == z.beat );
222    CK( 18 == z.tick );
223    CK( 115 == z.bbt_offset );
224    CK( 349 == z.bar_start_tick );
225    CK( 7 == z.beats_per_bar );
226    CK( 8 == z.beat_type );
227    CK( 99 == z.ticks_per_beat );
228    CK( 543.2 == z.beats_per_minute );
229}
230
231TEST_CASE( 020_frames_per_tick )
232{
233    CK( p.frames_per_tick() == 125.0 );
234
235    p.frame_rate = 123456;
236    p.ticks_per_beat = 48;
237    p.beats_per_minute = 33.12;
238    CK( round(p.frames_per_tick()) == 4659.0 );
239
240    CK( round(x.frames_per_tick()*100.0) == 21882.0 );
241}
242
243TEST_CASE( 030_tick_in_bar )
244{
245    CK(p.tick_in_bar() == 0);
246    p.tick = 191;
247    CK(p.tick_in_bar() == 191);
248    p.beat = 2;
249    CK(p.tick_in_bar() == 383);
250    p.bar = 9;
251    CK(p.tick_in_bar() == 383);
252
253    CK(x.tick_in_bar() == 414);
254}
255
256TEST_CASE( 040_normalize )
257{
258    TransportPosition a;
259    double frame;
260
261    // NOTE: normalize() only changes the frame position if it adjusts
262    // bbt_offset.
263
264    a = p;
265    a.tick += 192 * 6 + 101;
266    a.normalize();
267    CK( 2 == a.bar );
268    CK( 3 == a.beat );
269    CK( 101 == a.tick );
270    CK( 192 * 4 == a.bar_start_tick );
271    CK( DRIFT(0, a.frame, 1) );
272
273    a.beat -= 4;
274    a.normalize();
275    CK( 1 == a.bar );
276    CK( 3 == a.beat );
277    CK( 101 == a.tick );
278    CK( 0 == a.bar_start_tick );
279    CK( DRIFT(0, a.frame, 2) );
280
281    a.bar += 5;
282    a.normalize();
283    CK( 6 == a.bar );
284    CK( 3 == a.beat );
285    CK( 101 == a.tick );
286    CK( 0 == a.bar_start_tick ); // If you manually change bar, you also have
287                                 // to manually adjust bar_start_tick
288    CK( DRIFT(0, a.frame, 3) );
289
290    a = p;
291    frame = a.frame;
292    a.bbt_offset = 6 * 192 * 125 + 50;
293    //frame += a.bbt_offset - 50;
294    a.normalize();
295    CK( 2 == a.bar );
296    CK( 3 == a.beat );
297    CK( 0 == a.tick );
298    CK( DRIFT(50.0, a.bbt_offset, 1) );
299    CK( 192 * 4 == a.bar_start_tick );
300    CK( DRIFT(frame, a.frame, 1) );
301
302    a = x;
303    frame = a.frame;
304    a.tick -= 99 * 12 + 65;
305    a.normalize();
306    CK( 347 == a.bar );
307    CK( 6 == a.beat );
308    CK( 52 == a.tick );
309    CK( 115 == a.bbt_offset );
310    CK( 0 == a.bar_start_tick );
311    CK( DRIFT(frame, a.frame, 1) );
312
313    a = x;
314    frame = a.frame;
315    a.beat += 13;
316    a.normalize();
317    CK( 351 == a.bar );
318    CK( 4 == a.beat );
319    CK( 18 == a.tick );
320    CK( 115 == a.bbt_offset );
321    CK( 349 + 2 * 7 * 99 == a.bar_start_tick );
322    CK( DRIFT(frame, a.frame, 1) );
323
324    a = x;
325    a.bar_start_tick = 70000;
326    a.bar -= 11;
327    a.normalize();  // Does not change bar start tick
328    CK( 338 == a.bar );
329    CK( 5 == a.beat );
330    CK( 18 == a.tick );
331    CK( 115 == a.bbt_offset );
332    CK( 70000 == a.bar_start_tick );
333
334    a = x;                // 349:5.18.115 (70000)  349.59816096502567
335    a.bar_start_tick = 70000;
336    frame = a.frame;
337    a.bar += 3;           // 352:5.18.115 (70000)  352.59816096502567
338    a.beat -= 29;         // 348:4.18.115 (67228)  348.45530382216856
339    a.tick += 999;        // 349:7.27.115 (67921)  349.89686226372697
340    a.bbt_offset += 3114; // 349:7.41.165 (67921)  349.91739754005869
341    // frame += 3114.0 - 165.0 + 115.0;
342    a.normalize();
343    CK( 349 == a.bar );
344    CK( 7 == a.beat );
345    CK( 41 == a.tick );
346    CK( 165 == a.bbt_offset );
347    CK( 67921 == a.bar_start_tick );
348    CK( DRIFT(frame, a.frame, 1) );   
349}
350
351TEST_CASE( 050_increment )
352{
353    double frames_per_tick = double(p.frame_rate) * (60.0/p.beats_per_minute) / p.ticks_per_beat;
354    int k;
355
356    CK( p.frame == 0 );
357    double frame = 0.0;
358    for( k=1 ; (unsigned)k<p.ticks_per_beat ; ++k ) {
359        ++p;
360        frame += frames_per_tick;
361        CK( 1 == p.bar );
362        CK( 1 == p.beat );         
363        CK( k == p.tick );
364        CK( DRIFT(frame, p.frame, 1) );
365    }
366
367    CK( p.tick == 191 );
368    ++p;
369    frame += frames_per_tick;
370    CK( 1 == p.bar );
371    CK( 2 == p.beat );
372    CK( 0 == p.tick );
373    CK( DRIFT(frame, p.frame, 2) );
374    ++p;
375    frame += frames_per_tick;
376    CK( 1 == p.bar );
377    CK( 2 == p.beat );
378    CK( 1 == p.tick );
379    CK( DRIFT(frame, p.frame, 3) );
380    p.bar = 99;
381    p.beat = 3;
382    ++p;
383    frame += frames_per_tick;
384    CK( 99 == p.bar );
385    CK( 3 == p.beat );
386    CK( 2 == p.tick );
387    CK( DRIFT(frame, p.frame, 4) );
388
389    // Tests with 'x'
390    frame = x.frame;
391    frames_per_tick = x.frames_per_tick();
392    for( k=x.tick+1 ; (unsigned)k < x.ticks_per_beat ; ++k ) {
393        ++x;
394        frame += frames_per_tick;
395        CK( 349 == x.bar );
396        CK( 5 == x.beat );
397        CK( k == x.tick );
398        CK( 115 == x.bbt_offset );
399        CK( DRIFT( frame, x.frame, k ) );
400    }
401    CK( x.tick == 98 );
402    ++x;
403    frame += frames_per_tick;
404    CK( 349 == x.bar );
405    CK( 6 == x.beat );
406    CK( 0 == x.tick );
407    CK( 115 == x.bbt_offset );
408    CK( DRIFT( frame, x.frame, k+1 ) );
409
410    x.beat = 7;
411    x.tick = 98;
412    ++x;
413    frame += frames_per_tick;
414    CK( 350 == x.bar );
415    CK( 1 == x.beat );
416    CK( 0 == x.tick );
417    CK( 115 == x.bbt_offset );
418    CK( DRIFT( frame, x.frame, k+2 ) );
419    BOOST_MESSAGE( "++ drift = " << (frame - x.frame) );
420}
421
422TEST_CASE( 060_decrement )
423{
424    --p;
425    CK( 0 == p.frame );
426    CK( 1 == p.bar );
427    CK( 1 == p.beat );
428    CK( 0 == p.tick );
429    CK( 0 == p.bbt_offset );
430    CK( 0 == p.bar_start_tick );
431    ++p;
432    --p;
433    CK( 0 == p.frame );
434    CK( 1 == p.bar );
435    CK( 1 == p.beat );
436    CK( 0 == p.tick );
437    CK( 0 == p.bbt_offset );
438    CK( 0 == p.bar_start_tick );
439
440    p.bar = 5;
441    p.beat = 2;
442    p.tick = 1;
443    --p; --p;
444    CK( 0 == p.frame );
445    CK( 5 == p.bar );
446    CK( 1 == p.beat );
447    CK( 191 == p.tick );
448
449    double frame = 0.0;
450    double fpt = p.frames_per_tick();
451    p.frame = 1000.0;
452    p.bar = 5;
453    p.beat = 2;
454    p.tick = 1;
455    frame = p.frame;
456    --p; --p;
457    frame -= 2.0 * fpt;
458    CK( frame >= 0.0 );
459    CK( DRIFT(frame, p.frame, 2) );
460    CK( 5 == p.bar );
461    CK( 1 == p.beat );
462    CK( 191 == p.tick );
463
464    frame = 100000.0;
465    p.frame = frame;
466    p.bar = 100;
467    p.beat = 1;
468    p.tick = 0;
469    int k = p.ticks_per_beat * p.beats_per_bar;
470    frame -= fpt * k;
471    while( k > 0 ) {
472        --k; --p;
473    }
474    double drift = fabs( frame - double(p.frame) );
475    BOOST_MESSAGE( "-- drift @ 768 = " << drift );
476    CK( fabs(drift) < 10.0 );
477    CK( p.bar == 99 );
478    CK( p.beat == 1 );
479    CK( p.tick == 0 );
480
481    // Using the 'x' object
482    fpt = x.frames_per_tick();
483    frame = x.frame;
484    --x;
485    frame -= fpt;
486    CK( fabs(frame - x.frame) <= 1.0 );
487
488    for( k=2374 ; k > 0 ; --k ) --x;
489    frame -= fpt * 2374.0;
490    drift = frame - double(x.frame);
491    BOOST_MESSAGE( "-- drift @ 2375 = " << drift );
492    CK( fabs(drift) < 50.0 );
493    CK( 346 == x.bar );
494    CK( 2 == x.beat );
495    CK( 19 == x.tick );
496       
497}
498
499TEST_CASE( 070_floor )
500{
501    p.floor(TransportPosition::TICK);
502    CK( 1 == p.bar );
503    CK( 1 == p.beat );
504    CK( 0 == p.tick );
505    CK( 0 == p.frame );
506    p.floor(TransportPosition::BEAT);
507    CK( 1 == p.bar );
508    CK( 1 == p.beat );
509    CK( 0 == p.tick );
510    CK( 0 == p.frame );
511    p.floor(TransportPosition::BAR);
512    CK( 1 == p.bar );
513    CK( 1 == p.beat );
514    CK( 0 == p.tick );
515    CK( 0 == p.frame );
516
517    double fpt = p.frames_per_tick();
518    p.tick = 1;
519    p.bbt_offset = 921;
520    p.frame = round(fpt);
521    p.floor(TransportPosition::TICK);
522    CK( 1 == p.bar );
523    CK( 1 == p.beat );
524    CK( 8 == p.tick );
525    CK( DRIFT(79.0, p.frame, 1) );
526    CK( 0 == p.bbt_offset );
527    p.tick = 1;
528    p.bbt_offset = 921;
529    p.frame = round(fpt);
530    p.floor(TransportPosition::BEAT);
531    CK( 1 == p.bar );
532    CK( 1 == p.beat );
533    CK( 0 == p.tick );
534    CK( 0 == p.frame );
535    CK( 0 == p.bbt_offset );
536    p.tick = 1;
537    p.bbt_offset = 921;
538    p.frame = round(fpt);
539    p.floor(TransportPosition::BAR);
540    CK( 1 == p.bar );
541    CK( 1 == p.beat );
542    CK( 0 == p.tick );
543    CK( 0 == p.frame );
544    CK( 0 == p.bbt_offset );
545
546    p.tick = 1;
547    p.bbt_offset = 921;
548    p.frame = round(fpt/2.0);
549    p.floor(TransportPosition::BAR);
550    CK( 1 == p.bar );
551    CK( 1 == p.beat );
552    CK( 0 == p.tick );
553    CK( 0 == p.frame );
554    CK( 0 == p.bbt_offset );
555
556    TransportPosition tmp = x;
557    double lastframe = tmp.frame;
558    tmp.floor(TransportPosition::TICK);
559    CK( tmp.bar == 349 );
560    CK( tmp.beat == 5 );
561    CK( tmp.tick == 18 );
562    CK( tmp.bbt_offset == 0 );
563    lastframe -= 115.0;
564    CK( DRIFT(lastframe, tmp.frame, 1) );
565    // Repeating when already floored should
566    // give the same result.
567    tmp.floor(TransportPosition::TICK);
568    CK( tmp.bar == 349 );
569    CK( tmp.beat == 5 );
570    CK( tmp.tick == 18 );
571    CK( tmp.bbt_offset == 0 );
572    CK( DRIFT(lastframe, tmp.frame, 1) );
573
574    tmp = x;
575    tmp.floor(TransportPosition::BEAT);
576    CK( tmp.bar == 349 );
577    CK( tmp.beat == 5 );
578    CK( tmp.tick == 0 );
579    CK( tmp.bbt_offset == 0 );
580    lastframe -= 18.0 * tmp.frames_per_tick();
581    CK( DRIFT(lastframe, tmp.frame, 1) );
582    // Repeating when already floored should
583    // give the same result.
584    tmp.floor(TransportPosition::BEAT);
585    CK( tmp.bar == 349 );
586    CK( tmp.beat == 5 );
587    CK( tmp.tick == 0 );
588    CK( tmp.bbt_offset == 0 );
589    CK( DRIFT(lastframe, tmp.frame, 1) );
590
591    tmp = x;
592    tmp.floor(TransportPosition::BAR);
593    CK( tmp.bar == 349 );
594    CK( tmp.beat == 1 );
595    CK( tmp.tick == 0 );
596    CK( tmp.bbt_offset == 0 );
597    lastframe = x.frame - 4.0 * 99.0 * tmp.frames_per_tick()
598        - x.tick * tmp.frames_per_tick() - x.bbt_offset;
599    CK( DRIFT(lastframe, tmp.frame, 1) );
600    // Repeating when already floored should
601    // give the same result.
602    tmp.floor(TransportPosition::BAR);
603    CK( tmp.bar == 349 );
604    CK( tmp.beat == 1 );
605    CK( tmp.tick == 0 );
606    CK( tmp.bbt_offset == 0 );
607    CK( DRIFT(lastframe, tmp.frame, 1) );
608
609}
610
611TEST_CASE( 080_ceil )
612{
613    p.ceil(TransportPosition::TICK);
614    CK( 1 == p.bar );
615    CK( 1 == p.beat );
616    CK( 0 == p.tick );
617    CK( 0 == p.frame );
618    p.ceil(TransportPosition::BEAT);
619    CK( 1 == p.bar );
620    CK( 1 == p.beat );
621    CK( 0 == p.tick );
622    CK( 0 == p.frame );
623    p.ceil(TransportPosition::BAR);
624    CK( 1 == p.bar );
625    CK( 1 == p.beat );
626    CK( 0 == p.tick );
627    CK( 0 == p.frame );
628
629    double fpt = p.frames_per_tick();
630    double frame;
631    p.tick = 1;
632    p.bbt_offset = 75;
633    p.frame = round(fpt);
634    frame = p.frame + fpt - p.bbt_offset;
635    p.ceil(TransportPosition::TICK);
636    CK( 1 == p.bar );
637    CK( 1 == p.beat );
638    CK( 2 == p.tick );
639    CK( DRIFT(frame, p.frame, 1) );
640    CK( 0 == p.bbt_offset );
641    p.tick = 1;
642    p.bbt_offset = 921;
643    p.frame = round(fpt);
644    p.ceil(TransportPosition::BEAT);
645    CK( 1 == p.bar );
646    CK( 2 == p.beat );
647    CK( 0 == p.tick );
648    // frame = [1:1.8.0] + fpt * (192-8)
649    //       = 79 + 125.0 * 184 = 23079
650    CK( DRIFT(23079.0, p.frame, 2) ); // 2 internal ops.
651    CK( 0 == p.bbt_offset );
652    p.tick = 1;
653    p.bbt_offset = 921;
654    p.frame = round(fpt);
655    p.ceil(TransportPosition::BAR);
656    CK( 2 == p.bar );
657    CK( 1 == p.beat );
658    CK( 0 == p.tick );
659    // frame = 125 + [2:1.0.0] - [1:2.1.921]
660    //       = 125 + [2:1.0.0] - [1:2.8.46]
661    //       = 125 + [1:5.0.0] - [1:2.8.46]
662    //       = 125 + fpt* ((2 * 192) + (192-9)) + (125-46)
663    //       = 71079.0
664    CK( DRIFT(71079.0, p.frame, 2) ); // 2 internal ops
665    CK( 0 == p.bbt_offset );
666    CK( 192 * 4 == p.bar_start_tick );
667
668    p.tick = 1;
669    p.bbt_offset = 921;
670    p.frame = round(fpt/2.0);
671    p.ceil(TransportPosition::BAR);
672    CK( 3 == p.bar );
673    CK( 1 == p.beat );
674    CK( 0 == p.tick );
675    // frame = 63 + [3:1.0.0] - [2:1.1.921]
676    //       = 63 + [3:1.0.0] - [2:1.8.46]
677    //       = 63 + [2:5.0.0] - [2:1.8.46]
678    //       = 63 + fpt* ((3 * 192) + (192-9)) + (125-46)
679    //       = 95017
680    CK( DRIFT(95017.0, p.frame, 2) );
681    CK( 0 == p.bbt_offset );
682    CK( 192 * 4 * 2 == p.bar_start_tick );
683
684    // TODO:  ADD MORE TESTS FOR BAR START TICK
685
686    TransportPosition tmp = x;
687    double lastframe = tmp.frame;
688    tmp.ceil(TransportPosition::TICK);
689    CK( tmp.bar == 349 );
690    CK( tmp.beat == 5 );
691    CK( tmp.tick == 19 );
692    CK( tmp.bbt_offset == 0 );
693    lastframe += (x.frames_per_tick() - 115.0);
694    CK( DRIFT(lastframe, tmp.frame, 1) );
695    // Repeating when already ceiled should
696    // give the same result.
697    tmp.ceil(TransportPosition::TICK);
698    CK( tmp.bar == 349 );
699    CK( tmp.beat == 5 );
700    CK( tmp.tick == 19 );
701    CK( tmp.bbt_offset == 0 );
702    CK( DRIFT(lastframe, tmp.frame, 1) );
703
704    tmp = x;
705    lastframe = tmp.frame;
706    tmp.ceil(TransportPosition::BEAT);
707    CK( tmp.bar == 349 );
708    CK( tmp.beat == 6 );
709    CK( tmp.tick == 0 );
710    CK( tmp.bbt_offset == 0 );
711    lastframe += (99.0 - 18.0) * tmp.frames_per_tick() - 115.0;
712    CK( DRIFT(lastframe, tmp.frame, 1) );
713    // Repeating when already ceiled should
714    // give the same result.
715    tmp.ceil(TransportPosition::BEAT);
716    CK( tmp.bar == 349 );
717    CK( tmp.beat == 6 );
718    CK( tmp.tick == 0 );
719    CK( tmp.bbt_offset == 0 );
720    CK( DRIFT(lastframe, tmp.frame, 1) );
721
722    tmp = x;
723    lastframe = tmp.frame;
724    lastframe += ((2.0 * 99.0) + (99.0 - 18.0)) * tmp.frames_per_tick()
725        - tmp.bbt_offset;
726    tmp.ceil(TransportPosition::BAR);
727    CK( tmp.bar == 350 );
728    CK( tmp.beat == 1 );
729    CK( tmp.tick == 0 );
730    CK( tmp.bbt_offset == 0 );
731    CK( DRIFT(lastframe, tmp.frame, 1) );
732    // Repeating when already ceiled should
733    // give the same result.
734    tmp.ceil(TransportPosition::BAR);
735    CK( tmp.bar == 350 );
736    CK( tmp.beat == 1 );
737    CK( tmp.tick == 0 );
738    CK( tmp.bbt_offset == 0 );
739    CK( DRIFT(lastframe, tmp.frame, 1) );
740}
741
742TEST_CASE( 090_round )
743{
744    TransportPosition a;
745    double frame;
746
747    a = p;
748    a.round(TransportPosition::TICK);
749    CK( 0 == a.frame );
750    CK( 1 == a.bar );
751    CK( 1 == a.beat );
752    CK( 0 == a.tick );
753    CK( 0 == a.bbt_offset );
754    a.round(TransportPosition::BEAT);
755    CK( 0 == a.frame );
756    CK( 1 == a.bar );
757    CK( 1 == a.beat );
758    CK( 0 == a.tick );
759    CK( 0 == a.bbt_offset );
760    a.round(TransportPosition::BAR);
761    CK( 0 == a.frame );
762    CK( 1 == a.bar );
763    CK( 1 == a.beat );
764    CK( 0 == a.tick );
765    CK( 0 == a.bbt_offset );
766
767    a = p;
768    a.bbt_offset = a.frames_per_tick() / 4.0;
769    a.round(TransportPosition::TICK);
770    CK( 0 == a.frame );
771    CK( 1 == a.bar );
772    CK( 1 == a.beat );
773    CK( 0 == a.tick );
774    CK( 0 == a.bbt_offset );
775    a.bbt_offset = a.frames_per_tick() * 3.0 / 4.0;
776    frame = a.frame + round(a.frames_per_tick() / 4.0);
777    a.round(TransportPosition::TICK);
778    CK( DRIFT( frame, a.frame, 1 ) );
779    CK( 1 == a.bar );
780    CK( 1 == a.beat );
781    CK( 1 == a.tick );
782    CK( 0 == a.bbt_offset );
783    // Re-rounding should not change anything
784    a.round(TransportPosition::TICK);
785    CK( DRIFT( frame, a.frame, 1 ) );
786    CK( 1 == a.bar );
787    CK( 1 == a.beat );
788    CK( 1 == a.tick );
789    CK( 0 == a.bbt_offset );
790
791    // Could use more checks near the zero-point,
792    // but we'll move on.
793
794    a = x;
795    frame = a.frame;
796    frame += a.frames_per_tick() - a.bbt_offset;
797    a.round(TransportPosition::TICK);
798    CK( 349 == a.bar );
799    CK( 5 == a.beat );
800    CK( 19 == a.tick );
801    CK( 0 == a.bbt_offset );
802    CK( DRIFT( frame, a.frame, 1 ) );
803
804    frame -= a.frames_per_tick() * double(a.tick);
805    a.round(TransportPosition::BEAT);
806    CK( 349 == a.bar );
807    CK( 5 == a.beat );
808    CK( 0 == a.tick );
809    CK( 0 == a.bbt_offset );
810    CK( DRIFT( frame, a.frame, 2 ) );
811
812    frame += a.frames_per_tick() * 99.0 * 3.0;
813    a.round(TransportPosition::BAR);
814    CK( 350 == a.bar );
815    CK( 1 == a.beat );
816    CK( 0 == a.tick );
817    CK( 0 == a.bbt_offset );
818    CK( DRIFT( frame, a.frame, 3 ) );
819
820    // Could use more checks... but we'll move on.
821
822}
823
824TEST_CASE( 100_operator_plus )
825{
826    unsigned k;
827    TransportPosition a;
828    double fpt;
829    double frame;
830
831    a = p + (-51);
832    CK( 1 == a.bar );
833    CK( 1 == a.beat );
834    CK( 0 == a.tick );
835    CK( 0 == a.bbt_offset );
836    CK( 0 == a.frame );
837
838    a = p;
839    fpt = a.frames_per_tick();
840    for( k=0 ; k<191 ; ++k ) {
841        a = a + 1;
842        CK( 1 == a.bar );
843        CK( 1 == a.beat );
844        CK( (k+1) == unsigned(a.tick) );
845        CK( DRIFT( double(k+1) * fpt, a.frame, k ) );
846    }
847    CK( 191 == a.tick );
848    ++k;
849    a = a + 1;
850    CK( 1 == a.bar );
851    CK( 2 == a.beat );
852    CK( 0 == a.tick );
853    CK( DRIFT( double(k) * fpt, a.frame, k ) );
854
855    k = 192 * 2 + 30;
856    a = p + k;
857    CK( 1 == a.bar );
858    CK( 3 == a.beat );
859    CK( 30 == a.tick );
860    CK( DRIFT( double(k) * fpt, a.frame, 1 ) );
861
862
863    k = 99 * 5 + 64;
864    a = x + k;
865    frame = x.frame + (99*5 + 64) * x.frames_per_tick();
866    CK( 350 == a.bar );
867    CK( 3 == a.beat );
868    CK( 82 == a.tick );
869    CK( 115 == a.bbt_offset );
870    CK( DRIFT( frame, a.frame, 1 ) );
871
872    a = a + (-k);
873    frame = x.frame;
874    CK( 349 == a.bar );
875    CK( 5 == a.beat );
876    CK( 18 == a.tick );
877    CK( 115 == a.bbt_offset );
878    CK( DRIFT( frame, a.frame, 2 ) );
879}
880
881TEST_CASE( 110_operator_minus )
882{
883    unsigned k;
884    TransportPosition a;
885    double fpt;
886
887    a = p - 51;
888    CK( 1 == a.bar );
889    CK( 1 == a.beat );
890    CK( 0 == a.tick );
891    CK( 0 == a.bbt_offset );
892    CK( 0 == a.frame );
893
894    a = p;
895    a.bar = 2;
896    a.beat = 1;
897    a.tick = 0;
898    a.frame = 2000000;
899    fpt = a.frames_per_tick();
900    for( k=192 ; k>0 ; --k ) {
901        a = a - 1;
902        CK( 1 == a.bar );
903        CK( 4 == a.beat );
904        CK( (k-1) == unsigned(a.tick) );
905        CK( DRIFT( 2000000.0 - (double(192-k+1) * fpt), a.frame, (193-k) ) );
906    }
907    CK( 0 == a.tick );
908    a = a - 1;
909    k = 193;
910    CK( 1 == a.bar );
911    CK( 3 == a.beat );
912    CK( 191 == a.tick );
913    CK( DRIFT( 2000000.0 - (double(k) * fpt), a.frame, k ) );
914
915    k = 99 * 2 + 30;
916    a = x - k;
917    fpt = x.frames_per_tick();
918    CK( 349 == a.bar );
919    CK( 2 == a.beat );
920    CK( 87 == a.tick );
921    CK( 115 == a.bbt_offset );
922    CK( DRIFT( double(x.frame) - (double(k) * fpt), a.frame, 1 ) );
923
924    k = 99 * 5 + 64;
925    a = x - k;
926    CK( 348 == a.bar );
927    CK( 6 == a.beat );
928    CK( 53 == a.tick );
929    CK( 115 == a.bbt_offset );
930    CK( DRIFT( double(x.frame) - double(k) * fpt, a.frame, 1 ) );
931
932    a = a - (-k);
933    CK( 349 == a.bar );
934    CK( 5 == a.beat );
935    CK( 18 == a.tick );
936    CK( 115 == a.bbt_offset );
937    CK( DRIFT( double(x.frame), a.frame, 2 ) );
938}
939
940TEST_CASE( 120_operator_plus_equals )
941{
942    unsigned k;
943    TransportPosition a;
944    double fpt;
945
946    a = p;
947    fpt = a.frames_per_tick();
948    for( k=0 ; k<191 ; ++k ) {
949        a += 1;
950        CK( 1 == a.bar );
951        CK( 1 == a.beat );
952        CK( k+1 == unsigned(a.tick) );
953        CK( DRIFT( double(k+1) * fpt, a.frame, k ) );
954    }
955    CK( 191 == a.tick );
956    ++k;
957    a += 1;
958    CK( 1 == a.bar );
959    CK( 2 == a.beat );
960    CK( 0 == a.tick );
961    CK( DRIFT( double(k) * fpt, a.frame, k ) );
962
963    k = 192 * 2 + 30;
964    a = p;
965    a += k;
966    CK( 1 == a.bar );
967    CK( 3 == a.beat );
968    CK( 30 == a.tick );
969    CK( DRIFT( double(k) * fpt, a.frame, 1 ) );
970
971    k = 99 * 5 + 64;
972    a = x;
973    a += k;
974    fpt = a.frames_per_tick();
975    CK( 350 == a.bar );
976    CK( 3 == a.beat );
977    CK( 82 == a.tick );
978    CK( 115 == a.bbt_offset );
979    CK( DRIFT( double(x.frame) + double(k) * fpt, a.frame, 1 ) );
980
981    a += (-k);
982    CK( 349 == a.bar );
983    CK( 5 == a.beat );
984    CK( 18 == a.tick );
985    CK( 115 == a.bbt_offset );
986    CK( DRIFT( double(x.frame), a.frame, 2 ) );
987}
988
989TEST_CASE( 130_operator_minus_equals )
990{
991    unsigned k;
992    TransportPosition a;
993    double fpt;
994
995    a = p;
996    a -= 51;
997    CK( 1 == a.bar );
998    CK( 1 == a.beat );
999    CK( 0 == a.tick );
1000    CK( 0 == a.bbt_offset );
1001    CK( 0 == a.frame );
1002
1003    a = p;
1004    a.bar = 2;
1005    a.beat = 1;
1006    a.tick = 0;
1007    a.frame = 2000000;
1008    fpt = a.frames_per_tick();
1009    for( k=192 ; k>0 ; --k ) {
1010        a -= 1;
1011        CK( 1 == a.bar );
1012        CK( 4 == a.beat );
1013        CK( (k-1) == unsigned(a.tick) );
1014        CK( DRIFT( 2000000.0 - (double(192-k+1) * fpt), a.frame, (193-k) ) );
1015    }
1016    CK( 0 == a.tick );
1017    a -= 1;
1018    k = 193;
1019    CK( 1 == a.bar );
1020    CK( 3 == a.beat );
1021    CK( 191 == a.tick );
1022    CK( DRIFT( 2000000.0 - (double(k) * fpt), a.frame, k ) );
1023
1024    k = 99 * 2 + 30;
1025    a = x;
1026    a -= k;
1027    fpt = x.frames_per_tick();
1028    CK( 349 == a.bar );
1029    CK( 2 == a.beat );
1030    CK( 87 == a.tick );
1031    CK( 115 == a.bbt_offset );
1032    CK( DRIFT( double(x.frame) - (double(k) * fpt), a.frame, 1 ) );
1033
1034    k = 99 * 5 + 64;
1035    a = x;
1036    a -= k;
1037    CK( 348 == a.bar );
1038    CK( 6 == a.beat );
1039    CK( 53 == a.tick );
1040    CK( 115 == a.bbt_offset );
1041    CK( DRIFT( double(x.frame) - double(k) * fpt, a.frame, 1 ) );
1042
1043    a -= (-k);
1044    CK( 349 == a.bar );
1045    CK( 5 == a.beat );
1046    CK( 18 == a.tick );
1047    CK( 115 == a.bbt_offset );
1048    CK( DRIFT( double(x.frame), a.frame, 2 ) );
1049}
1050
1051TEST_END()
Note: See TracBrowser for help on using the browser.