1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12 
13 /* @(#)overlap.c	1.11 04/02/20 J. Schilling from cdparanoia-III-alpha9.8 */
14 /*
15  *	Modifications to make the code portable Copyright (c) 2002 J. Schilling
16  */
17 /*
18  * CopyPolicy: GNU Public License 2 applies
19  * Copyright (C) by Monty (xiphmont@mit.edu)
20  *
21  * Statistic code and cache management for overlap settings
22  *
23  */
24 
25 #include <mconfig.h>
26 #include <stdxlib.h>
27 #include <standard.h>
28 #include <utypes.h>
29 #include "p_block.h"
30 #include "cdda_paranoia.h"
31 #include "overlap.h"
32 #include "isort.h"
33 
34 void paranoia_resetcache(cdrom_paranoia *p);
35 void paranoia_resetall(cdrom_paranoia *p);
36 void i_paranoia_trim(cdrom_paranoia *p, long beginword, long endword);
37 void offset_adjust_settings(cdrom_paranoia *p, void (*callback)(long, int));
38 void offset_add_value(cdrom_paranoia *p, offsets *o, long value,
39 							 void (*callback)(long, int));
40 
41 /*
42  * Internal cache management
43  */
paranoia_resetcache(cdrom_paranoia * p)44 void paranoia_resetcache(cdrom_paranoia *p)
45 {
46 	c_block		*c = c_first(p);
47 	v_fragment	*v;
48 
49 	while (c) {
50 		free_c_block(c);
51 		c = c_first(p);
52 	}
53 
54 	v = v_first(p);
55 	while (v) {
56 		free_v_fragment(v);
57 		v = v_first(p);
58 	}
59 }
60 
paranoia_resetall(cdrom_paranoia * p)61 void paranoia_resetall(cdrom_paranoia *p)
62 {
63 	p->root.returnedlimit = 0;
64 	p->dyndrift = 0;
65 	p->root.lastsector = 0;
66 
67 	if (p->root.vector) {
68 		i_cblock_destructor(p->root.vector);
69 		p->root.vector = NULL;
70 	}
71 	paranoia_resetcache(p);
72 }
73 
i_paranoia_trim(cdrom_paranoia * p,long beginword,long endword)74 void i_paranoia_trim(cdrom_paranoia *p, long beginword, long endword)
75 {
76 	root_block	*root = &(p->root);
77 
78 	if (root->vector != NULL) {
79 		long	target = beginword - p->maxdynoverlap;
80 		long	rbegin = cb(root->vector);
81 		long	rend = ce(root->vector);
82 
83 		if (rbegin > beginword)
84 			goto rootfree;
85 
86 		if (rbegin + p->maxdynoverlap < beginword) {
87 			if (target + MIN_WORDS_OVERLAP > rend)
88 				goto rootfree;
89 
90 			{
91 				long	offset = target - rbegin;
92 
93 				c_removef(root->vector, offset);
94 			}
95 		} {
96 			c_block	*c = c_first(p);
97 
98 			while (c) {
99 				c_block	*next = c_next(c);
100 
101 				if (ce(c) < beginword - p->maxdynoverlap)
102 					free_c_block(c);
103 				c = next;
104 			}
105 		}
106 
107 	}
108 	return;
109 
110 rootfree:
111 
112 	i_cblock_destructor(root->vector);
113 	root->vector = NULL;
114 	root->returnedlimit = -1;
115 	root->lastsector = 0;
116 
117 }
118 
119 /*
120  * Statistical and heuristic[al? :-] management
121  */
offset_adjust_settings(cdrom_paranoia * p,void (* callback)(long,int))122 void offset_adjust_settings(cdrom_paranoia *p, void (*callback)(long, int))
123 {
124 	if (p->stage2.offpoints >= 10) {
125 		/*
126 		 * drift: look at the average offset value.  If it's over one
127 		 * sector, frob it.  We just want a little hysteresis [sp?]
128 		 */
129 		long	av = (p->stage2.offpoints ? p->stage2.offaccum / p->stage2.offpoints : 0);
130 
131 		if (abs(av) > p->dynoverlap / 4) {
132 			av = (av / MIN_SECTOR_EPSILON) * MIN_SECTOR_EPSILON;
133 
134 			if (callback)
135 				(*callback) (ce(p->root.vector), PARANOIA_CB_DRIFT);
136 			p->dyndrift += av;
137 
138 			/*
139 			 * Adjust all the values in the cache otherwise we get
140 			 * a (potentially unstable) feedback loop
141 			 */
142 			{
143 				c_block		*c = c_first(p);
144 				v_fragment	*v = v_first(p);
145 
146 				while (v && v->one) {
147 					/*
148 					 * safeguard beginning bounds case with
149 					 * a hammer
150 					 */
151 					if (fb(v) < av || cb(v->one) < av) {
152 						v->one = NULL;
153 					} else {
154 						fb(v) -= av;
155 					}
156 					v = v_next(v);
157 				}
158 				while (c) {
159 					long	adj = min(av, cb(c));
160 
161 					c_set(c, cb(c) - adj);
162 					c = c_next(c);
163 				}
164 			}
165 
166 			p->stage2.offaccum = 0;
167 			p->stage2.offmin = 0;
168 			p->stage2.offmax = 0;
169 			p->stage2.offpoints = 0;
170 			p->stage2.newpoints = 0;
171 			p->stage2.offdiff = 0;
172 		}
173 	}
174 	if (p->stage1.offpoints >= 10) {
175 		/*
176 		 * dynoverlap: we arbitrarily set it to 4x the running
177 		 * difference value, unless min/max are more
178 		 */
179 		p->dynoverlap = (p->stage1.offpoints ? p->stage1.offdiff /
180 			p->stage1.offpoints * 3 : CD_FRAMEWORDS);
181 
182 		if (p->dynoverlap < -p->stage1.offmin * 1.5)
183 			p->dynoverlap = -p->stage1.offmin * 1.5;
184 
185 		if (p->dynoverlap < p->stage1.offmax * 1.5)
186 			p->dynoverlap = p->stage1.offmax * 1.5;
187 
188 		if (p->dynoverlap < p->mindynoverlap)
189 			p->dynoverlap = p->mindynoverlap;
190 		if (p->dynoverlap > p->maxdynoverlap)
191 			p->dynoverlap = p->maxdynoverlap;
192 
193 		if (callback)
194 			(*callback) (p->dynoverlap, PARANOIA_CB_OVERLAP);
195 
196 		if (p->stage1.offpoints > 600) {
197 			/*
198 			 * bit of a bug; this routine is called too often
199 			 * due to the overlap mesh alg we use in stage 1
200 			 */
201 			p->stage1.offpoints /= 1.2;
202 			p->stage1.offaccum /= 1.2;
203 			p->stage1.offdiff /= 1.2;
204 		}
205 		p->stage1.offmin = 0;
206 		p->stage1.offmax = 0;
207 		p->stage1.newpoints = 0;
208 	}
209 }
210 
offset_add_value(cdrom_paranoia * p,offsets * o,long value,void (* callback)(long,int))211 void offset_add_value(cdrom_paranoia *p, offsets *o, long value,
212                       void (*callback)(long, int))
213 {
214 	if (o->offpoints != -1) {
215 
216 		o->offdiff += abs(value);
217 		o->offpoints++;
218 		o->newpoints++;
219 		o->offaccum += value;
220 		if (value < o->offmin)
221 			o->offmin = value;
222 		if (value > o->offmax)
223 			o->offmax = value;
224 
225 		if (o->newpoints >= 10)
226 			offset_adjust_settings(p, callback);
227 	}
228 }
229