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

Revision 720, 41.3 kB (checked in by ppalmers, 15 years ago)

first working version of the reworked streaming code

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 (rate<0.0) debugError("rate < 0! (%f)\n",rate);
152     if (fabsf(m_nominal_rate - rate)>(m_nominal_rate*0.1)) {
153         debugWarning("(%p) rate (%10.5f) more that 10%% off nominal (rate=%10.5f, diff="TIMESTAMP_FORMAT_SPEC", update_period=%d)\n",
154                      this, rate,m_nominal_rate,diff, m_update_period);
155
156         return m_nominal_rate;
157     } else {
158         return rate;
159     }
160 }
161
162 /**
163  * \brief Sets the size of the events
164  * @param s event size in bytes
165  * @return true if successful
166  */
167 bool TimestampedBuffer::setEventSize(unsigned int s) {
168     m_event_size=s;
169
170     m_bytes_per_frame=m_event_size*m_events_per_frame;
171     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
172
173     return true;
174 }
175
176 /**
177  * \brief Sets the number of events per frame
178  * @param n number of events per frame
179  * @return true if successful
180  */
181 bool TimestampedBuffer::setEventsPerFrame(unsigned int n) {
182     m_events_per_frame=n;
183
184     m_bytes_per_frame=m_event_size*m_events_per_frame;
185     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
186
187     return true;
188 }
189 /**
190  * \brief Sets the buffer size in frames
191  * @param n number frames
192  * @return true if successful
193  */
194 bool TimestampedBuffer::setBufferSize(unsigned int n) {
195     m_buffer_size=n;
196
197     m_bytes_per_frame=m_event_size*m_events_per_frame;
198     m_bytes_per_buffer=m_bytes_per_frame*m_buffer_size;
199
200     return true;
201 }
202
203 /**
204  * Sets the buffer offset in ticks.
205  *
206  * A positive value means that the buffer is 'delayed' for nticks ticks.
207  *
208  * @note These offsets are only used when reading timestamps. Any function
209  *       that returns a timestamp will incorporate this offset.
210  * @param nframes the number of ticks (positive = delay buffer)
211  * @return true if successful
212  */
213 bool TimestampedBuffer::setTickOffset(ffado_timestamp_t nticks) {
214     debugOutput(DEBUG_LEVEL_VERBOSE,"Setting ticks offset to "TIMESTAMP_FORMAT_SPEC"\n",nticks);
215
216     // JMW: I think we need to update the internal DLL state to take account
217     // of the new offset.  Doing so certainly makes for a smoother MOTU
218     // startup.
219     ENTER_CRITICAL_SECTION;
220     m_buffer_tail_timestamp = m_buffer_tail_timestamp - m_tick_offset + nticks;
221     m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2);
222     m_tick_offset=nticks;
223     EXIT_CRITICAL_SECTION;
224
225     return true;
226 }
227
228 /**
229  * \brief Returns the current fill of the buffer
230  *
231  * This returns the buffer fill of the internal ringbuffer. This
232  * can only be used as an indication because it's state is not
233  * guaranteed to be consistent at all times due to threading issues.
234  *
235  * In order to get the number of frames in the buffer, use the
236  * getBufferHeadTimestamp, getBufferTailTimestamp
237  * functions
238  *
239  * @return the internal buffer fill in frames
240  */
241 unsigned int TimestampedBuffer::getBufferFill() {
242     return ffado_ringbuffer_read_space(m_event_buffer)/(m_bytes_per_frame);
243 }
244
245 /**
246  * \brief Initializes the TimestampedBuffer
247  *
248  * Initializes the TimestampedBuffer, should be called before anything else
249  * is done.
250  *
251  * @return true if successful
252  */
253 bool TimestampedBuffer::init() {
254     return true;
255 }
256
257 /**
258  * \brief Resets the TimestampedBuffer
259  *
260  * Resets the TimestampedBuffer, clearing the buffers and counters.
261  * (not true yet: Also resets the DLL to the nominal values.)
262  *
263  * \note when this is called, you should make sure that the buffer
264  *       tail timestamp gets set before continuing
265  *
266  * @return true if successful
267  */
268 bool TimestampedBuffer::clearBuffer() {
269     debugOutput(DEBUG_LEVEL_VERBOSE, "Clearing buffer\n");
270     ffado_ringbuffer_reset(m_event_buffer);
271     resetFrameCounter();
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         // while disabled, we don't update the DLL, nor do we write frames
382         // we just set the correct timestamp for the frames
383         setBufferTailTimestamp(ts);
384     } else {
385         // add the data payload to the ringbuffer
386         size_t written = ffado_ringbuffer_write(m_event_buffer, data, write_size);
387         if (written < write_size)
388         {
389             debugWarning("ringbuffer full, %u, %u\n", write_size, written);
390             return false;
391         }
392         incrementFrameCounter(nframes,ts);
393     }
394     return true;
395 }
396
397 /**
398  * @brief Preload frames into the buffer
399  *
400  * Preload \ref nframes of frames from the buffer pointed to by \ref data to the
401  * internal ringbuffer. Does not care about transparency. Keeps the buffer head or tail
402  * timestamp constant.
403  *
404  * @note not thread safe
405  *
406  * @param nframes number of frames to copy
407  * @param data pointer to the frame buffer
408  * @param keep_head_ts if true, keep the head timestamp constant. If false, keep the
409  *                     tail timestamp constant.
410  * @return true if successful
411  */
412 bool TimestampedBuffer::preloadFrames(unsigned int nframes, char *data, bool keep_head_ts) {
413     unsigned int write_size = nframes * m_event_size * m_events_per_frame;
414     // add the data payload to the ringbuffer
415     size_t written = ffado_ringbuffer_write(m_event_buffer, data, write_size);
416     if (written < write_size)
417     {
418         debugWarning("ringbuffer full, request: %u, actual: %u\n", write_size, written);
419         return false;
420     }
421    
422     // make sure the head timestamp remains identical
423     signed int fc;
424     ffado_timestamp_t ts;
425
426     if (keep_head_ts) {
427         getBufferHeadTimestamp(&ts, &fc);
428     } else {
429         getBufferTailTimestamp(&ts, &fc);
430     }
431     // update frame counter
432     m_framecounter += nframes;
433     if (keep_head_ts) {
434         setBufferHeadTimestamp(ts);
435     } else {
436         setBufferTailTimestamp(ts);
437     }
438
439     return true;
440 }
441
442 /**
443  * @brief Drop frames from the head of the buffer
444  *
445  * drops \ref nframes of frames from the head of internal buffer
446  *
447  * @param nframes number of frames to drop
448  * @return true if successful
449  */
450 bool TimestampedBuffer::dropFrames(unsigned int nframes) {
451
452     unsigned int read_size=nframes*m_event_size*m_events_per_frame;
453
454     ffado_ringbuffer_read_advance(m_event_buffer, read_size);
455     decrementFrameCounter(nframes);
456     return true;
457 }
458
459 /**
460  * @brief Read frames from the buffer
461  *
462  * Copies \ref nframes of frames from the internal buffer to the data buffer pointed
463  * to by \ref data.
464  *
465  * @param nframes number of frames to copy
466  * @param data pointer to the frame buffer
467  * @return true if successful
468  */
469 bool TimestampedBuffer::readFrames(unsigned int nframes, char *data) {
470
471     unsigned int read_size=nframes*m_event_size*m_events_per_frame;
472
473     if (m_transparent) {
474         return true; // FIXME: the data still doesn't make sense!
475     } else {
476         // get the data payload to the ringbuffer
477         if ((ffado_ringbuffer_read(m_event_buffer,data,read_size)) < read_size)
478         {
479             debugWarning("readFrames buffer underrun\n");
480             return false;
481         }
482         decrementFrameCounter(nframes);
483     }
484     return true;
485 }
486
487 /**
488  * @brief Performs block processing write of frames
489  *
490  * This function allows for zero-copy writing into the ringbuffer.
491  * It calls the client's processWriteBlock function to write frames
492  * into the internal buffer's data area, in a thread safe fashion.
493  *
494  * It also updates the timestamp.
495  *
496  * @param nbframes number of frames to process
497  * @param ts timestamp of the last frame written to the buffer
498  * @return true if successful
499  */
500 bool TimestampedBuffer::blockProcessWriteFrames(unsigned int nbframes, ffado_timestamp_t ts) {
501
502     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Transferring period...\n");
503     int xrun;
504     unsigned int offset=0;
505
506     ffado_ringbuffer_data_t vec[2];
507     // we received one period of frames
508     // this is period_size*dimension of events
509     unsigned int events2write=nbframes*m_events_per_frame;
510     unsigned int bytes2write=events2write*m_event_size;
511
512     /* write events2write bytes to the ringbuffer
513     *  first see if it can be done in one read.
514     *  if so, ok.
515     *  otherwise write up to a multiple of clusters directly to the buffer
516     *  then do the buffer wrap around using ringbuffer_write
517     *  then write the remaining data directly to the buffer in a third pass
518     *  Make sure that we cannot end up on a non-cluster aligned position!
519     */
520     unsigned int cluster_size=m_events_per_frame*m_event_size;
521
522     while(bytes2write>0) {
523         int byteswritten=0;
524
525         unsigned int frameswritten=(nbframes*cluster_size-bytes2write)/cluster_size;
526         offset=frameswritten;
527
528         ffado_ringbuffer_get_write_vector(m_event_buffer, vec);
529
530         if(vec[0].len==0) { // this indicates a full event buffer
531             debugError("Event buffer overrun in buffer %p, fill: %u, bytes2write: %u \n",
532                        this, ffado_ringbuffer_read_space(m_event_buffer), bytes2write);
533             debugShowBackLog();
534             return false;
535         }
536
537         /* if we don't take care we will get stuck in an infinite loop
538         * because we align to a cluster boundary later
539         * the remaining nb of bytes in one write operation can be
540         * smaller than one cluster
541         * this can happen because the ringbuffer size is always a power of 2
542         */
543         if(vec[0].len<cluster_size) {
544
545             // encode to the temporary buffer
546             xrun = m_Client->processWriteBlock(m_cluster_buffer, 1, offset);
547
548             if(xrun<0) {
549                 // xrun detected
550                 debugError("Frame buffer underrun in buffer %p\n",this);
551                 return false;
552             }
553
554             // use the ringbuffer function to write one cluster
555             // the write function handles the wrap around.
556             ffado_ringbuffer_write(m_event_buffer,
557                          m_cluster_buffer,
558                          cluster_size);
559
560             // we advanced one cluster_size
561             bytes2write-=cluster_size;
562
563         } else { //
564
565             if(bytes2write>vec[0].len) {
566                 // align to a cluster boundary
567                 byteswritten=vec[0].len-(vec[0].len%cluster_size);
568             } else {
569                 byteswritten=bytes2write;
570             }
571
572             xrun = m_Client->processWriteBlock(vec[0].buf,
573                          byteswritten/cluster_size,
574                          offset);
575
576             if(xrun<0) {
577                     // xrun detected
578                 debugError("Frame buffer underrun in buffer %p\n",this);
579                 return false; // FIXME: return false ?
580             }
581
582             ffado_ringbuffer_write_advance(m_event_buffer, byteswritten);
583             bytes2write -= byteswritten;
584         }
585
586         // the bytes2write should always be cluster aligned
587         assert(bytes2write%cluster_size==0);
588
589     }
590
591     incrementFrameCounter(nbframes,ts);
592
593     return true;
594
595 }
596
597 /**
598  * @brief Performs block processing read of frames
599  *
600  * This function allows for zero-copy reading from the ringbuffer.
601  * It calls the client's processReadBlock function to read frames
602  * directly from the internal buffer's data area, in a thread safe
603  * fashion.
604  *
605  * @param nbframes number of frames to process
606  * @return true if successful
607  */
608 bool TimestampedBuffer::blockProcessReadFrames(unsigned int nbframes) {
609
610     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Reading %u from buffer (%p)...\n", nbframes, this);
611
612     int xrun;
613     unsigned int offset=0;
614
615     ffado_ringbuffer_data_t vec[2];
616     // we received one period of frames on each connection
617     // this is period_size*dimension of events
618
619     unsigned int events2read=nbframes*m_events_per_frame;
620     unsigned int bytes2read=events2read*m_event_size;
621     /* read events2read bytes from the ringbuffer
622     *  first see if it can be done in one read.
623     *  if so, ok.
624     *  otherwise read up to a multiple of clusters directly from the buffer
625     *  then do the buffer wrap around using ringbuffer_read
626     *  then read the remaining data directly from the buffer in a third pass
627     *  Make sure that we cannot end up on a non-cluster aligned position!
628     */
629     unsigned int cluster_size=m_events_per_frame*m_event_size;
630
631     while(bytes2read>0) {
632         unsigned int framesread=(nbframes*cluster_size-bytes2read)/cluster_size;
633         offset=framesread;
634
635         int bytesread=0;
636
637         ffado_ringbuffer_get_read_vector(m_event_buffer, vec);
638
639         if(vec[0].len==0) { // this indicates an empty event buffer
640             debugError("Event buffer underrun in buffer %p\n",this);
641             return false;
642         }
643
644         /* if we don't take care we will get stuck in an infinite loop
645         * because we align to a cluster boundary later
646         * the remaining nb of bytes in one read operation can be smaller than one cluster
647         * this can happen because the ringbuffer size is always a power of 2
648                 */
649         if(vec[0].len<cluster_size) {
650             // use the ringbuffer function to read one cluster
651             // the read function handles wrap around
652             ffado_ringbuffer_read(m_event_buffer,m_cluster_buffer,cluster_size);
653
654             assert(m_Client);
655             xrun = m_Client->processReadBlock(m_cluster_buffer, 1, offset);
656
657             if(xrun<0) {
658                 // xrun detected
659                 debugError("Frame buffer overrun in buffer %p\n",this);
660                     return false;
661             }
662
663             // we advanced one cluster_size
664             bytes2read-=cluster_size;
665
666         } else { //
667
668             if(bytes2read>vec[0].len) {
669                 // align to a cluster boundary
670                 bytesread=vec[0].len-(vec[0].len%cluster_size);
671             } else {
672                 bytesread=bytes2read;
673             }
674
675             assert(m_Client);
676             xrun = m_Client->processReadBlock(vec[0].buf, bytesread/cluster_size, offset);
677
678             if(xrun<0) {
679                 // xrun detected
680                 debugError("Frame buffer overrun in buffer %p\n",this);
681                 return false;
682             }
683
684             ffado_ringbuffer_read_advance(m_event_buffer, bytesread);
685             bytes2read -= bytesread;
686         }
687
688         // the bytes2read should always be cluster aligned
689         assert(bytes2read%cluster_size==0);
690     }
691
692     decrementFrameCounter(nbframes);
693
694     return true;
695 }
696
697 /**
698  * @brief Sets the buffer tail timestamp.
699  *
700  * Set the buffer tail timestamp to \ref new_timestamp. This will recalculate
701  * the internal state such that the buffer's timeframe starts at
702  * \ref new_timestamp.
703  *
704  * This is thread safe.
705  *
706  * @note considers offsets
707  *
708  * @param new_timestamp
709  */
710 void TimestampedBuffer::setBufferTailTimestamp(ffado_timestamp_t new_timestamp) {
711
712     // add the offsets
713     ffado_timestamp_t ts=new_timestamp;
714     ts += m_tick_offset;
715
716     if (ts >= m_wrap_at) {
717         ts -= m_wrap_at;
718     } else if (ts < 0) {
719         ts += m_wrap_at;
720     }
721
722 #ifdef DEBUG
723     if (new_timestamp >= m_wrap_at) {
724         debugWarning("timestamp not wrapped: "TIMESTAMP_FORMAT_SPEC"\n",new_timestamp);
725     }
726     if ((ts >= m_wrap_at) || (ts < 0 )) {
727         debugWarning("ts not wrapped correctly: "TIMESTAMP_FORMAT_SPEC"\n",ts);
728     }
729 #endif
730
731     ENTER_CRITICAL_SECTION;
732
733     m_buffer_tail_timestamp = ts;
734
735     m_dll_e2=m_update_period * (double)m_nominal_rate;
736     m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2);
737
738     EXIT_CRITICAL_SECTION;
739
740     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "for (%p) to "
741                                           TIMESTAMP_FORMAT_SPEC" => "TIMESTAMP_FORMAT_SPEC", NTS="
742                                           TIMESTAMP_FORMAT_SPEC", DLL2=%f, RATE=%f\n",
743                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, getRate());
744
745 }
746
747 /**
748  * @brief Sets the buffer head timestamp.
749  *
750  * Set the buffer tail timestamp such that the buffer head timestamp becomes
751  * \ref new_timestamp. This does not consider offsets, because it's use is to
752  * make sure the following is true after setBufferHeadTimestamp(x):
753  *   x == getBufferHeadTimestamp()
754  *
755  * This is thread safe.
756  *
757  * @param new_timestamp
758  */
759 void TimestampedBuffer::setBufferHeadTimestamp(ffado_timestamp_t new_timestamp) {
760
761 #ifdef DEBUG
762     if (new_timestamp >= m_wrap_at) {
763         debugWarning("timestamp not wrapped: "TIMESTAMP_FORMAT_SPEC"\n",new_timestamp);
764     }
765 #endif
766
767     ffado_timestamp_t ts = new_timestamp;
768
769     ENTER_CRITICAL_SECTION;
770
771     // add the time
772     ts += (ffado_timestamp_t)(m_nominal_rate * (float)m_framecounter);
773
774     if (ts >= m_wrap_at) {
775         ts -= m_wrap_at;
776     } else if (ts < 0) {
777         ts += m_wrap_at;
778     }
779
780     m_buffer_tail_timestamp = ts;
781
782     m_dll_e2=m_update_period * (double)m_nominal_rate;
783     m_buffer_next_tail_timestamp = (ffado_timestamp_t)((double)m_buffer_tail_timestamp + m_dll_e2);
784
785     EXIT_CRITICAL_SECTION;
786
787     debugOutput(DEBUG_LEVEL_VERBOSE, "for (%p) to "TIMESTAMP_FORMAT_SPEC" => "
788                                           TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC", DLL2=%f, RATE=%f\n",
789                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, getRate());
790
791 }
792
793 /**
794  * @brief Synchronize the buffer head to a specified timestamp
795  *
796  * Try to synchronize the buffer head to a specific timestamp. This
797  * can mean adding or removing samples to/from the buffer such that
798  * the buffer head aligns with the specified timestamp. The alignment
799  * is within ts +/- Tsample/2
800  *
801  * @param target the timestamp to align to
802  * @return true if alignment succeeded, false if not
803  */
804 bool
805 TimestampedBuffer::syncBufferHeadToTimestamp(ffado_timestamp_t target)
806 {
807     uint64_t ts_head;
808     uint64_t ts_target=(uint64_t)target;
809     signed int fc;
810     int32_t lag_ticks;
811     float lag_frames;
812
813     ffado_timestamp_t ts_head_tmp;
814     getBufferHeadTimestamp(&ts_head_tmp, &fc);
815     ts_head=(uint64_t)ts_head_tmp;
816     // if target > ts_head then the wanted buffer head timestamp
817     // is later than the actual. This means that we (might) have to drop
818     // some frames.
819     lag_ticks=diffTicks(ts_target, ts_head);
820     float rate=getRate();
821    
822     assert(rate!=0.0);
823
824     lag_frames=(((float)lag_ticks)/rate);
825    
826     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): HEAD=%llu, TS=%llu, diff=%ld = %10.5f frames (rate=%10.5f)\n",
827                                       this, ts_head, ts_target, lag_ticks, lag_frames, rate);
828
829     if (lag_frames>=1.0) {
830         // the buffer head is too early
831         // ditch frames until the buffer head is on time
832         char dummy[getBytesPerFrame()]; // one frame of garbage
833         int frames_to_ditch=(int)roundf(lag_frames);
834         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): ditching %d frames (@ ts=%lld)\n",this,frames_to_ditch,ts_target);
835        
836         while (frames_to_ditch--) {
837             readFrames(1, dummy);
838         }
839        
840     } else if (lag_frames<=-1.0) {
841         // the buffer head is too late
842         // add some padding frames
843         int frames_to_add=(int)roundf(lag_frames);
844         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): adding %d frames (@ ts=%lld)\n",this,-frames_to_add,ts_target);
845        
846         while (frames_to_add++) {
847              writeDummyFrame();
848         }
849     }
850     getBufferHeadTimestamp(&ts_head_tmp, &fc);
851     ts_head=(uint64_t)ts_head_tmp;
852     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): new HEAD=%llu, fc=%d, target=%llu, new diff=%lld\n",
853                                       this, ts_head, fc, ts_target, diffTicks(ts_target, ts_head));
854     // FIXME: of course this doesn't always succeed
855     return true;
856 }
857
858 /**
859  * @brief Synchronize the buffer tail to a specified timestamp
860  *
861  * Try to synchronize the buffer tail to a specific timestamp. This
862  * can mean adding or removing samples to/from the buffer such that
863  * the buffer tail aligns with the specified timestamp. The alignment
864  * is within ts +/- Tsample/2
865  *
866  * @param target the timestamp to align to
867  * @return true if alignment succeeded, false if not
868  */
869 bool
870 TimestampedBuffer::syncBufferTailToTimestamp(ffado_timestamp_t target)
871 {
872     uint64_t ts_tail;
873     uint64_t ts_target=(uint64_t)target;
874     signed int fc;
875     int32_t lag_ticks;
876     float lag_frames;
877
878     debugWarning("Untested\n");
879    
880     ffado_timestamp_t ts_tail_tmp;
881     getBufferTailTimestamp(&ts_tail_tmp, &fc);
882     ts_tail=(uint64_t)ts_tail_tmp;
883     // if target < ts_tail then the wanted buffer head timestamp
884     // is later than the actual. This means that we (might) have to drop
885     // some frames.
886     lag_ticks=diffTicks(ts_tail, ts_target);
887     float rate=getRate();
888    
889     assert(rate!=0.0);
890
891     lag_frames=(((float)lag_ticks)/rate);
892    
893     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): HEAD=%llu, TS=%llu, diff=%ld = %10.5f frames (rate=%10.5f)\n",
894                                       this, ts_tail, ts_target, lag_ticks, lag_frames, rate);
895
896     if (lag_frames>=1.0) {
897         // the buffer head is too early
898         // ditch frames until the buffer head is on time
899         char dummy[getBytesPerFrame()]; // one frame of garbage
900         int frames_to_ditch=(int)roundf(lag_frames);
901         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): ditching %d frames (@ ts=%lld)\n",this,frames_to_ditch,ts_target);
902        
903         while (frames_to_ditch--) {
904             readFrames(1, dummy);
905         }
906        
907     } else if (lag_frames<=-1.0) {
908         // the buffer head is too late
909         // add some padding frames
910         int frames_to_add=(int)roundf(lag_frames);
911         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): adding %d frames (@ ts=%lld)\n",this,-frames_to_add,ts_target);
912        
913         while (frames_to_add++) {
914              writeDummyFrame();
915         }
916     }
917     getBufferHeadTimestamp(&ts_tail_tmp, &fc);
918     ts_tail=(uint64_t)ts_tail_tmp;
919     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): new HEAD=%llu, fc=%d, target=%llu, new diff=%lld\n",
920                                       this, ts_tail, fc, ts_target, diffTicks(ts_target, ts_tail));
921     // FIXME: of course this doesn't always succeed
922     return true;
923 }
924
925 /**
926  * @brief correct lag
927  *
928  * Try to synchronize the buffer tail to a specific timestamp. This
929  * can mean adding or removing samples to/from the buffer such that
930  * the buffer tail aligns with the specified timestamp. The alignment
931  * is within ts +/- Tsample/2
932  *
933  * @param target the timestamp to align to
934  * @return true if alignment succeeded, false if not
935  */
936 bool
937 TimestampedBuffer::syncCorrectLag(int64_t lag_ticks)
938 {
939     float lag_frames;
940     float rate=getRate();
941     assert(rate!=0.0);
942
943     lag_frames=(((float)lag_ticks)/rate);
944     if (lag_frames >= 1.0) {
945         // the buffer head is too late
946         // add some padding frames
947         int frames_to_add=(int)roundf(lag_frames);
948         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): adding %d frames\n",this,frames_to_add);
949
950         while (frames_to_add++) {
951              writeDummyFrame();
952         }
953     } else if (lag_frames <= -1.0) {
954         // the buffer head is too early
955         // ditch frames until the buffer head is on time
956         char dummy[getBytesPerFrame()]; // one frame of garbage
957         int frames_to_ditch=(int)roundf(lag_frames);
958         debugOutput( DEBUG_LEVEL_VERBOSE, "(%p): ditching %d frames\n",this,-frames_to_ditch);
959
960         while (frames_to_ditch--) {
961             readFrames(1, dummy);
962         }
963     }
964     return true;
965 }
966
967 /**
968  * \brief return the timestamp of the first frame in the buffer
969  *
970  * This function returns the timestamp of the very first sample in
971  * the StreamProcessor's buffer. It also returns the framecounter value
972  * for which this timestamp is valid.
973  *
974  * @param ts address to store the timestamp in
975  * @param fc address to store the associated framecounter in
976  */
977 void TimestampedBuffer::getBufferHeadTimestamp(ffado_timestamp_t *ts, signed int *fc) {
978     // NOTE: this is still ok with threads, because we use *fc to compute
979     //       the timestamp
980     *fc = m_framecounter;
981     *ts=getTimestampFromTail(*fc);
982 }
983
984 /**
985  * \brief return the timestamp of the last frame in the buffer
986  *
987  * This function returns the timestamp of the last frame in
988  * the StreamProcessor's buffer. It also returns the framecounter
989  * value for which this timestamp is valid.
990  *
991  * @param ts address to store the timestamp in
992  * @param fc address to store the associated framecounter in
993  */
994 void TimestampedBuffer::getBufferTailTimestamp(ffado_timestamp_t *ts, signed int *fc) {
995     ENTER_CRITICAL_SECTION;
996     *fc = m_framecounter;
997     *ts = m_buffer_tail_timestamp;
998     EXIT_CRITICAL_SECTION;
999 }
1000
1001 /**
1002  * @brief Get timestamp for a specific position from the buffer tail
1003  *
1004  * Returns the timestamp for a position that is nframes earlier than the
1005  * buffer tail
1006  *
1007  * @param nframes number of frames
1008  * @return timestamp value
1009  */
1010 ffado_timestamp_t TimestampedBuffer::getTimestampFromTail(int nframes)
1011 {
1012     // ts(x) = m_buffer_tail_timestamp -
1013     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*(x)
1014     ffado_timestamp_t diff;
1015     float rate;
1016     ffado_timestamp_t timestamp;
1017    
1018     ENTER_CRITICAL_SECTION;
1019
1020     diff=m_buffer_next_tail_timestamp - m_buffer_tail_timestamp;
1021     timestamp=m_buffer_tail_timestamp;
1022    
1023     EXIT_CRITICAL_SECTION;
1024    
1025     if (diff < 0) diff += m_wrap_at;
1026     rate=(float)diff / (float)m_update_period;
1027
1028     timestamp-=(ffado_timestamp_t)((nframes) * 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 #ifdef DEBUG
1096     float rate=(float)diff / (float)m_update_period;
1097 #endif
1098
1099     ffado_timestamp_t ts = new_timestamp;
1100     ts += m_tick_offset;
1101
1102     if (ts >= m_wrap_at) {
1103         ts -= m_wrap_at;
1104     } else if (ts < 0) {
1105         ts += m_wrap_at;
1106     }
1107
1108 #ifdef DEBUG
1109     if (new_timestamp >= m_wrap_at) {
1110         debugWarning("timestamp not wrapped: "TIMESTAMP_FORMAT_SPEC"\n", new_timestamp);
1111     }
1112     if ((ts >= m_wrap_at) || (ts < 0 )) {
1113         debugWarning("ts not wrapped correctly: "TIMESTAMP_FORMAT_SPEC"\n",ts);
1114     }
1115 #endif
1116 // FIXME: JMW: at some points during startup the timestamp doesn't change.
1117 // This still needs to be verified in more detail. 
1118 // if (ts>m_buffer_tail_timestamp-1 && ts<m_buffer_tail_timestamp+1) {
1119 //   ENTER_CRITICAL_SECTION;
1120 //   m_framecounter += nbframes;
1121 //   EXIT_CRITICAL_SECTION;
1122 //   return;
1123 // }
1124     ffado_timestamp_t pred_buffer_next_tail_timestamp;
1125     if(nbframes == m_update_period) {
1126         pred_buffer_next_tail_timestamp = m_buffer_next_tail_timestamp;
1127     } else {
1128         debugOutput( DEBUG_LEVEL_VERBOSE,
1129                      "Number of frames (%u) != update period (%u)\n",
1130                      nbframes, m_update_period );
1131         // calculate the predicted timestamp for nframes (instead of m_update_period)
1132         // after the previous update.
1133         float rel_step = ((float)nbframes)/((float)m_update_period);
1134         ENTER_CRITICAL_SECTION; // FIXME: do we need these?
1135         ffado_timestamp_t corrected_step = (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp) * rel_step;
1136         pred_buffer_next_tail_timestamp = m_buffer_tail_timestamp + corrected_step;
1137         EXIT_CRITICAL_SECTION;
1138        
1139         debugOutput( DEBUG_LEVEL_VERBOSE,
1140                      "Updated ("TIMESTAMP_FORMAT_SPEC","TIMESTAMP_FORMAT_SPEC") to ("TIMESTAMP_FORMAT_SPEC","TIMESTAMP_FORMAT_SPEC")\n",
1141                      m_buffer_tail_timestamp, m_buffer_next_tail_timestamp,
1142                      m_buffer_tail_timestamp, pred_buffer_next_tail_timestamp);
1143        
1144     }
1145    
1146     // the difference between the given TS and the one predicted for this time instant
1147     // this is the error for the DLL
1148     diff = ts - pred_buffer_next_tail_timestamp;
1149
1150     // check whether the update is within the allowed bounds
1151     const float max_deviation = (50.0/100.0); // maximal relative difference considered normal
1152     ffado_timestamp_t one_update_step = nbframes * getRate();
1153     ffado_timestamp_t max_abs_diff = one_update_step * (1.0 + max_deviation);
1154    
1155     if (diff > max_abs_diff) {
1156         debugWarning("(%p) difference rather large (+): diff="TIMESTAMP_FORMAT_SPEC", max="TIMESTAMP_FORMAT_SPEC", "TIMESTAMP_FORMAT_SPEC", "TIMESTAMP_FORMAT_SPEC"\n",
1157             this, diff, max_abs_diff, ts, pred_buffer_next_tail_timestamp);
1158 //         debugShowBackLogLines(40);
1159     } else if (diff < -max_abs_diff) {
1160         debugWarning("(%p) difference rather large (-): diff="TIMESTAMP_FORMAT_SPEC", max="TIMESTAMP_FORMAT_SPEC", "TIMESTAMP_FORMAT_SPEC", "TIMESTAMP_FORMAT_SPEC"\n",
1161             this, diff, -max_abs_diff, ts, pred_buffer_next_tail_timestamp);
1162 //         debugShowBackLogLines(40);
1163     }
1164
1165     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%p): diff="TIMESTAMP_FORMAT_SPEC" ",
1166                 this, diff);
1167
1168     double err = diff;
1169
1170     debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "diff2="TIMESTAMP_FORMAT_SPEC" err=%f\n",
1171                     diff, err);
1172     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "B: FC=%10u, TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC"\n",
1173                     m_framecounter, m_buffer_tail_timestamp, pred_buffer_next_tail_timestamp);
1174
1175     ENTER_CRITICAL_SECTION;
1176     m_framecounter += nbframes;
1177
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_buffer_tail_timestamp=ts;
1181 //    m_buffer_next_tail_timestamp += (ffado_timestamp_t)(m_dll_b * err + m_dll_e2);
1182    
1183     m_dll_e2 += m_dll_c*err;
1184
1185     EXIT_CRITICAL_SECTION;
1186
1187     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "tail for (%p) to "
1188                                           TIMESTAMP_FORMAT_SPEC" => "TIMESTAMP_FORMAT_SPEC", NTS="
1189                                           TIMESTAMP_FORMAT_SPEC", DLL2=%f, RATE=%f\n",
1190                 this, new_timestamp, ts, m_buffer_next_tail_timestamp, m_dll_e2, getRate());
1191
1192     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "U: FC=%10u, TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC"\n",
1193                     m_framecounter, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
1194
1195     ENTER_CRITICAL_SECTION;
1196     if (m_buffer_next_tail_timestamp >= m_wrap_at) {
1197         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Unwrapping next tail timestamp: "TIMESTAMP_FORMAT_SPEC"",
1198                 m_buffer_next_tail_timestamp);
1199
1200         m_buffer_next_tail_timestamp -= m_wrap_at;
1201
1202         debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, " => "TIMESTAMP_FORMAT_SPEC"\n",
1203                 m_buffer_next_tail_timestamp);
1204
1205     }
1206     EXIT_CRITICAL_SECTION;
1207
1208     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "A: TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC", DLLe2=%f, RATE=%f\n",
1209                 m_buffer_tail_timestamp, m_buffer_next_tail_timestamp, m_dll_e2, rate);
1210
1211
1212     if(m_buffer_tail_timestamp>=m_wrap_at) {
1213         debugError("Wrapping failed for m_buffer_tail_timestamp! "TIMESTAMP_FORMAT_SPEC"\n",m_buffer_tail_timestamp);
1214         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN="TIMESTAMP_FORMAT_SPEC", TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC"\n",
1215                     ts, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
1216
1217     }
1218     if(m_buffer_next_tail_timestamp>=m_wrap_at) {
1219         debugError("Wrapping failed for m_buffer_next_tail_timestamp! "TIMESTAMP_FORMAT_SPEC"\n",m_buffer_next_tail_timestamp);
1220         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " IN="TIMESTAMP_FORMAT_SPEC", TS="TIMESTAMP_FORMAT_SPEC", NTS="TIMESTAMP_FORMAT_SPEC"\n",
1221                     ts, m_buffer_tail_timestamp, m_buffer_next_tail_timestamp);
1222     }
1223    
1224     if(m_buffer_tail_timestamp==m_buffer_next_tail_timestamp) {
1225         debugError("Current and next timestamps are equal: "TIMESTAMP_FORMAT_SPEC" "TIMESTAMP_FORMAT_SPEC"\n",
1226                    m_buffer_tail_timestamp,m_buffer_next_tail_timestamp);
1227    
1228     }
1229
1230     // this DLL allows the calculation of any sample timestamp relative to the buffer tail,
1231     // to the next period and beyond (through extrapolation)
1232     //
1233     // ts(x) = m_buffer_tail_timestamp +
1234     //         (m_buffer_next_tail_timestamp - m_buffer_tail_timestamp)/(samples_between_updates)*x
1235
1236 }
1237
1238 /**
1239  * @brief Print status info.
1240  */
1241 void TimestampedBuffer::dumpInfo() {
1242
1243     ffado_timestamp_t ts_head;
1244     signed int fc;
1245     getBufferHeadTimestamp(&ts_head,&fc);
1246
1247 #ifdef DEBUG
1248     ffado_timestamp_t diff=(ffado_timestamp_t)ts_head - (ffado_timestamp_t)m_buffer_tail_timestamp;
1249 #endif
1250
1251     debugOutputShort( DEBUG_LEVEL_NORMAL, "  TimestampedBuffer (%p) info:\n",this);
1252     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Frame counter         : %d\n", m_framecounter);
1253     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Events in buffer      : %d\n", getBufferFill());
1254     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Buffer head timestamp : "TIMESTAMP_FORMAT_SPEC"\n",ts_head);
1255     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Buffer tail timestamp : "TIMESTAMP_FORMAT_SPEC"\n",m_buffer_tail_timestamp);
1256     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Next tail timestamp   : "TIMESTAMP_FORMAT_SPEC"\n",m_buffer_next_tail_timestamp);
1257     debugOutputShort( DEBUG_LEVEL_NORMAL, "  Head - Tail           : "TIMESTAMP_FORMAT_SPEC"\n",diff);
1258     debugOutputShort( DEBUG_LEVEL_NORMAL, "  rate                  : %f (%f)\n",m_dll_e2,m_dll_e2/m_update_period);
1259 }
1260
1261 } // end of namespace Util
Note: See TracBrowser for help on using the browser.