1 /*
2  * Copyright (C) 2005, Magnus Hjorth
3  *
4  * This file is part of mhWaveEdit.
5  *
6  * mhWaveEdit is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * mhWaveEdit is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with mhWaveEdit; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 
22 #include <config.h>
23 
24 #include "rateest.h"
25 
26 #define BIN_SEC 0.25
27 #define BIN_USEC 250000
28 #define BIN_SEC_DIV 4
29 
30 #define LOW_RATIO_THRESHOLD 0.7
31 #define HIGH_RATIO_THRESHOLD 1.3
32 
33 /* The best previous estimate we got so far */
34 static off_t best_written, best_played;
35 static gfloat best_seconds;
36 static gfloat best_discarded_seconds;
37 
38 /* Total number of frames logged */
39 static off_t total_written_frames;
40 
41 /* The value of total_written_frames when the current_* variables were reset */
42 static off_t current_checkpoint;
43 /* Time current checkpoint was taken. Also time of last underrun */
44 static GTimeVal current_checkpoint_time;
45 
46 /* The current estimation we're building up */
47 /* Number of frames written for all time intervals that had "normal" usage */
48 static off_t current_played_frames;
49 static gfloat current_played_seconds;
50 /* Number of seconds that were discarded because of too high usage */
51 static gfloat current_discarded_seconds;
52 
53 /* Last time period # */
54 static gboolean is_first_period = TRUE;
55 static long last_log_period;
56 /* Amount of data played during this time period */
57 static off_t current_period_frames;
58 
59 /* Last value returned by rateest_frames_played */
60 static off_t last_frames_played_value;
61 
62 
calc_current_period(void)63 static long calc_current_period(void)
64 {
65      GTimeVal tv;
66      g_get_current_time(&tv);
67      return tv.tv_sec*BIN_SEC_DIV + tv.tv_usec/BIN_USEC;
68 }
69 
rateest_init(guint expected_samplerate)70 void rateest_init(guint expected_samplerate)
71 {
72      best_written = best_played = (gfloat)expected_samplerate;
73      best_seconds = 1.0;
74      total_written_frames = 0;
75      current_checkpoint = 0;
76      g_get_current_time(&current_checkpoint_time);
77      current_played_frames = 0;
78      current_played_seconds = 0;
79      last_log_period = calc_current_period();
80      is_first_period = TRUE;
81      current_period_frames = 0;
82      last_frames_played_value = 0;
83 }
84 
rateest_log_data(guint frames)85 void rateest_log_data(guint frames)
86 {
87      long l;
88      gfloat ratio;
89 
90      l = calc_current_period();
91      if (l != last_log_period) {
92 
93 	  ratio = ((gfloat)(current_period_frames * BIN_SEC_DIV)) /
94 	       (best_played / best_seconds);
95 	  /* printf("Period finished: frames=%d, ratio=%f\n",
96 	     (int)current_period_frames,ratio); */
97 
98 	  /* Ignore the first period, since it has unknown length */
99 	  if (is_first_period) {
100 	       current_discarded_seconds += current_period_frames /
101 		    rateest_real_samplerate();
102 	  } else if (l < last_log_period || (l-last_log_period > 1) ||
103 		     ratio < LOW_RATIO_THRESHOLD) {
104 	       /* Store the current estimate as the best if it is */
105 	       if (current_played_seconds > best_seconds) {
106 		    best_written = (gfloat)(total_written_frames-
107 					    current_checkpoint);
108 		    best_played = current_played_frames;
109 		    best_seconds = current_played_seconds;
110 		    best_discarded_seconds = current_discarded_seconds;
111 	       }
112 	       current_checkpoint = total_written_frames;
113 	       g_get_current_time(&current_checkpoint_time);
114 	       current_played_frames = 0;
115 	       current_played_seconds = 0.0;
116 	       current_discarded_seconds = 0.0;
117 	  } else if (ratio < HIGH_RATIO_THRESHOLD) {
118 	       /* Add this period to the current estimation */
119 	       current_played_frames += current_period_frames;
120 	       current_played_seconds += BIN_SEC;
121 	  } else
122 	       current_discarded_seconds += BIN_SEC;
123 	  current_period_frames = 0;
124 	  last_log_period = l;
125      }
126 
127      current_period_frames += frames;
128      total_written_frames += frames;
129 }
130 
rateest_prelog_data(guint frames)131 void rateest_prelog_data(guint frames)
132 {
133      total_written_frames += frames;
134 }
135 
rateest_frames_written(void)136 off_t rateest_frames_written(void)
137 {
138      return total_written_frames;
139 }
140 
rateest_real_samplerate(void)141 gfloat rateest_real_samplerate(void)
142 {
143      if (best_seconds > current_played_seconds)
144 	  return ((gfloat)best_played)/best_seconds;
145      else
146 	  return ((gfloat)current_played_frames)/current_played_seconds;
147 }
148 
rateest_frames_played(void)149 off_t rateest_frames_played(void)
150 {
151      GTimeVal tv,tv2;
152      int i;
153      gfloat f,sr;
154      off_t o,lat;
155      /* First, use clock time, checkpoint time and estimated sample
156       * rate to calculate position.
157       * If the clock seems broken, use written frames and estimated latency. */
158      g_get_current_time(&tv);
159      i = timeval_subtract(&tv2,&tv,&current_checkpoint_time);
160      sr = rateest_real_samplerate();
161      if (i >= 0) {
162 	  /* printf("tv2: %ld.%6ld\n",tv2.tv_sec,tv2.tv_usec); */
163 	  f = (gfloat)tv2.tv_usec * 0.000001 + (gfloat)tv2.tv_sec;
164 	  f *= sr;
165 	  /* printf("f: %f, sr: %f\n",f,sr); */
166 	  o = current_checkpoint + (off_t)f;
167 	  /* printf("First guess: %d, total_written: %d\n",(int)o,
168 	     (int)total_written_frames); */
169 	  if (o>total_written_frames) o=total_written_frames;
170      }
171      if (i<0) {
172 	  if (best_seconds > current_played_seconds)
173 	       lat = best_written - (best_seconds + best_discarded_seconds)*sr;
174 	  else
175 	       lat = (total_written_frames - current_checkpoint) -
176 		    (current_played_seconds + current_discarded_seconds)*sr;
177 	  o = total_written_frames - lat;
178      }
179      if (o > last_frames_played_value) last_frames_played_value = o;
180      return last_frames_played_value;
181 }
182