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(¤t_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(¤t_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,¤t_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