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

Revision 742, 41.2 kB (checked in by ppalmers, 13 years ago)

- Remove some obsolete support files and dirs

- Clean up the license statements in the source files. Everything is

GPL version 3 now.

- Add license and copyright notices to scons scripts

- Clean up some other text files

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