Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / libparanoia / overlap.c
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  */
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
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
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  */
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
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 }