root/branches/ppalmers-streaming/src/libutil/TimestampedBuffer.cpp

Revision 712, 40.4 kB (checked in by ppalmers, 16 years ago)

almost there...

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