root/trunk/libffado/src/libutil/TimestampedBuffer.cpp

Revision 796, 40.9 kB (checked in by ppalmers, 13 years ago)

- move #define constants to config.h.in
- switch receive handler over to packet-per-buffer mode to improve latency performance

Line 
1 /*
2  * Copyright (C) 2005-2007 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "config.h"
25
26 #include "libutil/Atomic.h"
27 #include "libieee1394/cycletimer.h"
28
29 #include "TimestampedBuffer.h"
30 #include "assert.h"
31 #include "errno.h"
32
33
34 #define DLL_PI        (3.141592653589793238)
35 #define DLL_SQRT2     (1.414213562373095049)
36 #define DLL_OMEGA     (2.0*DLL_PI*TIMESTAMPEDBUFFER_DLL_BANDWIDTH)
37 #define DLL_COEFF_B   (DLL_SQRT2 * DLL_OMEGA)
38 #define DLL_COEFF_C   (DLL_OMEGA * DLL_OMEGA)
39
40 /*
41 #define ENTER_CRITICAL_SECTION { \
42     if (pthread_mutex_trylock(&m_framecounter_lock) == EBUSY) { \
43         debugWarning(" (%p) lock clash\n", this); \
44         pthread_mutex_lock(&m_framecounter_lock); \
45     } \
46     }
47 */
48 #define ENTER_CRITICAL_SECTION { \
49     pthread_mutex_lock(&m_framecounter_lock); \
50     }
51 #define EXIT_CRITICAL_SECTION { \
52     pthread_mutex_unlock(&m_framecounter_lock); \
53     }
54
55 namespace Util {
56
57 IMPL_DEBUG_MODULE( TimestampedBuffer, TimestampedBuffer, DEBUG_LEVEL_VERBOSE );
58
59 TimestampedBuffer::TimestampedBuffer(TimestampedBufferClient *c)
60     : m_event_buffer(NULL), m_cluster_buffer(NULL),
61       m_event_size(0), m_events_per_frame(0), m_buffer_size(0),
62       m_bytes_per_frame(0), m_bytes_per_buffer(0),
63       m_enabled( false ), m_transparent ( true ),
64       m_wrap_at(0xFFFFFFFFFFFFFFFFLLU),
65       m_Client(c), m_framecounter(0),
66       m_tick_offset(0.0),
67       m_buffer_tail_timestamp(0.0),
68       m_buffer_next_tail_timestamp(0.0),
69       m_dll_e2(0.0), m_dll_b(DLL_COEFF_B), m_dll_c(DLL_COEFF_C),
70       m_nominal_rate(0.0), m_current_rate(0.0), m_update_period(0)
71 {
72     pthread_mutex_init(&m_framecounter_lock, NULL);
73
74 }
75
76 TimestampedBuffer::~TimestampedBuffer() {
77     ffado_ringbuffer_free(m_event_buffer);
78     free(m_cluster_buffer);
79 }
80
81 /**
82  * \brief Set the nominal rate in frames/timeunit
83  *
84  * Sets the nominal rate in frames per time unit. This rate is used
85  * to initialize the DLL that will extract the effective rate based
86  * upon the timestamps it gets fed.
87  *
88  * @param r rate
89  * @return true if successful
90  */
91 bool TimestampedBuffer::setNominalRate(float r) {
92     m_nominal_rate=r;
93     debugOutput(DEBUG_LEVEL_VERBOSE," nominal rate=%e set to %e\n",
94                                     m_nominal_rate, r);
95     return true;
96 }
97
98 /**
99  * \brief Set the nominal update period (in frames)
100  *
101  * Sets the nominal update period. This period is the number of frames
102  * between two timestamp updates (hence buffer writes)
103  *
104  * @param n period in frames
105  * @return true if successful
106  */
107 bool TimestampedBuffer::setUpdatePeriod(unsigned int n) {
108     m_update_period=n;
109     return true;
110 }
111
112 /**
113  * \brief set the value at which timestamps should wrap around
114  * @param w value to wrap at
115  * @return true if successful
116  */
117 bool TimestampedBuffer::setWrapValue(ffado_timestamp_t w) {
118     m_wrap_at=w;
119     return true;
120 }
121 #include <math.h>
122
123 /**
124  * \brief return the effective rate
125  *
126  * Returns the effective rate calculated by the DLL.
127  *
128  * @return rate (in timeunits/frame)
129  */
130 float TimestampedBuffer::getRate() {
131     return m_current_rate;
132 }
133
134 /**
135  * \brief calculate the effective rate
136  *
137  * Returns the effective rate calculated by the DLL.
138  * @note should be called with the lock held
139  * @return rate (in timeunits/frame)
140  */
141 float TimestampedBuffer::calculateRate() {
142     ffado_timestamp_t diff;
143    
144     diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
145    
146     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"getRate: %f/%f=%f\n",
147         (float)(diff),
148         (float)m_update_period,
149         ((float)(diff))/((float) m_update_period));
150    
151     // the maximal difference we can allow (64secs)
152     const ffado_timestamp_t max=m_wrap_at/((ffado_timestamp_t)2);
153
154     if(diff > max) {
155         diff -= m_wrap_at;
156     } else if (diff < -max) {
157         diff += m_wrap_at;
158     }
159
160     float rate=((float)diff)/((float) m_update_period);
161     if (rate<0.0) debugError("rate < 0! (%f)\n",rate);
162     if (fabsf(m_nominal_rate - rate)>(m_nominal_rate*0.1)) {
163         debugWarning("(%p) rate (%10.5f) more that 10%% off nominal (rate=%10.5f, diff="TIMESTAMP_FORMAT_SPEC", update_period=%d)\n",
164                      this, rate,m_nominal_rate,diff, m_update_period);
165
166         return m_nominal_rate;
167     } else {
168         return rate;
169     }
170 }
171
172 /**
173  * \brief Sets the size of the events
174  * @param s event size in bytes
175  * @return true if successful
176  */
177 bool TimestampedBuffer::setEventSize(unsigned int s) {
178     m_event_size=s;
179
180     m_bytes_per_frame=m_event_size*m_events_per_frame;
181     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
182
183     return true;
184 }
185
186 /**
187  * \brief Sets the number of events per frame
188  * @param n number of events per frame
189  * @return true if successful
190  */
191 bool TimestampedBuffer::setEventsPerFrame(unsigned int n) {
192     m_events_per_frame=n;
193
194     m_bytes_per_frame=m_event_size*m_events_per_frame;
195     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
196
197     return true;
198 }
199 /**
200  * \brief Sets the buffer size in frames
201  * @param n number frames
202  * @return true if successful
203  */
204 bool TimestampedBuffer::setBufferSize(unsigned int n) {
205     m_buffer_size=n;
206
207     m_bytes_per_frame=m_event_size*m_events_per_frame;
208     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
209
210     return true;
211 }
212
213 /**
214  * Sets the buffer offset in ticks.
215  *
216  * A positive value means that the buffer is 'delayed' for nticks ticks.
217  *
218  * @note These offsets are only used when reading timestamps. Any function
219  *       that returns a timestamp will incorporate this offset.
220  * @param nframes the number of ticks (positive = delay buffer)
221  * @return true if successful
222  */
223 bool TimestampedBuffer::setTickOffset(ffado_timestamp_t nticks) {
224     debugOutput(DEBUG_LEVEL_VERBOSE,"Setting ticks offset to "TIMESTAMP_FORMAT_SPEC"\n",nticks);
225
226     // JMW: I think we need to update the internal DLL state to take account
227     // of the new offset.  Doing so certainly makes for a smoother MOTU
228     // startup.
229     ENTER_CRITICAL_SECTION;
230     m_buffer_tail_timestamp = m_buffer_tail_timestamp - m_tick_offset + nticks;
231     m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2);
232     m_tick_offset=nticks;
233     EXIT_CRITICAL_SECTION;
234
235     return true;
236 }
237
238 /**
239  * \brief Returns the current fill of the buffer
240  *
241  * This returns the buffer fill of the internal ringbuffer. This
242  * can only be used as an indication because it's state is not
243  * guaranteed to be consistent at all times due to threading issues.
244  *
245  * In order to get the number of frames in the buffer, use the
246  * getBufferHeadTimestamp, getBufferTailTimestamp
247  * functions
248  *
249  * @return the internal buffer fill in frames
250  */
251 unsigned int TimestampedBuffer::getBufferFill() {
252     return ffado_ringbuffer_read_space(m_event_buffer)/(m_bytes_per_frame);
253 }
254
255 /**
256  * \brief Initializes the TimestampedBuffer
257  *
258  * Initializes the TimestampedBuffer, should be called before anything else
259  * is done.
260  *
261  * @return true if successful
262  */
263 bool TimestampedBuffer::init() {
264     return true;
265 }
266
267 /**
268  * \brief Resets the TimestampedBuffer
269  *
270  * Resets the TimestampedBuffer, clearing the buffers and counters.
271  * (not true yet: Also resets the DLL to the nominal values.)
272  *
273  * \note when this is called, you should make sure that the buffer
274  *       tail timestamp gets set before continuing
275  *
276  * @return true if successful
277  */
278 bool TimestampedBuffer::clearBuffer() {
279     debugOutput(DEBUG_LEVEL_VERBOSE, "Clearing buffer\n");
280     ffado_ringbuffer_reset(m_event_buffer);
281     resetFrameCounter();
282     return true;
283 }
284
285 /**
286  * \brief Prepares the TimestampedBuffer
287  *
288  * Prepare the TimestampedBuffer. This allocates all internal buffers and
289  * initializes all data structures.
290  *
291  * This should be called after parameters such as buffer size, event size etc.. are set,
292  * and before any read/write operations are performed.
293  *
294  * @return true if successful
295  */
296 bool TimestampedBuffer::prepare() {
297     debugOutput(DEBUG_LEVEL_VERBOSE,"Preparing buffer (%p)\n",this);
298     debugOutput(DEBUG_LEVEL_VERBOSE," Size=%u events, events/frame=%u, event size=%ubytes\n",
299                                         m_buffer_size,m_events_per_frame,m_event_size);
300
301     debugOutput(DEBUG_LEVEL_VERBOSE," update period %u\n",
302                                     m_update_period);
303     debugOutput(DEBUG_LEVEL_VERBOSE," nominal rate=%f\n",
304                                     m_nominal_rate);
305
306     debugOutput(DEBUG_LEVEL_VERBOSE," wrapping at "TIMESTAMP_FORMAT_SPEC"\n",m_wrap_at);
307
308     assert(m_buffer_size);
309     assert(m_events_per_frame);
310     assert(m_event_size);
311
312     assert(m_nominal_rate != 0.0L);
313     assert(m_update_period != 0);
314
315     m_current_rate = m_nominal_rate;
316
317     if( !(m_event_buffer=ffado_ringbuffer_create(
318             (m_events_per_frame * m_buffer_size) * m_event_size))) {
319         debugFatal("Could not allocate memory event ringbuffer\n");
320         return false;
321     }
322
323     // allocate the temporary cluster buffer
324     if( !(m_cluster_buffer=(char *)calloc(m_events_per_frame,m_event_size))) {
325             debugFatal("Could not allocate temporary cluster buffer\n");
326         ffado_ringbuffer_free(m_event_buffer);
327         return false;
328     }
329
330     // init the DLL
331     m_dll_e2=m_nominal_rate * (float)m_update_period;
332
333     m_dll_b=((float)(DLL_COEFF_B));
334     m_dll_c=((float)(DLL_COEFF_C));
335    
336     // this will init the internal timestamps to a sensible value
337     setBufferTailTimestamp(m_buffer_tail_timestamp);
338    
339     return true;
340 }
341
342 /**
343  * @brief Insert a dummy frame to the head buffer
344  *
345  * Writes one frame of dummy data to the head of the buffer.
346  * This is to assist the phase sync of several buffers.
347  *
348  * Note: currently the dummy data is added to the tail of the
349  *       buffer, but without updating the timestamp.
350  *
351  * @return true if successful
352  */
353 bool TimestampedBuffer::writeDummyFrame() {
354
355     unsigned int write_size=m_event_size*m_events_per_frame;
356    
357     char dummy[write_size]; // one frame of garbage
358     memset(dummy,0,write_size);
359
360     // add the data payload to the ringbuffer
361     if (ffado_ringbuffer_write(m_event_buffer,dummy,write_size) < write_size)
362     {
363 //         debugWarning("writeFrames buffer overrun\n");
364         return false;
365     }
366
367 //     incrementFrameCounter(nframes,ts);
368    
369     // increment without updating the DLL
370     ENTER_CRITICAL_SECTION;
371     m_framecounter++;
372     EXIT_CRITICAL_SECTION;
373    
374     return true;
375 }
376
377 /**
378  * @brief Write frames to the buffer
379  *
380  * Copies \ref nframes of frames from the buffer pointed to by \ref data to the
381  * internal ringbuffer. The time of the last frame in the buffer is set to \ref ts.
382  *
383  * @param nframes number of frames to copy
384  * @param data pointer to the frame buffer
385  * @param ts timestamp of the last frame copied
386  * @return true if successful
387  */
388 bool TimestampedBuffer::writeFrames(unsigned int nframes, char *data, ffado_timestamp_t ts) {
389
390     unsigned int write_size=nframes*m_event_size*m_events_per_frame;
391
392     if (m_transparent) {
393         // while disabled, we don't update the DLL, nor do we write frames
394         // we just set the correct timestamp for the frames
395         setBufferTailTimestamp(ts);
396     } else {
397         // add the data payload to the ringbuffer
398         size_t written = ffado_ringbuffer_write(m_event_buffer, data, write_size);
399         if (written < write_size)
400         {
401             debugWarning("ringbuffer full, %u, %u\n", write_size, written);
402             return false;
403         }
404         incrementFrameCounter(nframes,ts);
405     }
406     return true;
407 }
408
409 /**
410  * @brief Preload frames into the buffer
411  *
412  * Preload \ref nframes of frames from the buffer pointed to by \ref data to the
413  * internal ringbuffer. Does not care about transparency. Keeps the buffer head or tail
414  * timestamp constant.
415  *
416  * @note not thread safe
417  *
418  * @param nframes number of frames to copy
419  * @param data pointer to the frame buffer
420  * @param keep_head_ts if true, keep the head timestamp constant. If false, keep the
421  *                     tail timestamp constant.
422  * @return true if successful
423  */
424 bool TimestampedBuffer::preloadFrames(unsigned int nframes, char *data, bool keep_head_ts) {
425     unsigned int write_size = nframes * m_event_size * m_events_per_frame;
426     // add the data payload to the ringbuffer
427     size_t written = ffado_ringbuffer_write(m_event_buffer, data, write_size);
428     if (written < write_size)
429     {
430         debugWarning("ringbuffer full, request: %u, actual: %u\n", write_size, written);
431         return false;
432     }
433    
434     // make sure the head timestamp remains identical
435     signed int fc;
436     ffado_timestamp_t ts;
437
438     if (keep_head_ts) {
439         getBufferHeadTimestamp(&ts, &fc);
440     } else {
441         getBufferTailTimestamp(&ts, &fc);
442     }
443     // update frame counter
444     m_framecounter += nframes;
445     if (keep_head_ts) {
446         setBufferHeadTimestamp(ts);
447     } else {
448         setBufferTailTimestamp(ts);
449     }
450
451     return true;
452 }
453
454 /**
455  * @brief Drop frames from the head of the buffer
456  *
457  * drops \ref nframes of frames from the head of internal buffer
458  *
459  * @param nframes number of frames to drop
460  * @return true if successful
461  */
462 bool
463 TimestampedBuffer::dropFrames(unsigned int nframes) {
464     unsigned int read_size = nframes * m_event_size * m_events_per_frame;
465     ffado_ringbuffer_read_advance(m_event_buffer, read_size);
466     decrementFrameCounter(nframes);
467     return true;
468 }
469
470 /**
471  * @brief Read frames from the buffer
472  *
473  * Copies \ref nframes of frames from the internal buffer to the data buffer pointed
474  * to by \ref data.
475  *
476  * @param nframes number of frames to copy
477  * @param data pointer to the frame buffer
478  * @return true if successful
479  */
480 bool TimestampedBuffer::readFrames(unsigned int nframes, char *data) {
481
482     unsigned int read_size=nframes*m_event_size*m_events_per_frame;
483
484     if (m_transparent) {
485         return true; // FIXME: the data still doesn't make sense!
486     } else {
487         // get the data payload to the ringbuffer
488         if ((ffado_ringbuffer_read(m_event_buffer,data,read_size)) < read_size)
489         {
490             debugWarning("readFrames buffer underrun\n");
491             return false;
492         }
493         decrementFrameCounter(nframes);
494     }
495     return true;
496 }
497
498 /**
499  * @brief Performs block processing write of frames
500  *
501  * This function allows for zero-copy writing into the ringbuffer.
502  * It calls the client's processWriteBlock function to write frames
503  * into the internal buffer's data area, in a thread safe fashion.
504  *
505  * It also updates the timestamp.
506  *
507  * @param nbframes number of frames to process
508  * @param ts timestamp of the last frame written to the buffer
509  * @return true if successful
510  */
511 bool TimestampedBuffer::blockProcessWriteFrames(unsigned int nbframes, ffado_timestamp_t ts) {
512
513     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Transferring period...\n");
514     int xrun;
515     unsigned int offset=0;
516
517     ffado_ringbuffer_data_t vec[2];
518     // we received one period of frames
519     // this is period_size*dimension of events
520     unsigned int events2write=nbframes*m_events_per_frame;
521     unsigned int bytes2write=events2write*m_event_size;
522
523     /* write events2write bytes to the ringbuffer
524     *  first see if it can be done in one read.
525     *  if so, ok.
526     *  otherwise write up to a multiple of clusters directly to the buffer
527     *  then do the buffer wrap around using ringbuffer_write
528     *  then write the remaining data directly to the buffer in a third pass
529     *  Make sure that we cannot end up on a non-cluster aligned position!
530     */
531     unsigned int cluster_size=m_events_per_frame*m_event_size;
532
533     while(bytes2write>0) {
534         int byteswritten=0;
535
536         unsigned int frameswritten=(nbframes*cluster_size-bytes2write)/cluster_size;
537         offset=frameswritten;
538
539         ffado_ringbuffer_get_write_vector(m_event_buffer, vec);
540
541         if(vec[0].len==0) { // this indicates a full event buffer
542             debugError("Event buffer overrun in buffer %p, fill: %u, bytes2write: %u \n",
543                        this, ffado_ringbuffer_read_space(m_event_buffer), bytes2write);
544             debugShowBackLog();
545             return false;
546         }
547
548         /* if we don't take care we will get stuck in an infinite loop
549         * because we align to a cluster boundary later
550         * the remaining nb of bytes in one write operation can be
551         * smaller than one cluster
552         * this can happen because the ringbuffer size is always a power of 2
553         */
554         if(vec[0].len<cluster_size) {
555
556             // encode to the temporary buffer
557             xrun = m_Client->processWriteBlock(m_cluster_buffer, 1, offset);
558
559             if(xrun<0) {
560                 // xrun detected
561                 debugError("Frame buffer underrun in buffer %p\n",this);
562                 return false;
563             }
564
565             // use the ringbuffer function to write one cluster
566             // the write function handles the wrap around.
567             ffado_ringbuffer_write(m_event_buffer,
568                          m_cluster_buffer,
569                          cluster_size);
570
571             // we advanced one cluster_size
572             bytes2write-=cluster_size;
573
574         } else { //
575
576             if(bytes2write>vec[0].len) {
577                 // align to a cluster boundary
578                 byteswritten=vec[0].len-(vec[0].len%cluster_size);
579             } else {
580                 byteswritten=bytes2write;
581             }
582
583             xrun = m_Client->processWriteBlock(vec[0].buf,
584                          byteswritten/cluster_size,
585                          offset);
586
587             if(xrun<0) {
588                     // xrun detected
589                 debugError("Frame buffer underrun in buffer %p\n",this);
590                 return false; // FIXME: return false ?
591             }
592
593             ffado_ringbuffer_write_advance(m_event_buffer, byteswritten);
594             bytes2write -= byteswritten;
595         }
596
597         // the bytes2write should always be cluster aligned
598         assert(bytes2write%cluster_size==0);
599
600     }
601
602     incrementFrameCounter(nbframes,ts);
603
604     return true;
605
606 }
607
608 /**
609  * @brief Performs block processing read of frames
610  *
611  * This function allows for zero-copy reading from the ringbuffer.
612  * It calls the client's processReadBlock function to read frames
613  * directly from the internal buffer's data area, in a thread safe
614  * fashion.
615  *
616  * @param nbframes number of frames to process
617  * @return true if successful
618  */
619 bool TimestampedBuffer::blockProcessReadFrames(unsigned int nbframes) {
620
621     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Reading %u from buffer (%p)...\n", nbframes, this);
622
623     int xrun;
624     unsigned int offset=0;
625
626     ffado_ringbuffer_data_t vec[2];
627     // we received one period of frames on each connection
628     // this is period_size*dimension of events
629
630     unsigned int events2read=nbframes*m_events_per_frame;
631     unsigned int bytes2read=events2read*m_event_size;
632     /* read events2read bytes from the ringbuffer
633     *  first see if it can be done in one read.
634     *  if so, ok.
635     *  otherwise read up to a multiple of clusters directly from the buffer
636     *  then do the buffer wrap around using ringbuffer_read
637     *  then read the remaining data directly from the buffer in a third pass
638     *  Make sure that we cannot end up on a non-cluster aligned position!
639     */
640     unsigned int cluster_size=m_events_per_frame*m_event_size;
641
642     while(bytes2read>0) {
643         unsigned int framesread=(nbframes*cluster_size-bytes2read)/cluster_size;
644         offset=framesread;
645
646         int bytesread=0;
647
648         ffado_ringbuffer_get_read_vector(m_event_buffer, vec);
649
650         if(vec[0].len==0) { // this indicates an empty event buffer
651             debugError("Event buffer underrun in buffer %p\n",this);
652             return false;
653         }
654
655         /* if we don't take care we will get stuck in an infinite loop
656         * because we align to a cluster boundary later
657         * the remaining nb of bytes in one read operation can be smaller than one cluster
658         * this can happen because the ringbuffer size is always a power of 2
659                 */
660         if(vec[0].len<cluster_size) {
661             // use the ringbuffer function to read one cluster
662             // the read function handles wrap around
663             ffado_ringbuffer_read(m_event_buffer,m_cluster_buffer,cluster_size);
664
665             assert(m_Client);
666             xrun = m_Client->processReadBlock(m_cluster_buffer, 1, offset);
667
668             if(xrun<0) {
669                 // xrun detected
670                 debugError("Frame buffer overrun in buffer %p\n",this);
671                     return false;
672             }
673
674             // we advanced one cluster_size
675             bytes2read-=cluster_size;
676
677         } else { //
678
679             if(bytes2read>vec[0].len) {
680                 // align to a cluster boundary
681                 bytesread=vec[0].len-(vec[0].len%cluster_size);
682             } else {
683                 bytesread=bytes2read;
684             }
685
686             assert(m_Client);
687             xrun = m_Client->processReadBlock(vec[0].buf, bytesread/cluster_size, offset);
688
689             if(xrun<0) {
690                 // xrun detected
691                 debugError("Frame buffer overrun in buffer %p\n",this);
692                 return false;
693             }
694
695             ffado_ringbuffer_read_advance(m_event_buffer, bytesread);
696             bytes2read -= bytesread;
697         }
698
699         // the bytes2read should always be cluster aligned
700         assert(bytes2read%cluster_size==0);
701     }
702
703     decrementFrameCounter(nbframes);
704
705     return true;
706 }
707
708 /**
709  * @brief Sets the buffer tail timestamp.
710  *
711  * Set the buffer tail timestamp to \ref new_timestamp. This will recalculate
712  * the internal state such that the buffer's timeframe starts at
713  * \ref new_timestamp.
714  *
715  * This is thread safe.
716  *
717  * @note considers offsets
718  *
719  * @param new_timestamp
720  */
721 void TimestampedBuffer::setBufferTailTimestamp(ffado_timestamp_t new_timestamp) {
722
723     // add the offsets
724     ffado_timestamp_t ts=new_timestamp;
725     ts += m_tick_offset;
726
727     if (ts >= m_wrap_at) {
728         ts -= m_wrap_at;
729     } else if (ts < 0) {
730         ts += m_wrap_at;
731     }
732
733 #ifdef DEBUG
734     if (new_timestamp >= m_wrap_at) {
735         debugWarning("timestamp not wrapped: "TIMESTAMP_FORMAT_SPEC"\n",new_timestamp);
736     }
737     if ((ts >= m_wrap_at) || (ts < 0 )) {
738         debugWarning("ts not wrapped correctly: "TIMESTAMP_FORMAT_SPEC"\n",ts);
739     }
740 #endif
741
742     ENTER_CRITICAL_SECTION;
743
744     m_buffer_tail_timestamp = ts;
745
746     m_dll_e2=m_update_period * (double)m_nominal_rate;
747     m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2);
748
749     EXIT_CRITICAL_SECTION;
750
751     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "for (%p) to "
752                                           TIMESTAMP_FORMAT_SPEC" => "TIMESTAMP_FORMAT_SPEC", NTS="
753                                           TIMESTAMP_FORMAT_SPEC", DLL2=%f, RATE=%f\n",
754                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, getRate());
755
756 }
757
758 /**
759  * @brief Sets the buffer head timestamp.
760  *
761  * Set the buffer tail timestamp such that the buffer head timestamp becomes
762  * \ref new_timestamp. This does not consider offsets, because it's use is to
763  * make sure the following is true after setBufferHeadTimestamp(x):
764  *   x == getBufferHeadTimestamp()
765  *
766  * This is thread safe.
767  *
768  * @param new_timestamp
769  */
770 void TimestampedBuffer::setBufferHeadTimestamp(ffado_timestamp_t new_timestamp) {
771
772 #ifdef DEBUG
773     if (new_timestamp >= m_wrap_at) {
774         debugWarning("timestamp not wrapped: "TIMESTAMP_FORMAT_SPEC"\n",new_timestamp);
775     }
776 #endif
777
778     ffado_timestamp_t ts = new_timestamp;
779
780     ENTER_CRITICAL_SECTION;
781
782     // add the time
783     ts += (ffado_timestamp_t)(m_nominal_rate * (float)m_framecounter);
784
785     if (ts >= m_wrap_at) {
786         ts -= m_wrap_at;
787     } else if (ts < 0) {
788         ts += m_wrap_at;
789     }
790
791     m_buffer_tail_timestamp = ts;
792
793     m_dll_e2=m_update_period * (double)m_nominal_rate;
794     m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2);
795
796     EXIT_CRITICAL_SECTION;
797
798     debugOutput(DEBUG_LEVEL_VERBOSE, "for (%p) to "TIMESTAMP_FORMAT_SPEC" => "
799                                           TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC", DLL2=%f, RATE=%f\n",
800                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, getRate());
801
802 }
803
804 /**
805  * @brief Synchronize the buffer head to a specified timestamp
806  *
807  * Try to synchronize the buffer head to a specific timestamp. This
808  * can mean adding or removing samples to/from the buffer such that
809  * the buffer head aligns with the specified timestamp. The alignment
810  * is within ts +/- Tsample/2
811  *
812  * @param target the timestamp to align to
813  * @return true if alignment succeeded, false if not
814  */
815 bool
816 TimestampedBuffer::syncBufferHeadToTimestamp(ffado_timestamp_t target)
817 {
818     uint64_t ts_head;
819     uint64_t ts_target=(uint64_t)target;
820     signed int fc;
821     int32_t lag_ticks;
822     float lag_frames;
823
824     ffado_timestamp_t ts_head_tmp;
825     getBufferHeadTimestamp(&ts_head_tmp, &fc);
826     ts_head=(uint64_t)ts_head_tmp;
827     // if target > ts_head then the wanted buffer head timestamp
828     // is later than the actual. This means that we (might) have to drop
829     // some frames.
830     lag_ticks=diffTicks(ts_target, ts_head);
831     float rate=getRate();
832    
833     assert(rate!=0.0);
834
835     lag_frames=(((float)lag_ticks)/rate);
836    
837     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): HEAD=%llu, TS=%llu, diff=%ld = %10.5f frames (rate=%10.5f)\n",
838                                       this, ts_head, ts_target, lag_ticks, lag_frames, rate);
839
840     if (lag_frames>=1.0) {
841         // the buffer head is too early
842         // ditch frames until the buffer head is on time
843         char dummy[getBytesPerFrame()]; // one frame of garbage
844         int frames_to_ditch=(int)roundf(lag_frames);
845         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): ditching %d frames (@ ts=%lld)\n",this,frames_to_ditch,ts_target);
846        
847         while (frames_to_ditch--) {
848             readFrames(1, dummy);
849         }
850        
851     } else if (lag_frames<=-1.0) {
852         // the buffer head is too late
853         // add some padding frames
854         int frames_to_add=(int)roundf(lag_frames);
855         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): adding %d frames (@ ts=%lld)\n",this,-frames_to_add,ts_target);
856        
857         while (frames_to_add++) {
858              writeDummyFrame();
859         }
860     }
861     getBufferHeadTimestamp(&ts_head_tmp, &fc);
862     ts_head=(uint64_t)ts_head_tmp;
863     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): new HEAD=%llu, fc=%d, target=%llu, new diff=%lld\n",
864                                       this, ts_head, fc, ts_target, diffTicks(ts_target, ts_head));
865     // FIXME: of course this doesn't always succeed
866     return true;
867 }
868
869 /**
870  * @brief Synchronize the buffer tail to a specified timestamp
871  *
872  * Try to synchronize the buffer tail to a specific timestamp. This
873  * can mean adding or removing samples to/from the buffer such that
874  * the buffer tail aligns with the specified timestamp. The alignment
875  * is within ts +/- Tsample/2
876  *
877  * @param target the timestamp to align to
878  * @return true if alignment succeeded, false if not
879  */
880 bool
881 TimestampedBuffer::syncBufferTailToTimestamp(ffado_timestamp_t target)
882 {
883     uint64_t ts_tail;
884     uint64_t ts_target=(uint64_t)target;
885     signed int fc;
886     int32_t lag_ticks;
887     float lag_frames;
888
889     debugWarning("Untested\n");
890    
891     ffado_timestamp_t ts_tail_tmp;
892     getBufferTailTimestamp(&ts_tail_tmp, &fc);
893     ts_tail=(uint64_t)ts_tail_tmp;
894     // if target < ts_tail then the wanted buffer head timestamp
895     // is later than the actual. This means that we (might) have to drop
896     // some frames.
897     lag_ticks=diffTicks(ts_tail, ts_target);
898     float rate=getRate();
899    
900     assert(rate!=0.0);
901
902     lag_frames=(((float)lag_ticks)/rate);
903    
904     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): HEAD=%llu, TS=%llu, diff=%ld = %10.5f frames (rate=%10.5f)\n",
905                                       this, ts_tail, ts_target, lag_ticks, lag_frames, rate);
906
907     if (lag_frames>=1.0) {
908         // the buffer head is too early
909         // ditch frames until the buffer head is on time
910         char dummy[getBytesPerFrame()]; // one frame of garbage
911         int frames_to_ditch=(int)roundf(lag_frames);
912         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): ditching %d frames (@ ts=%lld)\n",this,frames_to_ditch,ts_target);
913        
914         while (frames_to_ditch--) {
915             readFrames(1, dummy);
916         }
917        
918     } else if (lag_frames<=-1.0) {
919         // the buffer head is too late
920         // add some padding frames
921         int frames_to_add=(int)roundf(lag_frames);
922         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): adding %d frames (@ ts=%lld)\n",this,-frames_to_add,ts_target);
923        
924         while (frames_to_add++) {
925              writeDummyFrame();
926         }
927     }
928     getBufferHeadTimestamp(&ts_tail_tmp, &fc);
929     ts_tail=(uint64_t)ts_tail_tmp;
930     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): new HEAD=%llu, fc=%d, target=%llu, new diff=%lld\n",
931                                       this, ts_tail, fc, ts_target, diffTicks(ts_target, ts_tail));
932     // FIXME: of course this doesn't always succeed
933     return true;
934 }
935
936 /**
937  * @brief correct lag
938  *
939  * Try to synchronize the buffer tail to a specific timestamp. This
940  * can mean adding or removing samples to/from the buffer such that
941  * the buffer tail aligns with the specified timestamp. The alignment
942  * is within ts +/- Tsample/2
943  *
944  * @param target the timestamp to align to
945  * @return true if alignment succeeded, false if not
946  */
947 bool
948 TimestampedBuffer::syncCorrectLag(int64_t lag_ticks)
949 {
950     float lag_frames;
951     float rate=getRate();
952     assert(rate!=0.0);
953
954     lag_frames=(((float)lag_ticks)/rate);
955     if (lag_frames >= 1.0) {
956         // the buffer head is too late
957         // add some padding frames
958         int frames_to_add=(int)roundf(lag_frames);
959         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): adding %d frames\n",this,frames_to_add);
960
961         while (frames_to_add++) {
962              writeDummyFrame();
963         }
964     } else if (lag_frames <= -1.0) {
965         // the buffer head is too early
966         // ditch frames until the buffer head is on time
967         char dummy[getBytesPerFrame()]; // one frame of garbage
968         int frames_to_ditch=(int)roundf(lag_frames);
969         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): ditching %d frames\n",this,-frames_to_ditch);
970
971         while (frames_to_ditch--) {
972             readFrames(1, dummy);
973         }
974     }
975     return true;
976 }
977
978 /**
979  * \brief return the timestamp of the first frame in the buffer
980  *
981  * This function returns the timestamp of the very first sample in
982  * the StreamProcessor's buffer. It also returns the framecounter value
983  * for which this timestamp is valid.
984  *
985  * @param ts address to store the timestamp in
986  * @param fc address to store the associated framecounter in
987  */
988 void TimestampedBuffer::getBufferHeadTimestamp(ffado_timestamp_t *ts, signed int *fc) {
989     // NOTE: this is still ok with threads, because we use *fc to compute
990     //       the timestamp
991     *fc = m_framecounter;
992     *ts = getTimestampFromTail(*fc);
993 }
994
995 /**
996  * \brief return the timestamp of the last frame in the buffer
997  *
998  * This function returns the timestamp of the last frame in
999  * the StreamProcessor's buffer. It also returns the framecounter
1000  * value for which this timestamp is valid.
1001  *
1002  * @param ts address to store the timestamp in
1003  * @param fc address to store the associated framecounter in
1004  */
1005 void TimestampedBuffer::getBufferTailTimestamp(ffado_timestamp_t *ts, signed int *fc) {
1006     // NOTE: this is still ok with threads, because we use *fc to compute
1007     //       the timestamp
1008     *fc = m_framecounter;
1009     *ts = getTimestampFromTail(0);
1010 }
1011
1012 /**
1013  * @brief Get timestamp for a specific position from the buffer tail
1014  *
1015  * Returns the timestamp for a position that is nframes earlier than the
1016  * buffer tail
1017  *
1018  * @param nframes number of frames
1019  * @return timestamp value
1020  */
1021 ffado_timestamp_t TimestampedBuffer::getTimestampFromTail(int nframes)
1022 {
1023     // ts(x) = m_buffer_tail_timestamp -
1024     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*(x)
1025     ffado_timestamp_t timestamp;
1026     timestamp = m_buffer_tail_timestamp;
1027
1028     timestamp -= (ffado_timestamp_t)((nframes) * m_current_rate);
1029
1030     if(timestamp >= m_wrap_at) {
1031         timestamp -= m_wrap_at;
1032     } else if(timestamp < 0) {
1033         timestamp += m_wrap_at;
1034     }
1035
1036     return timestamp;
1037 }
1038
1039 /**
1040  * @brief Get timestamp for a specific position from the buffer head
1041  *
1042  * Returns the timestamp for a position that is nframes later than the
1043  * buffer head
1044  *
1045  * @param nframes number of frames
1046  * @return timestamp value
1047  */
1048 ffado_timestamp_t TimestampedBuffer::getTimestampFromHead(int nframes)
1049 {
1050     return getTimestampFromTail(m_framecounter-nframes);
1051 }
1052
1053 /**
1054  * Resets the frame counter, in a atomic way. This
1055  * is thread safe.
1056  */
1057 void TimestampedBuffer::resetFrameCounter() {
1058     ENTER_CRITICAL_SECTION;
1059     m_framecounter = 0;
1060     EXIT_CRITICAL_SECTION;
1061 }
1062
1063 /**
1064  * Decrements the frame counter in a thread safe way.
1065  *
1066  * @param nbframes number of frames to decrement
1067  */
1068 void TimestampedBuffer::decrementFrameCounter(int nbframes) {
1069     ENTER_CRITICAL_SECTION;
1070     m_framecounter -= nbframes;
1071     EXIT_CRITICAL_SECTION;
1072 }
1073
1074 /**
1075  * Increments the frame counter in a thread safe way.
1076  * Also updates the timestamp.
1077  *
1078  * @note the offsets defined by setTicksOffset and setFrameOffset
1079  *       are added here.
1080  *
1081  * @param nbframes the number of frames to add
1082  * @param new_timestamp the new timestamp
1083  */
1084 void TimestampedBuffer::incrementFrameCounter(int nbframes, ffado_timestamp_t new_timestamp) {
1085
1086     // add the offsets
1087     ffado_timestamp_t diff;
1088    
1089     ENTER_CRITICAL_SECTION;
1090     diff = m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
1091     EXIT_CRITICAL_SECTION;
1092
1093     if (diff < 0) diff += m_wrap_at;
1094
1095     ffado_timestamp_t ts = new_timestamp;
1096     ts += m_tick_offset;
1097
1098     if (ts >= m_wrap_at) {
1099         ts -= m_wrap_at;
1100     } else if (ts < 0) {
1101         ts += m_wrap_at;
1102     }
1103
1104 #ifdef DEBUG
1105     if (new_timestamp >= m_wrap_at) {
1106         debugWarning("timestamp not wrapped: "TIMESTAMP_FORMAT_SPEC"\n", new_timestamp);
1107     }
1108     if ((ts >= m_wrap_at) || (ts < 0 )) {
1109         debugWarning("ts not wrapped correctly: "TIMESTAMP_FORMAT_SPEC"\n",ts);
1110     }
1111 #endif
1112 // FIXME: JMW: at some points during startup the timestamp doesn't change.
1113 // This still needs to be verified in more detail. 
1114 // if (ts>m_buffer_tail_timestamp-1 && ts<m_buffer_tail_timestamp+1) {
1115 //   ENTER_CRITICAL_SECTION;
1116 //   m_framecounter += nbframes;
1117 //   EXIT_CRITICAL_SECTION;
1118 //   return;
1119 // }
1120     ffado_timestamp_t pred_buffer_next_tail_timestamp;
1121     if(nbframes == m_update_period) {
1122         pred_buffer_next_tail_timestamp = m_buffer_next_tail_timestamp;
1123     } else {
1124         debugOutput( DEBUG_LEVEL_VERBOSE,
1125                      "Number of frames (%u) != update period (%u)\n",
1126                      nbframes, m_update_period );
1127         // calculate the predicted timestamp for nframes (instead of m_update_period)
1128         // after the previous update.
1129         float rel_step = ((float)nbframes)/((float)m_update_period);
1130         ENTER_CRITICAL_SECTION; // FIXME: do we need these?
1131         ffado_timestamp_t corrected_step = (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp) * rel_step;
1132         pred_buffer_next_tail_timestamp = m_buffer_tail_timestamp + corrected_step;
1133         EXIT_CRITICAL_SECTION;
1134        
1135         debugOutput( DEBUG_LEVEL_VERBOSE,
1136                      "Updated ("TIMESTAMP_FORMAT_SPEC","TIMESTAMP_FORMAT_SPEC") to ("TIMESTAMP_FORMAT_SPEC","TIMESTAMP_FORMAT_SPEC")\n",
1137                      m_buffer_tail_timestamp, m_buffer_next_tail_timestamp,
1138                      m_buffer_tail_timestamp, pred_buffer_next_tail_timestamp);
1139     }
1140    
1141     // the difference between the given TS and the one predicted for this time instant
1142     // this is the error for the DLL
1143     diff = ts - pred_buffer_next_tail_timestamp;
1144
1145     // check whether the update is within the allowed bounds
1146     const float max_deviation = (50.0/100.0); // maximal relative difference considered normal
1147     ffado_timestamp_t one_update_step = nbframes * getRate();
1148     ffado_timestamp_t max_abs_diff = one_update_step * (1.0 + max_deviation);
1149    
1150     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " nbframes: %d, m_update_period: %d \n", nbframes, m_update_period);
1151     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " tail TS: "TIMESTAMP_FORMAT_SPEC", next tail TS: "TIMESTAMP_FORMAT_SPEC"\n",
1152                                           m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
1153     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " new TS: "TIMESTAMP_FORMAT_SPEC", wrapped new TS: "TIMESTAMP_FORMAT_SPEC"\n",
1154                                           new_timestamp, ts);
1155
1156     if (diff > max_abs_diff) {
1157         debugShowBackLogLines(100);
1158         debugWarning("(%p) difference rather large (+): diff="TIMESTAMP_FORMAT_SPEC", max="TIMESTAMP_FORMAT_SPEC", "TIMESTAMP_FORMAT_SPEC", "TIMESTAMP_FORMAT_SPEC"\n",
1159             this, diff, max_abs_diff, ts, pred_buffer_next_tail_timestamp);
1160     } else if (diff < -max_abs_diff) {
1161         debugShowBackLogLines(100);
1162         debugWarning("(%p) difference rather large (-): diff="TIMESTAMP_FORMAT_SPEC", max="TIMESTAMP_FORMAT_SPEC", "TIMESTAMP_FORMAT_SPEC", "TIMESTAMP_FORMAT_SPEC"\n",
1163             this, diff, -max_abs_diff, ts, pred_buffer_next_tail_timestamp);
1164     }
1165
1166     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p): diff="TIMESTAMP_FORMAT_SPEC" ",
1167                 this, diff);
1168
1169     double err = diff;
1170
1171     debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "diff2="TIMESTAMP_FORMAT_SPEC" err=%f\n",
1172                     diff, err);
1173     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "B: FC=%10u, TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC"\n",
1174                     m_framecounter, m_buffer_tail_timestamp, pred_buffer_next_tail_timestamp);
1175
1176     ENTER_CRITICAL_SECTION;
1177     m_framecounter += nbframes;
1178     m_buffer_tail_timestamp = pred_buffer_next_tail_timestamp;
1179     m_buffer_next_tail_timestamp = pred_buffer_next_tail_timestamp + (ffado_timestamp_t)(m_dll_b * err + m_dll_e2);
1180     m_dll_e2 += m_dll_c*err;
1181
1182     if (m_buffer_next_tail_timestamp >= m_wrap_at) {
1183         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Unwrapping next tail timestamp: "TIMESTAMP_FORMAT_SPEC"",
1184                 m_buffer_next_tail_timestamp);
1185
1186         m_buffer_next_tail_timestamp -= m_wrap_at;
1187
1188         debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, " => "TIMESTAMP_FORMAT_SPEC"\n",
1189                 m_buffer_next_tail_timestamp);
1190
1191     }
1192     m_current_rate = calculateRate();
1193     EXIT_CRITICAL_SECTION;
1194
1195     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "A: TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC", DLLe2=%f, RATE=%f\n",
1196                 m_buffer_tail_timestamp, m_buffer_next_tail_timestamp, m_dll_e2, m_current_rate);
1197
1198
1199     if(m_buffer_tail_timestamp>=m_wrap_at) {
1200         debugError("Wrapping failed for m_buffer_tail_timestamp! "TIMESTAMP_FORMAT_SPEC"\n",m_buffer_tail_timestamp);
1201         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN="TIMESTAMP_FORMAT_SPEC", TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC"\n",
1202                     ts, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
1203
1204     }
1205     if(m_buffer_next_tail_timestamp>=m_wrap_at) {
1206         debugError("Wrapping failed for m_buffer_next_tail_timestamp! "TIMESTAMP_FORMAT_SPEC"\n",m_buffer_next_tail_timestamp);
1207         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN="TIMESTAMP_FORMAT_SPEC", TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC"\n",
1208                     ts, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
1209     }
1210    
1211     if(m_buffer_tail_timestamp==m_buffer_next_tail_timestamp) {
1212         debugError("Current and next timestamps are equal: "TIMESTAMP_FORMAT_SPEC" "TIMESTAMP_FORMAT_SPEC"\n",
1213                    m_buffer_tail_timestamp,m_buffer_next_tail_timestamp);
1214    
1215     }
1216
1217     // this DLL allows the calculation of any sample timestamp relative to the buffer tail,
1218     // to the next period and beyond (through extrapolation)
1219     //
1220     // ts(x) = m_buffer_tail_timestamp +
1221     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*x
1222
1223 }
1224
1225 /**
1226  * @brief Print status info.
1227  */
1228 void TimestampedBuffer::dumpInfo() {
1229
1230     ffado_timestamp_t ts_head;
1231     signed int fc;
1232     getBufferHeadTimestamp(&ts_head,&fc);
1233
1234 #ifdef DEBUG
1235     ffado_timestamp_t diff=(ffado_timestamp_t)ts_head - (ffado_timestamp_t)m_buffer_tail_timestamp;
1236 #endif
1237
1238     debugOutputShort( DEBUG_LEVEL_NORMAL, "  TimestampedBuffer (%p): %04d frames, %04d events\n",
1239                                           this, m_framecounter, getBufferFill());
1240     debugOutputShort( DEBUG_LEVEL_NORMAL, "   Timestamps           : head: "TIMESTAMP_FORMAT_SPEC", Tail: "TIMESTAMP_FORMAT_SPEC", Next tail: "TIMESTAMP_FORMAT_SPEC"\n",
1241                                           ts_head, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
1242     debugOutputShort( DEBUG_LEVEL_NORMAL, "    Head - Tail         : "TIMESTAMP_FORMAT_SPEC"\n", diff);
1243     debugOutputShort( DEBUG_LEVEL_NORMAL, "   DLL Rate             : %f (%f)\n", m_dll_e2, m_dll_e2/m_update_period);
1244 }
1245
1246 } // end of namespace Util
Note: See TracBrowser for help on using the browser.