diff options
Diffstat (limited to 'libparanoia')
-rw-r--r-- | libparanoia/CMakeLists.txt | 6 | ||||
-rw-r--r-- | libparanoia/README.interface | 75 | ||||
-rw-r--r-- | libparanoia/README.paranoia | 83 | ||||
-rw-r--r-- | libparanoia/cdda_paranoia.h | 108 | ||||
-rw-r--r-- | libparanoia/gap.c | 244 | ||||
-rw-r--r-- | libparanoia/gap.h | 46 | ||||
-rw-r--r-- | libparanoia/isort.c | 159 | ||||
-rw-r--r-- | libparanoia/isort.h | 69 | ||||
-rw-r--r-- | libparanoia/overlap.c | 228 | ||||
-rw-r--r-- | libparanoia/overlap.h | 33 | ||||
-rw-r--r-- | libparanoia/overlapdef.txt | 20 | ||||
-rw-r--r-- | libparanoia/p_block.c | 474 | ||||
-rw-r--r-- | libparanoia/p_block.h | 210 | ||||
-rw-r--r-- | libparanoia/paranoia.c | 1741 | ||||
-rw-r--r-- | libparanoia/pmalloc.c | 89 | ||||
-rw-r--r-- | libparanoia/pmalloc.h | 42 |
16 files changed, 3627 insertions, 0 deletions
diff --git a/libparanoia/CMakeLists.txt b/libparanoia/CMakeLists.txt new file mode 100644 index 0000000..4319496 --- /dev/null +++ b/libparanoia/CMakeLists.txt @@ -0,0 +1,6 @@ +PROJECT (LIBparanoia C) +INCLUDE_DIRECTORIES(../include ${CMAKE_BINARY_DIR} ../wodim ${CMAKE_BINARY_DIR}/include) +ADD_DEFINITIONS(-DHAVE_CONFIG_H) +SET(LIBparanoia_SRCS gap.c isort.c overlap.c p_block.c paranoia.c pmalloc.c) + +ADD_LIBRARY (paranoia STATIC ${LIBparanoia_SRCS}) diff --git a/libparanoia/README.interface b/libparanoia/README.interface new file mode 100644 index 0000000..a7a44b4 --- /dev/null +++ b/libparanoia/README.interface @@ -0,0 +1,75 @@ +/* + * Exports (libparanoia) cdda_paranoia.h + */ +cdrom_paranoia *paranoia_init __PR((void * d, int nsectors)); +void paranoia_modeset __PR((cdrom_paranoia * p, int mode)); +long paranoia_seek __PR((cdrom_paranoia * p, long seek, int mode)); +Int16_t *paranoia_read __PR((cdrom_paranoia * p, void (*callback) (long, int))); +Int16_t *paranoia_read_limited __PR((cdrom_paranoia * p, void (*callback) (long, int), int maxretries)); +void paranoia_free __PR((cdrom_paranoia * p)); +void paranoia_overlapset __PR((cdrom_paranoia * p, long overlap)); + +/* + * Exports ?? (libparanoia) overlap.h + */ +extern void paranoia_resetall __PR((cdrom_paranoia * p)); +extern void paranoia_resetcache __PR((cdrom_paranoia * p)); + +Supported: + +PARANOIA_MODE_VERIFY +PARANOIA_MODE_OVERLAP +PARANOIA_MODE_NEVERSKIP + +Unsupported: + +PARANOIA_MODE_FRAGMENT +PARANOIA_MODE_SCRATCH +PARANOIA_MODE_REPAIR + + + +/* + * Imports (global Code) + */ +cdda_disc_firstsector (cdrom_drive *d) -> long sector +cdda_disc_lastsector (cdrom_drive *d) -> long sector +cdda_read (cdrom_drive *d, void *buffer, long beginsector, long sectors) -> long sectors +cdda_sector_gettrack (cdrom_drive *d,long sector) -> int trackno +cdda_track_audiop (cdrom_drive *d,int track) -> int ??? /* Is audiotrack */ +cdda_track_firstsector (cdrom_drive *d,int track) -> long sector +cdda_track_lastsector (cdrom_drive *d,int track) -> long sector +cdda_tracks (cdrom_drive *d) -> int tracks + +callback (long inpos, int function) + +/* + * Imports (libc) + */ +calloc +free +malloc +realloc + +memcmp +memcpy +memmove +memset + +qsort + +/*--------------------------------------------------------------------------*/ +usalp = usal_open(); +bufsize = usal_bufsize(usalp, CDR_BUF_SIZE); +nsecs = bufsize / SEC_SIZE; + +cdp = paranoia_init(usalp, nsecs); +# paranoia_modeset(cdp, mode); +# paranoia_overlapset(cdp, overlap); + +while (not ready) { + bp = paranoia_read(cdp, NULL); + write(f, bp, SEC_SISE); +} + +paranoia_free(cdp); diff --git a/libparanoia/README.paranoia b/libparanoia/README.paranoia new file mode 100644 index 0000000..4864c32 --- /dev/null +++ b/libparanoia/README.paranoia @@ -0,0 +1,83 @@ +# @(#)README.paranoia 1.1 97/04/04 J. Schilling from Monty (xiphmont@mit.edu) +README.paranoia + +Paranoia II: +(c) Monty +xiphmont@mit.edu + +Hi there. + +All CDROM drives are not created equal. You're probably using this +patch because yours is a little less equal than others-- or maybe you +just keep your CD collection in a box of full of gravel. Jewel cases +are for wimps; you know what I'm talking about. + +This patch adds extra-robust interframe syncronization, code to detect +scratches (and hold sync across the scratch) and finally routines to +filter out scratches as best possible. These are all handled +automatically by fairly modular code. + +1) extra interframe syncronization ------------------------------------- + +Some CD drives can read audio data from an exact starting point to an +exact ending point flawlessly; these are rare. A larger number of +drives read from only an approximately correct starting point, but do +manage to get all the data in-tact from wherever they manage to begin +the read. Stock cdda2wav is coded with this in mind. + +More drives, especially IDE-like and recent ATAPI drives, suffer from +framing misalignments within atomic multi-sector read operations. +Cdda2wav is also set, by default, to read 75 sectors at a time from +ATAPI drives, which Linux's IDE driver breaks into multiple 8 sector +reads to conserve kernel memory. Both of these things will break +cdda2wav; the symptoms include cracks or pops within the read sample. + +The "Paranoia" patch will verify the alignment of *every* byte read by +cdda2wav. The goal is data integrity, not performance: a minimum 50% +speed hit will result from the patch, likely more if your CDROM +suffers from the framing bugs the patch corrects. + +2) scratch detection and tolerance ------------------------------------ + +Because overlap syncronization requires matching exact, perfect sample +sections, scratched CDs typically cause normal cdda2wav to bail when +overlap syncronization is turned on. The second part of the +"Paranoia" patch ignores scratches in the read bitstream and +syncronizes using the data that can still be "trusted". This step +also collects first-pass scratch detection information used by the +third section of the patch. + +Expect performance to drop through the floor on a badly scratched CD; +the code can no longer use a simple case of exact matching for speedy +correlation. Maintaining solid sync across a scratch is processor +bound; later I'll add speedier algorithms for correlation than the +brute force method currently used. Scratch detection imposes little +additional overhead on a non-scratched CD. + +3) scratch repair ----------------------------------------------------- + +Scratches are an irrevocable loss of data. Still, it's usually +possible to reconstruct a sample closer to the original data than the +raw hissing, fluttering and crackling that severe scratches in the CD +surface produce. + +Scratch filtering is the 'hard part' of this patch. It needs to do +two things; first, find the 'pops' that escaped detection in section 2 +of the patch and secondly fill in the gaps. Both are acheived using +delta (slew) averaging and forward and backward linear predictive +interpolation (with IIR filters) to fill in known gaps as well as look +for 'problem values' in stretches of sample known to be scratched. + +THE SCRATCH DETECTION IS NOT PERFECT. Nor is the repair perfect, +although it is easier than detecting scratches reliably. Both are +cases of coming arbitrarily close to perfection; Paranoia is designed +with light to moderate damage in mind, but will still recover a +listenable sample from heavy damage. + +Paranoia still has quite a bit of room for improvement in the scratch +repair code... If cdda2wav + Paranoia is not doing an acceptable job +on CDs that you just gotta have, drop me a line; I didn't want to sink +months into a project I wasn't certain anyone would ever use :-) + +Monty + diff --git a/libparanoia/cdda_paranoia.h b/libparanoia/cdda_paranoia.h new file mode 100644 index 0000000..24a2411 --- /dev/null +++ b/libparanoia/cdda_paranoia.h @@ -0,0 +1,108 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)cdda_paranoia.h 1.20 04/02/20 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + * + */ + +#ifndef _CDROM_PARANOIA_H +#define _CDROM_PARANOIA_H + +#ifndef _MCONFIG_H +#include <mconfig.h> +#endif +#ifndef _UTYPES_H +#include <utypes.h> +#endif + +#ifndef __GNUC__ +#define inline +#endif + +#define CD_FRAMESIZE_RAW 2352 +#define CD_FRAMEWORDS (CD_FRAMESIZE_RAW/2) + +/* + * Second parameter of the callback function + */ +#define PARANOIA_CB_READ 0 /* Read off adjust ??? */ +#define PARANOIA_CB_VERIFY 1 /* Verifying jitter */ +#define PARANOIA_CB_FIXUP_EDGE 2 /* Fixed edge jitter */ +#define PARANOIA_CB_FIXUP_ATOM 3 /* Fixed atom jitter */ +#define PARANOIA_CB_SCRATCH 4 /* Unsupported */ +#define PARANOIA_CB_REPAIR 5 /* Unsupported */ +#define PARANOIA_CB_SKIP 6 /* Skip exhausted retry */ +#define PARANOIA_CB_DRIFT 7 /* Drift detected */ +#define PARANOIA_CB_BACKOFF 8 /* Unsupported */ +#define PARANOIA_CB_OVERLAP 9 /* Dyn Overlap adjust */ +#define PARANOIA_CB_FIXUP_DROPPED 10 /* Fixed dropped bytes */ +#define PARANOIA_CB_FIXUP_DUPED 11 /* Fixed duplicate bytes */ +#define PARANOIA_CB_READERR 12 /* Hard read error */ + +/* + * Cdparanoia modes to be set with paranoia_modeset() + */ +#define PARANOIA_MODE_FULL 0xFF +#define PARANOIA_MODE_DISABLE 0 + +#define PARANOIA_MODE_VERIFY 1 /* Verify data integrity in overlap area */ +#define PARANOIA_MODE_FRAGMENT 2 /* unsupported */ +#define PARANOIA_MODE_OVERLAP 4 /* Perform overlapped reads */ +#define PARANOIA_MODE_SCRATCH 8 /* unsupported */ +#define PARANOIA_MODE_REPAIR 16 /* unsupported */ +#define PARANOIA_MODE_NEVERSKIP 32 /* Do not skip failed reads (retry maxretries) */ + + +#ifndef CDP_COMPILE +typedef void cdrom_paranoia; +#endif + +/* + * The interface from libcdparanoia to the high level caller + */ +extern cdrom_paranoia *paranoia_init(void * d, int nsectors); +extern void paranoia_dynoverlapset(cdrom_paranoia * p, int minoverlap, + int maxoverlap); +extern void paranoia_modeset(cdrom_paranoia * p, int mode); +extern long paranoia_seek(cdrom_paranoia * p, long seek, int mode); +extern Int16_t *paranoia_read(cdrom_paranoia * p, void (*callback) (long, int)); +extern Int16_t *paranoia_read_limited(cdrom_paranoia * p, + void (*callback) (long, int), + int maxretries); +extern void paranoia_free(cdrom_paranoia * p); +extern void paranoia_overlapset(cdrom_paranoia * p, long overlap); + +#ifndef HAVE_MEMMOVE +#define memmove(dst, src, size) movebytes((src), (dst), (size)) +#endif + + +/* + * The callback interface from libparanoia to the CD-ROM interface + */ +extern long cdda_disc_firstsector(void *d); /* -> long sector */ +extern long cdda_disc_lastsector(void *d); /* -> long sector */ +/* -> long sectors */ +extern long cdda_read(void *d, void *buffer, long beginsector, long sectors); +extern int cdda_sector_gettrack(void *d, long sector); /* -> int trackno */ +extern int cdda_track_audiop(void *d, int track); /* -> int Is audiotrack */ +extern long cdda_track_firstsector(void *d, int track); /* -> long sector */ +extern long cdda_track_lastsector(void *d, int track); /* -> long sector */ +extern int cdda_tracks(void *d); /* -> int tracks */ + +#endif /* _CDROM_PARANOIA_H */ diff --git a/libparanoia/gap.c b/libparanoia/gap.c new file mode 100644 index 0000000..6bb1b92 --- /dev/null +++ b/libparanoia/gap.c @@ -0,0 +1,244 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)gap.c 1.12 04/02/18 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + * + * Gapa analysis support code for paranoia + * + */ + +#include <mconfig.h> +#include <standard.h> +#include <utypes.h> +#include <strdefs.h> +#include "p_block.h" +#include "cdda_paranoia.h" +#include "gap.h" + +long i_paranoia_overlap_r(Int16_t * buffA, Int16_t * buffB, long offsetA, + long offsetB); +long i_paranoia_overlap_f(Int16_t * buffA, Int16_t * buffB, long offsetA, + long offsetB, long sizeA, long sizeB); +int i_stutter_or_gap(Int16_t * A, Int16_t * B, long offA, long offB, long gap); +void i_analyze_rift_f(Int16_t * A, Int16_t * B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA, long *matchB, long *matchC); +void i_analyze_rift_r(Int16_t * A, Int16_t * B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA, long *matchB, long *matchC); +void analyze_rift_silence_f(Int16_t * A, Int16_t * B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA, long *matchB); + +/* + * Gap analysis code + */ +long i_paranoia_overlap_r(Int16_t *buffA, Int16_t *buffB, long offsetA, + long offsetB) +{ + long beginA = offsetA; + long beginB = offsetB; + + for (; beginA >= 0 && beginB >= 0; beginA--, beginB--) + if (buffA[beginA] != buffB[beginB]) + break; + beginA++; + beginB++; + + return (offsetA - beginA); +} + +long i_paranoia_overlap_f(Int16_t *buffA, Int16_t *buffB, long offsetA, + long offsetB, long sizeA, long sizeB) +{ + long endA = offsetA; + long endB = offsetB; + + for (; endA < sizeA && endB < sizeB; endA++, endB++) + if (buffA[endA] != buffB[endB]) + break; + + return (endA - offsetA); +} + +int i_stutter_or_gap(Int16_t *A, Int16_t *B, long offA, long offB, long gap) +{ + long a1 = offA; + long b1 = offB; + + if (a1 < 0) { + b1 -= a1; + gap += a1; + a1 = 0; + } + return (memcmp(A + a1, B + b1, gap * 2)); +} + +/* + * riftv is the first value into the rift -> or <- + */ +void i_analyze_rift_f(Int16_t *A, Int16_t *B, long sizeA, long sizeB, + long aoffset, long boffset, long *matchA, long *matchB, + long *matchC) +{ + + long apast = sizeA - aoffset; + long bpast = sizeB - boffset; + long i; + + *matchA = 0, *matchB = 0, *matchC = 0; + + /* + * Look for three possible matches... (A) Ariftv->B, + * (B) Briftv->A and (c) AB->AB. + */ + for (i = 0; ; i++) { + if (i < bpast) /* A */ + if (i_paranoia_overlap_f(A, B, aoffset, boffset + i, sizeA, sizeB) >= MIN_WORDS_RIFT) { + *matchA = i; + break; + } + if (i < apast) { /* B */ + if (i_paranoia_overlap_f(A, B, aoffset + i, boffset, sizeA, sizeB) >= MIN_WORDS_RIFT) { + *matchB = i; + break; + } + if (i < bpast) /* C */ + if (i_paranoia_overlap_f(A, B, aoffset + i, boffset + i, sizeA, sizeB) >= MIN_WORDS_RIFT) { + *matchC = i; + break; + } + } else if (i >= bpast) + break; + + } + + if (*matchA == 0 && *matchB == 0 && *matchC == 0) + return; + + if (*matchC) + return; + if (*matchA) { + if (i_stutter_or_gap(A, B, aoffset - *matchA, boffset, *matchA)) + return; + *matchB = -*matchA; /* signify we need to remove n bytes */ + /* from B */ + *matchA = 0; + return; + } else { + if (i_stutter_or_gap(B, A, boffset - *matchB, aoffset, *matchB)) + return; + *matchA = -*matchB; + *matchB = 0; + return; + } +} + +/* + * riftv must be first even val of rift moving back + */ +void i_analyze_rift_r(Int16_t *A, Int16_t *B, long sizeA, long sizeB, + long aoffset, long boffset, long *matchA, long *matchB, + long *matchC) +{ + + long apast = aoffset + 1; + long bpast = boffset + 1; + long i; + + *matchA = 0, *matchB = 0, *matchC = 0; + + /* + * Look for three possible matches... (A) Ariftv->B, (B) Briftv->A and + * (c) AB->AB. + */ + for (i = 0; ; i++) { + if (i < bpast) /* A */ + if (i_paranoia_overlap_r(A, B, aoffset, boffset - i) >= MIN_WORDS_RIFT) { + *matchA = i; + break; + } + if (i < apast) { /* B */ + if (i_paranoia_overlap_r(A, B, aoffset - i, boffset) >= MIN_WORDS_RIFT) { + *matchB = i; + break; + } + if (i < bpast) /* C */ + if (i_paranoia_overlap_r(A, B, aoffset - i, boffset - i) >= MIN_WORDS_RIFT) { + *matchC = i; + break; + } + } else if (i >= bpast) + break; + + } + + if (*matchA == 0 && *matchB == 0 && *matchC == 0) + return; + + if (*matchC) + return; + + if (*matchA) { + if (i_stutter_or_gap(A, B, aoffset + 1, boffset - *matchA + 1, *matchA)) + return; + *matchB = -*matchA; /* signify we need to remove n bytes */ + /* from B */ + *matchA = 0; + return; + } else { + if (i_stutter_or_gap(B, A, boffset + 1, aoffset - *matchB + 1, *matchB)) + return; + *matchA = -*matchB; + *matchB = 0; + return; + } +} + +void analyze_rift_silence_f(Int16_t *A, Int16_t *B, long sizeA, long sizeB, + long aoffset, long boffset, long *matchA, + long *matchB) +{ + *matchA = -1; + *matchB = -1; + + sizeA = min(sizeA, aoffset + MIN_WORDS_RIFT); + sizeB = min(sizeB, boffset + MIN_WORDS_RIFT); + + aoffset++; + boffset++; + + while (aoffset < sizeA) { + if (A[aoffset] != A[aoffset - 1]) { + *matchA = 0; + break; + } + aoffset++; + } + + while (boffset < sizeB) { + if (B[boffset] != B[boffset - 1]) { + *matchB = 0; + break; + } + boffset++; + } +} diff --git a/libparanoia/gap.h b/libparanoia/gap.h new file mode 100644 index 0000000..f772ae3 --- /dev/null +++ b/libparanoia/gap.h @@ -0,0 +1,46 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)gap.h 1.10 04/02/18 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + */ + +#ifndef _GAP_H_ +#define _GAP_H_ + +extern long i_paranoia_overlap_r(Int16_t * buffA, Int16_t * buffB, + long offsetA, long offsetB); +extern long i_paranoia_overlap_f(Int16_t * buffA, Int16_t * buffB, + long offsetA, long offsetB, + long sizeA, long sizeB); +extern int i_stutter_or_gap(Int16_t * A, Int16_t * B, + long offA, long offB, + long gap); +extern void i_analyze_rift_f(Int16_t * A, Int16_t * B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA, long *matchB, long *matchC); +extern void i_analyze_rift_r(Int16_t * A, Int16_t * B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA, long *matchB, long *matchC); +extern void analyze_rift_silence_f(Int16_t * A, Int16_t * B, + long sizeA, long sizeB, + long aoffset, long boffset, + long *matchA, long *matchB); + +#endif diff --git a/libparanoia/isort.c b/libparanoia/isort.c new file mode 100644 index 0000000..a3d38ad --- /dev/null +++ b/libparanoia/isort.c @@ -0,0 +1,159 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)isort.c 1.14 04/02/20 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + * + * sorted vector abstraction for paranoia + * + */ + +/* + * Old isort got a bit complex. This re-constrains complexity to + * give a go at speed through a more alpha-6-like mechanism. + */ + +#include <mconfig.h> +#include <stdxlib.h> +#include <standard.h> +#include <utypes.h> +#include <strdefs.h> +#include "p_block.h" +#include "isort.h" +#include "pmalloc.h" + +sort_info *sort_alloc(long size); +void sort_unsortall(sort_info * i); +void sort_free(sort_info * i); +void sort_sort(sort_info * i, long sortlo, long sorthi); +void sort_setup(sort_info * i, Int16_t * vector, long *abspos, long size, + long sortlo, long sorthi); +sort_link *sort_getmatch(sort_info * i, long post, long overlap, int value); +sort_link *sort_nextmatch(sort_info * i, sort_link * prev); + + +sort_info *sort_alloc(long size) +{ + sort_info *ret = _pcalloc(1, sizeof (sort_info)); + + ret->vector = NULL; + ret->sortbegin = -1; + ret->size = -1; + ret->maxsize = size; + + ret->head = _pcalloc(65536, sizeof (sort_link *)); + ret->bucketusage = _pmalloc(65536 * sizeof (long)); + ret->revindex = _pcalloc(size, sizeof (sort_link)); + ret->lastbucket = 0; + + return (ret); +} + +void sort_unsortall(sort_info *i) +{ + if (i->lastbucket > 2000) { /* a guess */ + memset(i->head, 0, 65536 * sizeof (sort_link *)); + } else { + long b; + + for (b = 0; b < i->lastbucket; b++) + i->head[i->bucketusage[b]] = NULL; + } + + i->lastbucket = 0; + i->sortbegin = -1; +} + +void sort_free(sort_info *i) +{ + _pfree(i->revindex); + _pfree(i->head); + _pfree(i->bucketusage); + _pfree(i); +} + +void sort_sort(sort_info *i, long sortlo, long sorthi) +{ + long j; + + for (j = sorthi - 1; j >= sortlo; j--) { + sort_link **hv = i->head + i->vector[j] + 32768; + sort_link *l = i->revindex + j; + + if (*hv == NULL) { + i->bucketusage[i->lastbucket] = i->vector[j] + 32768; + i->lastbucket++; + } + l->next = *hv; + *hv = l; + } + i->sortbegin = 0; +} + +/* + * size *must* be less than i->maxsize + */ +void sort_setup(sort_info *i, Int16_t *vector, long *abspos, long size, + long sortlo, long sorthi) +{ + if (i->sortbegin != -1) + sort_unsortall(i); + + i->vector = vector; + i->size = size; + i->abspos = abspos; + + i->lo = min(size, max(sortlo - *abspos, 0)); + i->hi = max(0, min(sorthi - *abspos, size)); +} + +sort_link *sort_getmatch(sort_info *i, long post, long overlap, int value) +{ + sort_link *ret; + + if (i->sortbegin == -1) + sort_sort(i, i->lo, i->hi); + /* + * Now we reuse lo and hi + */ + post = max(0, min(i->size, post)); + i->val = value + 32768; + i->lo = max(0, post - overlap); /* absolute position */ + i->hi = min(i->size, post + overlap); /* absolute position */ + + ret = i->head[i->val]; + while (ret) { + if (ipos(i, ret) < i->lo) { + ret = ret->next; + } else { + if (ipos(i, ret) >= i->hi) + ret = NULL; + break; + } + } +/* i->head[i->val]=ret; */ + return (ret); +} + +sort_link *sort_nextmatch(sort_info *i, sort_link *prev) +{ + sort_link *ret = prev->next; + + if (!ret || ipos(i, ret) >= i->hi) + return (NULL); + return (ret); +} diff --git a/libparanoia/isort.h b/libparanoia/isort.h new file mode 100644 index 0000000..1b6661c --- /dev/null +++ b/libparanoia/isort.h @@ -0,0 +1,69 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)isort.h 1.10 04/02/18 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + */ + +#ifndef _ISORT_H_ +#define _ISORT_H_ + +typedef struct sort_link { + struct sort_link *next; +} sort_link; + +typedef struct sort_info { + Int16_t *vector; /* vector */ + /* vec storage doesn't belong to us */ + + long *abspos; /* pointer for side effects */ + long size; /* vector size */ + + long maxsize; /* maximum vector size */ + + long sortbegin; /* range of contiguous sorted area */ + long lo; + long hi; /* current post, overlap range */ + int val; /* ...and val */ + + /* + * sort structs + */ + sort_link **head; /* sort buckets (65536) */ + + long *bucketusage; /* of used buckets (65536) */ + long lastbucket; + sort_link *revindex; + +} sort_info; + +extern sort_info *sort_alloc(long size); +extern void sort_unsortall(sort_info * i); +extern void sort_setup(sort_info * i, Int16_t * vector, long *abspos, + long size, long sortlo, long sorthi); +extern void sort_free(sort_info * i); +extern sort_link *sort_getmatch(sort_info * i, long post, long overlap, + int value); +extern sort_link *sort_nextmatch(sort_info * i, sort_link * prev); + +#define is(i) ((i)->size) +#define ib(i) (*(i)->abspos) +#define ie(i) ((i)->size + *(i)->abspos) +#define iv(i) ((i)->vector) +#define ipos(i, l) ((l) - (i)->revindex) + +#endif diff --git a/libparanoia/overlap.c b/libparanoia/overlap.c new file mode 100644 index 0000000..6f15baf --- /dev/null +++ b/libparanoia/overlap.c @@ -0,0 +1,228 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)overlap.c 1.11 04/02/20 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + * + * Statistic code and cache management for overlap settings + * + */ + +#include <mconfig.h> +#include <stdxlib.h> +#include <standard.h> +#include <utypes.h> +#include "p_block.h" +#include "cdda_paranoia.h" +#include "overlap.h" +#include "isort.h" + +void paranoia_resetcache(cdrom_paranoia *p); +void paranoia_resetall(cdrom_paranoia *p); +void i_paranoia_trim(cdrom_paranoia *p, long beginword, long endword); +void offset_adjust_settings(cdrom_paranoia *p, void (*callback)(long, int)); +void offset_add_value(cdrom_paranoia *p, offsets *o, long value, + void (*callback)(long, int)); + +/* + * Internal cache management + */ +void paranoia_resetcache(cdrom_paranoia *p) +{ + c_block *c = c_first(p); + v_fragment *v; + + while (c) { + free_c_block(c); + c = c_first(p); + } + + v = v_first(p); + while (v) { + free_v_fragment(v); + v = v_first(p); + } +} + +void paranoia_resetall(cdrom_paranoia *p) +{ + p->root.returnedlimit = 0; + p->dyndrift = 0; + p->root.lastsector = 0; + + if (p->root.vector) { + i_cblock_destructor(p->root.vector); + p->root.vector = NULL; + } + paranoia_resetcache(p); +} + +void i_paranoia_trim(cdrom_paranoia *p, long beginword, long endword) +{ + root_block *root = &(p->root); + + if (root->vector != NULL) { + long target = beginword - p->maxdynoverlap; + long rbegin = cb(root->vector); + long rend = ce(root->vector); + + if (rbegin > beginword) + goto rootfree; + + if (rbegin + p->maxdynoverlap < beginword) { + if (target + MIN_WORDS_OVERLAP > rend) + goto rootfree; + + { + long offset = target - rbegin; + + c_removef(root->vector, offset); + } + } { + c_block *c = c_first(p); + + while (c) { + c_block *next = c_next(c); + + if (ce(c) < beginword - p->maxdynoverlap) + free_c_block(c); + c = next; + } + } + + } + return; + +rootfree: + + i_cblock_destructor(root->vector); + root->vector = NULL; + root->returnedlimit = -1; + root->lastsector = 0; + +} + +/* + * Statistical and heuristic[al? :-] management + */ +void offset_adjust_settings(cdrom_paranoia *p, void (*callback)(long, int)) +{ + if (p->stage2.offpoints >= 10) { + /* + * drift: look at the average offset value. If it's over one + * sector, frob it. We just want a little hysteresis [sp?] + */ + long av = (p->stage2.offpoints ? p->stage2.offaccum / p->stage2.offpoints : 0); + + if (abs(av) > p->dynoverlap / 4) { + av = (av / MIN_SECTOR_EPSILON) * MIN_SECTOR_EPSILON; + + if (callback) + (*callback) (ce(p->root.vector), PARANOIA_CB_DRIFT); + p->dyndrift += av; + + /* + * Adjust all the values in the cache otherwise we get + * a (potentially unstable) feedback loop + */ + { + c_block *c = c_first(p); + v_fragment *v = v_first(p); + + while (v && v->one) { + /* + * safeguard beginning bounds case with + * a hammer + */ + if (fb(v) < av || cb(v->one) < av) { + v->one = NULL; + } else { + fb(v) -= av; + } + v = v_next(v); + } + while (c) { + long adj = min(av, cb(c)); + + c_set(c, cb(c) - adj); + c = c_next(c); + } + } + + p->stage2.offaccum = 0; + p->stage2.offmin = 0; + p->stage2.offmax = 0; + p->stage2.offpoints = 0; + p->stage2.newpoints = 0; + p->stage2.offdiff = 0; + } + } + if (p->stage1.offpoints >= 10) { + /* + * dynoverlap: we arbitrarily set it to 4x the running + * difference value, unless min/max are more + */ + p->dynoverlap = (p->stage1.offpoints ? p->stage1.offdiff / + p->stage1.offpoints * 3 : CD_FRAMEWORDS); + + if (p->dynoverlap < -p->stage1.offmin * 1.5) + p->dynoverlap = -p->stage1.offmin * 1.5; + + if (p->dynoverlap < p->stage1.offmax * 1.5) + p->dynoverlap = p->stage1.offmax * 1.5; + + if (p->dynoverlap < p->mindynoverlap) + p->dynoverlap = p->mindynoverlap; + if (p->dynoverlap > p->maxdynoverlap) + p->dynoverlap = p->maxdynoverlap; + + if (callback) + (*callback) (p->dynoverlap, PARANOIA_CB_OVERLAP); + + if (p->stage1.offpoints > 600) { + /* + * bit of a bug; this routine is called too often + * due to the overlap mesh alg we use in stage 1 + */ + p->stage1.offpoints /= 1.2; + p->stage1.offaccum /= 1.2; + p->stage1.offdiff /= 1.2; + } + p->stage1.offmin = 0; + p->stage1.offmax = 0; + p->stage1.newpoints = 0; + } +} + +void offset_add_value(cdrom_paranoia *p, offsets *o, long value, + void (*callback)(long, int)) +{ + if (o->offpoints != -1) { + + o->offdiff += abs(value); + o->offpoints++; + o->newpoints++; + o->offaccum += value; + if (value < o->offmin) + o->offmin = value; + if (value > o->offmax) + o->offmax = value; + + if (o->newpoints >= 10) + offset_adjust_settings(p, callback); + } +} diff --git a/libparanoia/overlap.h b/libparanoia/overlap.h new file mode 100644 index 0000000..44db6e2 --- /dev/null +++ b/libparanoia/overlap.h @@ -0,0 +1,33 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)overlap.h 1.7 04/02/18 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + */ + +#ifndef _OVERLAP_H_ +#define _OVERLAP_H_ + +extern void paranoia_resetcache(cdrom_paranoia *p); +extern void paranoia_resetall(cdrom_paranoia *p); +extern void i_paranoia_trim(cdrom_paranoia *p, long beginword, long endword); +extern void offset_adjust_settings(cdrom_paranoia *p, + void (*callback) (long, int)); +extern void offset_add_value(cdrom_paranoia *p, offsets *o, long value, + void (*callback) (long, int)); + +#endif diff --git a/libparanoia/overlapdef.txt b/libparanoia/overlapdef.txt new file mode 100644 index 0000000..3aff6cf --- /dev/null +++ b/libparanoia/overlapdef.txt @@ -0,0 +1,20 @@ +# @(#)overlapdef.txt 1.1 98/08/19 J. Schilling from Monty (xiphmont@mit.edu) + + 0 70 100 +A |----------|-----| +B |-----|---------| + 0 40 100 + +offset=-30 +begin=30 +end=100 + + 0 70 100 +A |----------|-----| +B |-----|---------| + 50 90 150 + +offset=20 +begin=30 +end=100 + diff --git a/libparanoia/p_block.c b/libparanoia/p_block.c new file mode 100644 index 0000000..e37334b --- /dev/null +++ b/libparanoia/p_block.c @@ -0,0 +1,474 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)p_block.c 1.19 04/02/23 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +#include <mconfig.h> +#include <stdxlib.h> +#include <standard.h> +#include <utypes.h> +#include <strdefs.h> +#include "p_block.h" +#include "cdda_paranoia.h" +#include "pmalloc.h" + +linked_list *new_list(void *(*newp) (void), void (*freep) (void *)); +linked_element *add_elem(linked_list *l, void *elem); +linked_element *new_elem(linked_list *list); +void free_elem(linked_element *e, int free_ptr); +void free_list(linked_list *list, int free_ptr); +void *get_elem(linked_element *e); +linked_list *copy_list(linked_list *list); +static c_block *i_cblock_constructor(void); +void i_cblock_destructor(c_block *c); +c_block *new_c_block(cdrom_paranoia *p); +void free_c_block(c_block *c); +static v_fragment *i_vfragment_constructor(void); +static void i_v_fragment_destructor(v_fragment *v); +v_fragment *new_v_fragment(cdrom_paranoia *p, c_block *one, long begin, + long end, int last); +void free_v_fragment(v_fragment *v); +c_block *c_first(cdrom_paranoia *p); +c_block *c_last(cdrom_paranoia *p); +c_block *c_next(c_block *c); +c_block *c_prev(c_block *c); +v_fragment *v_first(cdrom_paranoia *p); +v_fragment *v_last(cdrom_paranoia *p); +v_fragment *v_next(v_fragment *v); +v_fragment *v_prev(v_fragment *v); +void recover_cache(cdrom_paranoia *p); +Int16_t *v_buffer(v_fragment *v); +c_block *c_alloc(Int16_t *vector, long begin, long size); +void c_set(c_block *v, long begin); +void c_remove(c_block *v, long cutpos, long cutsize); +void c_overwrite(c_block *v, long pos, Int16_t *b, long size); +void c_append(c_block *v, Int16_t *vector, long size); +void c_removef(c_block *v, long cut); +void i_paranoia_firstlast(cdrom_paranoia *p); +cdrom_paranoia *paranoia_init(void *d, int nsectors); + + +linked_list *new_list(void *(*newp)(void), void (*freep)(void *)) +{ + linked_list *ret = _pcalloc(1, sizeof (linked_list)); + + ret->new_poly = newp; + ret->free_poly = freep; + return (ret); +} + +linked_element *add_elem(linked_list *l, void *elem) +{ + + linked_element *ret = _pcalloc(1, sizeof (linked_element)); + + ret->stamp = l->current++; + ret->ptr = elem; + ret->list = l; + + if (l->head) + l->head->prev = ret; + else + l->tail = ret; + ret->next = l->head; + ret->prev = NULL; + l->head = ret; + l->active++; + + return (ret); +} + +linked_element *new_elem(linked_list *list) +{ + void *new = list->new_poly(); + + return (add_elem(list, new)); +} + +void free_elem(linked_element *e, int free_ptr) +{ + linked_list *l = e->list; + + if (free_ptr) + l->free_poly(e->ptr); + + if (e == l->head) + l->head = e->next; + if (e == l->tail) + l->tail = e->prev; + + if (e->prev) + e->prev->next = e->next; + if (e->next) + e->next->prev = e->prev; + + l->active--; + _pfree(e); +} + +void free_list(linked_list *list, int free_ptr) +{ + while (list->head) + free_elem(list->head, free_ptr); + _pfree(list); +} + +void *get_elem(linked_element *e) +{ + return (e->ptr); +} + +linked_list *copy_list(linked_list *list) +{ + linked_list *new = new_list(list->new_poly, list->free_poly); + linked_element *i = list->tail; + + while (i) { + add_elem(new, i->ptr); + i = i->prev; + } + return (new); +} + +/**** C_block stuff ******************************************************/ + +#define vp_cblock_constructor_func ((void*(*)(void))i_cblock_constructor) +static c_block *i_cblock_constructor() +{ + c_block *ret = _pcalloc(1, sizeof (c_block)); + + return (ret); +} + +#define vp_cblock_destructor_func ((void(*)(void*))i_cblock_destructor) +void i_cblock_destructor(c_block *c) +{ + if (c) { + if (c->vector) + _pfree(c->vector); + if (c->flags) + _pfree(c->flags); + c->e = NULL; + _pfree(c); + } +} + +c_block *new_c_block(cdrom_paranoia *p) +{ + linked_element *e = new_elem(p->cache); + c_block *c = e->ptr; + + c->e = e; + c->p = p; + return (c); +} + +void free_c_block(c_block *c) +{ + /* + * also rid ourselves of v_fragments that reference this block + */ + v_fragment *v = v_first(c->p); + + while (v) { + v_fragment *next = v_next(v); + + if (v->one == c) + free_v_fragment(v); + v = next; + } + + free_elem(c->e, 1); +} + +#define vp_vfragment_constructor_func ((void*(*)(void))i_vfragment_constructor) +static v_fragment *i_vfragment_constructor() +{ + v_fragment *ret = _pcalloc(1, sizeof (v_fragment)); + + return (ret); +} + +#define vp_v_fragment_destructor_func ((void(*)(void*))i_v_fragment_destructor) +static void i_v_fragment_destructor(v_fragment *v) +{ + _pfree(v); +} + +v_fragment *new_v_fragment(cdrom_paranoia *p, c_block *one, long begin, + long end, int last) +{ + linked_element *e = new_elem(p->fragments); + v_fragment *b = e->ptr; + + b->e = e; + b->p = p; + + b->one = one; + b->begin = begin; + b->vector = one->vector + begin - one->begin; + b->size = end - begin; + b->lastsector = last; + + return (b); +} + +void free_v_fragment(v_fragment *v) +{ + free_elem(v->e, 1); +} + +c_block *c_first(cdrom_paranoia *p) +{ + if (p->cache->head) + return (p->cache->head->ptr); + return (NULL); +} + +c_block* c_last(cdrom_paranoia *p) +{ + if (p->cache->tail) + return (p->cache->tail->ptr); + return (NULL); +} + +c_block *c_next(c_block *c) +{ + if (c->e->next) + return (c->e->next->ptr); + return (NULL); +} + +c_block *c_prev(c_block *c) +{ + if (c->e->prev) + return (c->e->prev->ptr); + return (NULL); +} + +v_fragment *v_first(cdrom_paranoia *p) +{ + if (p->fragments->head) { + return (p->fragments->head->ptr); + } + return (NULL); +} + +v_fragment *v_last(cdrom_paranoia *p) +{ + if (p->fragments->tail) + return (p->fragments->tail->ptr); + return (NULL); +} + +v_fragment *v_next(v_fragment *v) +{ + if (v->e->next) + return (v->e->next->ptr); + return (NULL); +} + +v_fragment *v_prev(v_fragment *v) +{ + if (v->e->prev) + return (v->e->prev->ptr); + return (NULL); +} + +void recover_cache(cdrom_paranoia *p) +{ + linked_list *l = p->cache; + + /* + * Are we at/over our allowed cache size? + */ + while (l->active > p->cache_limit) { + /* + * cull from the tail of the list + */ + free_c_block(c_last(p)); + } + +} + +Int16_t *v_buffer(v_fragment *v) +{ + if (!v->one) + return (NULL); + if (!cv(v->one)) + return (NULL); + return (v->vector); +} + +/* + * alloc a c_block not on a cache list + */ +c_block *c_alloc(Int16_t *vector, long begin, long size) +{ + c_block *c = _pcalloc(1, sizeof (c_block)); + + c->vector = vector; + c->begin = begin; + c->size = size; + return (c); +} + +void c_set(c_block *v, long begin) +{ + v->begin = begin; +} + +/* + * pos here is vector position from zero + */ +void c_insert(c_block *v, long pos, Int16_t *b, long size) +{ + int vs = cs(v); + + if (pos < 0 || pos > vs) + return; + + if (v->vector) + v->vector = _prealloc(v->vector, sizeof (Int16_t) * (size + vs)); + else + v->vector = _pmalloc(sizeof (Int16_t) * size); + + if (pos < vs) + memmove(v->vector + pos + size, v->vector + pos, + (vs - pos) * sizeof (Int16_t)); + memcpy(v->vector + pos, b, size * sizeof (Int16_t)); + + v->size += size; +} + +void c_remove(c_block *v, long cutpos, long cutsize) +{ + int vs = cs(v); + + if (cutpos < 0 || cutpos > vs) + return; + if (cutpos + cutsize > vs) + cutsize = vs - cutpos; + if (cutsize < 0) + cutsize = vs - cutpos; + if (cutsize < 1) + return; + + memmove(v->vector + cutpos, v->vector + cutpos + cutsize, + (vs - cutpos - cutsize) * sizeof (Int16_t)); + + v->size -= cutsize; +} + +void c_overwrite(c_block *v, long pos, Int16_t *b, long size) +{ + int vs = cs(v); + + if (pos < 0) + return; + if (pos + size > vs) + size = vs - pos; + + memcpy(v->vector + pos, b, size * sizeof (Int16_t)); +} + +void c_append(c_block *v, Int16_t *vector, long size) +{ + int vs = cs(v); + + /* + * update the vector + */ + if (v->vector) + v->vector = _prealloc(v->vector, sizeof (Int16_t) * (size + vs)); + else + v->vector = _pmalloc(sizeof (Int16_t) * size); + memcpy(v->vector + vs, vector, sizeof (Int16_t) * size); + + v->size += size; +} + +void c_removef(c_block *v, long cut) +{ + c_remove(v, 0, cut); + v->begin += cut; +} + + + +/* + * Initialization + */ +void i_paranoia_firstlast(cdrom_paranoia *p) +{ + int i; + void *d = p->d; + + p->current_lastsector = -1; + for (i = cdda_sector_gettrack(d, p->cursor); i < cdda_tracks(d); i++) + if (!cdda_track_audiop(d, i)) + p->current_lastsector = cdda_track_lastsector(d, i - 1); + if (p->current_lastsector == -1) + p->current_lastsector = cdda_disc_lastsector(d); + + p->current_firstsector = -1; + for (i = cdda_sector_gettrack(d, p->cursor); i > 0; i--) + if (!cdda_track_audiop(d, i)) + p->current_firstsector = cdda_track_firstsector(d, i + 1); + if (p->current_firstsector == -1) + p->current_firstsector = cdda_disc_firstsector(d); + +} + +cdrom_paranoia *paranoia_init(void *d, int nsectors) +{ + cdrom_paranoia *p = _pcalloc(1, sizeof (cdrom_paranoia)); + + p->cache = new_list(vp_cblock_constructor_func, + vp_cblock_destructor_func); + + p->fragments = new_list(vp_vfragment_constructor_func, + vp_v_fragment_destructor_func); + + p->nsectors = nsectors; + p->readahead = 150; + p->sortcache = sort_alloc(p->readahead * CD_FRAMEWORDS); + p->d = d; + p->mindynoverlap = MIN_SECTOR_EPSILON; + p->maxdynoverlap = MAX_SECTOR_OVERLAP * CD_FRAMEWORDS; + p->maxdynoverlap = (nsectors - 1) * CD_FRAMEWORDS; + p->dynoverlap = MAX_SECTOR_OVERLAP * CD_FRAMEWORDS; + p->cache_limit = JIGGLE_MODULO; + p->enable = PARANOIA_MODE_FULL; + p->cursor = cdda_disc_firstsector(d); + p->lastread = -1000000; + + /* + * One last one... in case data and audio tracks are mixed... + */ + i_paranoia_firstlast(p); + + return (p); +} + +void paranoia_dynoverlapset(cdrom_paranoia *p, int minoverlap, int maxoverlap) +{ + if (minoverlap >= 0) + p->mindynoverlap = minoverlap; + if (maxoverlap > minoverlap) + p->maxdynoverlap = maxoverlap; + + if (p->maxdynoverlap < p->mindynoverlap) + p->maxdynoverlap = p->mindynoverlap; +} diff --git a/libparanoia/p_block.h b/libparanoia/p_block.h new file mode 100644 index 0000000..418c193 --- /dev/null +++ b/libparanoia/p_block.h @@ -0,0 +1,210 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)p_block.h 1.16 04/02/20 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + */ + +#ifndef _p_block_h_ +#define _p_block_h_ + +#define MIN_WORDS_OVERLAP 64 /* 16 bit words */ +#define MIN_WORDS_SEARCH 64 /* 16 bit words */ +#define MIN_WORDS_RIFT 16 /* 16 bit words */ +#define MAX_SECTOR_OVERLAP 32 /* sectors */ +#define MIN_SECTOR_EPSILON 128 /* words */ +#define MIN_SECTOR_BACKUP 16 /* sectors */ +#define JIGGLE_MODULO 15 /* sectors */ +#define MIN_SILENCE_BOUNDARY 1024 /* 16 bit words */ + +#define min(x, y) ((x) > (y)?(y):(x)) +#define max(x, y) ((x) < (y)?(y):(x)) + +#include "isort.h" + +typedef struct linked_list { + /* linked list */ + struct linked_element *head; + struct linked_element *tail; + + void *(*new_poly)(void); + void (*free_poly)(void *poly); + long current; + long active; + +} linked_list; + +typedef struct linked_element { + void *ptr; + struct linked_element *prev; + struct linked_element *next; + + struct linked_list *list; + int stamp; +} linked_element; + +extern linked_list *new_list(void *(*newp) (void), + void (*freep) (void *)); +extern linked_element *new_elem(linked_list *list); +extern linked_element *add_elem(linked_list *list, void *elem); +extern void free_list(linked_list *list, int free_ptr); /* unlink or free */ +extern void free_elem(linked_element *e, int free_ptr); /* unlink or free */ +extern void *get_elem(linked_element *e); +extern linked_list *copy_list(linked_list *list); /* shallow; doesn't copy */ + /* contained structures */ + +typedef struct c_block { + /* The buffer */ + Int16_t *vector; + long begin; + long size; + + /* auxiliary support structures */ + unsigned char *flags; + /* + * 1 known boundaries in read data + * 2 known blanked data + * 4 matched sample + * 8 reserved + * 16 reserved + * 32 reserved + * 64 reserved + * 128 reserved + */ + + /* end of session cases */ + long lastsector; + struct cdrom_paranoia *p; + struct linked_element *e; +} c_block; + +extern void free_c_block(c_block *c); +extern void i_cblock_destructor(c_block *c); +extern c_block *new_c_block(struct cdrom_paranoia *p); + +typedef struct v_fragment { + c_block *one; + + long begin; + long size; + Int16_t *vector; + + /* end of session cases */ + long lastsector; + + /* linked list */ + struct cdrom_paranoia *p; + struct linked_element *e; + +} v_fragment; + +extern void free_v_fragment(v_fragment *c); +extern v_fragment *new_v_fragment(struct cdrom_paranoia *p, c_block *one, + long begin, long end, + int lastsector); +extern Int16_t *v_buffer(v_fragment *v); + +extern c_block *c_first(struct cdrom_paranoia *p); +extern c_block *c_last(struct cdrom_paranoia *p); +extern c_block *c_next(c_block *c); +extern c_block *c_prev(c_block *c); + +extern v_fragment *v_first(struct cdrom_paranoia *p); +extern v_fragment *v_last(struct cdrom_paranoia *p); +extern v_fragment *v_next(v_fragment *v); +extern v_fragment *v_prev(v_fragment *v); + +typedef struct root_block { + long returnedlimit; + long lastsector; + struct cdrom_paranoia *p; + + c_block *vector; /* doesn't use any sorting */ + int silenceflag; + long silencebegin; +} root_block; + +typedef struct offsets { + long offpoints; + long newpoints; + long offaccum; + long offdiff; + long offmin; + long offmax; + +} offsets; + +typedef struct cdrom_paranoia { + void *d; /* A pointer to the driver interface */ + int nsectors; /* # of sectors that fit into DMA buf */ + + root_block root; /* verified/reconstructed cached data */ + linked_list *cache; /* our data as read from the cdrom */ + long cache_limit; + linked_list *fragments; /* fragments of blocks that have been */ + /* 'verified' */ + sort_info *sortcache; + + int readahead; /* sectors of readahead in each readop */ + int jitter; + long lastread; + + int enable; + long cursor; + long current_lastsector; + long current_firstsector; + + /* statistics for drift/overlap */ + struct offsets stage1; + struct offsets stage2; + + long mindynoverlap; + long maxdynoverlap; + long dynoverlap; + long dyndrift; + + /* statistics for verification */ + +} cdrom_paranoia; + +extern c_block *c_alloc(Int16_t *vector, long begin, long size); +extern void c_set(c_block *v, long begin); +extern void c_insert(c_block *v, long pos, Int16_t *b, long size); +extern void c_remove(c_block *v, long cutpos, long cutsize); +extern void c_overwrite(c_block *v, long pos, Int16_t *b, long size); +extern void c_append(c_block *v, Int16_t *vector, long size); +extern void c_removef(c_block *v, long cut); + +#define ce(v) ((v)->begin + (v)->size) +#define cb(v) ((v)->begin) +#define cs(v) ((v)->size) + +/* + * pos here is vector position from zero + */ +extern void recover_cache(cdrom_paranoia *p); +extern void i_paranoia_firstlast(cdrom_paranoia *p); + +#define cv(c) ((c)->vector) + +#define fe(f) ((f)->begin + (f)->size) +#define fb(f) ((f)->begin) +#define fs(f) ((f)->size) +#define fv(f) (v_buffer(f)) + +#define CDP_COMPILE +#endif diff --git a/libparanoia/paranoia.c b/libparanoia/paranoia.c new file mode 100644 index 0000000..56a7be4 --- /dev/null +++ b/libparanoia/paranoia.c @@ -0,0 +1,1741 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)paranoia.c 1.33 04/08/17 J. Schilling from cdparanoia-III-alpha9.8 */ +/* + * Modifications to make the code portable Copyright (c) 2002 J. Schilling + */ +/* + * CopyPolicy: GNU Public License 2 applies + * Copyright (C) by Monty (xiphmont@mit.edu) + * + * Toplevel file for the paranoia abstraction over the cdda lib + * + */ + +/* immediate todo:: */ +/* Allow disabling of root fixups? */ + +/* + * Dupe bytes are creeping into cases that require greater overlap + * than a single fragment can provide. We need to check against a + * larger area* (+/-32 sectors of root?) to better eliminate + * dupes. Of course this leads to other problems... Is it actually a + * practically solvable problem? + */ +/* Bimodal overlap distributions break us. */ +/* scratch detection/tolerance not implemented yet */ + +/* + * Da new shtick: verification now a two-step assymetric process. + * + * A single 'verified/reconstructed' data segment cache, and then the + * multiple fragment cache + * + * verify a newly read block against previous blocks; do it only this + * once. We maintain a list of 'verified sections' from these matches. + * + * We then glom these verified areas into a new data buffer. + * Defragmentation fixups are allowed here alone. + * + * We also now track where read boundaries actually happened; do not + * verify across matching boundaries. + */ + +/* + * Silence. "It's BAAAAAAaaack." + * + * audio is now treated as great continents of values floating on a + * mantle of molten silence. Silence is not handled by basic + * verification at all; we simply anchor sections of nonzero audio to a + * position and fill in everything else as silence. We also note the + * audio that interfaces with silence; an edge must be 'wet'. + */ + +#include <mconfig.h> +#include <allocax.h> +#include <stdxlib.h> +#include <unixstd.h> +#include <standard.h> +#include <utypes.h> +#include <stdio.h> +#include <strdefs.h> +#include "p_block.h" +#include "cdda_paranoia.h" +#include "overlap.h" +#include "gap.h" +#include "isort.h" +#include "pmalloc.h" + +/* + * used by: i_iterate_stage2() / i_stage2_each() + */ +typedef struct sync_result { + long offset; + long begin; + long end; +} sync_result; + +static inline long re(root_block *root); +static inline long rb(root_block *root); +static inline long rs(root_block *root); +static inline Int16_t *rv(root_block *root); +static inline long i_paranoia_overlap(Int16_t *buffA, Int16_t *buffB, + long offsetA, long offsetB, + long sizeA, long sizeB, + long *ret_begin, long *ret_end); +static inline long i_paranoia_overlap2(Int16_t *buffA, Int16_t *buffB, + Uchar *flagsA, Uchar *flagsB, + long offsetA, long offsetB, + long sizeA, long sizeB, + long *ret_begin, long *ret_end); +static inline long do_const_sync(c_block *A, + sort_info *B, Uchar *flagB, + long posA, long posB, + long *begin, long *end, long *offset); +static inline long try_sort_sync(cdrom_paranoia *p, + sort_info *A, Uchar *Aflags, + c_block *B, + long post, long *begin, long *end, + long *offset, void (*callback) (long, int)); +static inline void stage1_matched(c_block *old, c_block *new, + long matchbegin, long matchend, + long matchoffset, + void (*callback) (long, int)); +static long i_iterate_stage1(cdrom_paranoia *p, c_block *old, c_block *new, + void (*callback) (long, int)); +static long i_stage1(cdrom_paranoia *p, c_block *new, + void (*callback) (long, int)); +static long i_iterate_stage2(cdrom_paranoia *p, v_fragment *v, sync_result *r, + void (*callback)(long, int)); +static void i_silence_test(root_block *root); +static long i_silence_match(root_block *root, v_fragment *v, + void (*callback) (long, int)); +static long i_stage2_each(root_block *root, v_fragment *v, + void (*callback) (long, int)); +static int i_init_root(root_block *root, v_fragment *v, + long begin, void (*callback)(long, int)); +static int vsort(const void *a, const void *b); +static int i_stage2(cdrom_paranoia *p, long beginword, long endword, + void (*callback)(long, int)); +static void i_end_case(cdrom_paranoia *p, long endword, + void (*callback)(long, int)); +static void verify_skip_case(cdrom_paranoia *p, + void (*callback)(long, int)); +void paranoia_free(cdrom_paranoia *p); +void paranoia_modeset(cdrom_paranoia *p, int enable); +long paranoia_seek(cdrom_paranoia *p, long seek, int mode); +c_block *i_read_c_block(cdrom_paranoia *p, long beginword, long endword, + void (*callback)(long,int)); +Int16_t *paranoia_read(cdrom_paranoia *p, void (*callback)(long, int)); +Int16_t *paranoia_read_limited(cdrom_paranoia *p, void (*callback)(long, int), + int max_retries); +void paranoia_overlapset(cdrom_paranoia *p, long overlap); + + +static inline long re(root_block *root) +{ + if (!root) + return (-1); + if (!root->vector) + return (-1); + return (ce(root->vector)); +} + +static inline long rb(root_block *root) +{ + if (!root) + return (-1); + if (!root->vector) + return (-1); + return (cb(root->vector)); +} + +static inline long rs(root_block *root) +{ + if (!root) + return (-1); + if (!root->vector) + return (-1); + return (cs(root->vector)); +} + +static inline Int16_t *rv(root_block *root) +{ + if (!root) + return (NULL); + if (!root->vector) + return (NULL); + return (cv(root->vector)); +} + +#define rc(r) ((r)->vector) + +/* + * matching and analysis code + */ +static inline long +i_paranoia_overlap(Int16_t *buffA, Int16_t *buffB, long offsetA, long offsetB, + long sizeA, long sizeB, long *ret_begin, long *ret_end) +{ + long beginA = offsetA; + long endA = offsetA; + long beginB = offsetB; + long endB = offsetB; + + for (; beginA >= 0 && beginB >= 0; beginA--, beginB--) + if (buffA[beginA] != buffB[beginB]) + break; + beginA++; + beginB++; + + for (; endA < sizeA && endB < sizeB; endA++, endB++) + if (buffA[endA] != buffB[endB]) + break; + + if (ret_begin) + *ret_begin = beginA; + if (ret_end) + *ret_end = endA; + return (endA - beginA); +} + +static inline long +i_paranoia_overlap2(Int16_t *buffA, Int16_t *buffB, Uchar *flagsA, + Uchar *flagsB, long offsetA, long offsetB, long sizeA, + long sizeB, long *ret_begin, long *ret_end) +{ + long beginA = offsetA; + long endA = offsetA; + long beginB = offsetB; + long endB = offsetB; + + for (; beginA >= 0 && beginB >= 0; beginA--, beginB--) { + if (buffA[beginA] != buffB[beginB]) + break; + /* + * don't allow matching across matching sector boundaries + */ + if ((flagsA[beginA] & flagsB[beginB] & 1)) { + beginA--; + beginB--; + break; + } + /* + * don't allow matching through known missing data + */ + if ((flagsA[beginA] & 2) || (flagsB[beginB] & 2)) + break; + } + beginA++; + beginB++; + + for (; endA < sizeA && endB < sizeB; endA++, endB++) { + if (buffA[endA] != buffB[endB]) + break; + /* + * don't allow matching across matching sector boundaries + */ + if ((flagsA[endA] & flagsB[endB] & 1) && endA != beginA) { + break; + } + /* + * don't allow matching through known missing data + */ + if ((flagsA[endA] & 2) || (flagsB[endB] & 2)) + break; + } + + if (ret_begin) + *ret_begin = beginA; + if (ret_end) + *ret_end = endA; + return (endA - beginA); +} + +/* Top level of the first stage matcher */ + +/* + * We match each analysis point of new to the preexisting blocks + * recursively. We can also optionally maintain a list of fragments of + * the preexisting block that didn't match anything, and match them back + * afterward. + */ +#define OVERLAP_ADJ (MIN_WORDS_OVERLAP/2-1) + +static inline long +do_const_sync(c_block *A, sort_info *B, Uchar *flagB, long posA, long posB, + long *begin, long *end, long *offset) +{ + Uchar *flagA = A->flags; + long ret = 0; + + if (flagB == NULL) + ret = i_paranoia_overlap(cv(A), iv(B), posA, posB, + cs(A), is(B), begin, end); + else if ((flagB[posB] & 2) == 0) + ret = i_paranoia_overlap2(cv(A), iv(B), flagA, flagB, posA, posB, cs(A), + is(B), begin, end); + + if (ret > MIN_WORDS_SEARCH) { + *offset = (posA + cb(A)) - (posB + ib(B)); + *begin += cb(A); + *end += cb(A); + return (ret); + } + return (0); +} + +/* + * post is w.r.t. B. in stage one, we post from old. In stage 2 we + * post from root. Begin, end, offset count from B's frame of + * reference + */ +static inline long +try_sort_sync(cdrom_paranoia *p, sort_info *A, Uchar *Aflags, c_block *B, + long post, long *begin, long *end, long *offset, + void (*callback)(long, int)) +{ + + long dynoverlap = p->dynoverlap; + sort_link *ptr = NULL; + Uchar *Bflags = B->flags; + + /* + * block flag matches 0x02 (unmatchable) + */ + if (Bflags == NULL || (Bflags[post - cb(B)] & 2) == 0) { + /* + * always try absolute offset zero first! + */ + { + long zeropos = post - ib(A); + + if (zeropos >= 0 && zeropos < is(A)) { + if (cv(B)[post - cb(B)] == iv(A)[zeropos]) { + if (do_const_sync(B, A, Aflags, + post - cb(B), zeropos, + begin, end, offset)) { + + offset_add_value(p, &(p->stage1), *offset, callback); + + return (1); + } + } + } + } + } else + return (0); + + ptr = sort_getmatch(A, post - ib(A), dynoverlap, cv(B)[post - cb(B)]); + + while (ptr) { + + if (do_const_sync(B, A, Aflags, + post - cb(B), ipos(A, ptr), + begin, end, offset)) { + offset_add_value(p, &(p->stage1), *offset, callback); + return (1); + } + ptr = sort_nextmatch(A, ptr); + } + + *begin = -1; + *end = -1; + *offset = -1; + return (0); +} + +static inline void +stage1_matched(c_block *old, c_block *new, long matchbegin, long matchend, + long matchoffset, void (*callback)(long, int)) +{ + long i; + long oldadjbegin = matchbegin - cb(old); + long oldadjend = matchend - cb(old); + long newadjbegin = matchbegin - matchoffset - cb(new); + long newadjend = matchend - matchoffset - cb(new); + + if (matchbegin - matchoffset <= cb(new) || + matchbegin <= cb(old) || + (new->flags[newadjbegin] & 1) || + (old->flags[oldadjbegin] & 1)) { + if (matchoffset) + if (callback) + (*callback) (matchbegin, PARANOIA_CB_FIXUP_EDGE); + } else if (callback) + (*callback) (matchbegin, PARANOIA_CB_FIXUP_ATOM); + + if (matchend - matchoffset >= ce(new) || + (new->flags[newadjend] & 1) || + matchend >= ce(old) || + (old->flags[oldadjend] & 1)) { + if (matchoffset) + if (callback) + (*callback) (matchend, PARANOIA_CB_FIXUP_EDGE); + } else if (callback) + (*callback) (matchend, PARANOIA_CB_FIXUP_ATOM); + + /* + * Mark the verification flags. Don't mark the first or last OVERLAP/2 + * elements so that overlapping fragments have to overlap by OVERLAP to + * actually merge. We also remove elements from the sort such that + * later sorts do not have to sift through already matched data + */ + newadjbegin += OVERLAP_ADJ; + newadjend -= OVERLAP_ADJ; + for (i = newadjbegin; i < newadjend; i++) + new->flags[i] |= 4; /* mark verified */ + + oldadjbegin += OVERLAP_ADJ; + oldadjend -= OVERLAP_ADJ; + for (i = oldadjbegin; i < oldadjend; i++) + old->flags[i] |= 4; /* mark verified */ + +} + +#define CB_NULL (void (*)(long, int))0 + +static long +i_iterate_stage1(cdrom_paranoia *p, c_block *old, c_block *new, + void (*callback)(long, int)) +{ + + long matchbegin = -1; + long matchend = -1; + long matchoffset; + + /* + * we no longer try to spread the stage one search area by dynoverlap + */ + long searchend = min(ce(old), ce(new)); + long searchbegin = max(cb(old), cb(new)); + long searchsize = searchend - searchbegin; + sort_info *i = p->sortcache; + long ret = 0; + long j; + + long tried = 0; + long matched = 0; + + if (searchsize <= 0) + return (0); + + /* + * match return values are in terms of the new vector, not old + */ + for (j = searchbegin; j < searchend; j += 23) { + if ((new->flags[j - cb(new)] & 6) == 0) { + tried++; + if (try_sort_sync(p, i, new->flags, old, j, &matchbegin, &matchend, &matchoffset, + callback) == 1) { + + matched += matchend - matchbegin; + + /* + * purely cosmetic: if we're matching zeros, + * don't use the callback because they will + * appear to be all skewed + */ + { + long jj = matchbegin - cb(old); + long end = matchend - cb(old); + + for (; jj < end; jj++) + if (cv(old)[jj] != 0) + break; + if (jj < end) { + stage1_matched(old, new, matchbegin, matchend, matchoffset, callback); + } else { + stage1_matched(old, new, matchbegin, matchend, matchoffset, CB_NULL); + } + } + ret++; + if (matchend - 1 > j) + j = matchend - 1; + } + } + } +#ifdef NOISY + fprintf(stderr, "iterate_stage1: search area=%ld[%ld-%ld] tried=%ld matched=%ld spans=%ld\n", + searchsize, searchbegin, searchend, tried, matched, ret); +#endif + + return (ret); +} + +static long +i_stage1(cdrom_paranoia *p, c_block *new, void (*callback)(long, int)) +{ + + long size = cs(new); + c_block *ptr = c_last(p); + int ret = 0; + long begin = 0; + long end; + + if (ptr) + sort_setup(p->sortcache, cv(new), &cb(new), cs(new), + cb(new), ce(new)); + + while (ptr && ptr != new) { + + if (callback) + (*callback) (cb(new), PARANOIA_CB_VERIFY); + i_iterate_stage1(p, ptr, new, callback); + + ptr = c_prev(ptr); + } + + /* + * parse the verified areas of new into v_fragments + */ + begin = 0; + while (begin < size) { + for (; begin < size; begin++) + if (new->flags[begin] & 4) + break; + for (end = begin; end < size; end++) + if ((new->flags[end] & 4) == 0) + break; + if (begin >= size) + break; + + ret++; + + new_v_fragment(p, new, cb(new) + max(0, begin - OVERLAP_ADJ), + cb(new) + min(size, end + OVERLAP_ADJ), + (end + OVERLAP_ADJ >= size && new->lastsector)); + + begin = end; + } + + return (ret); +} + +/* + * reconcile v_fragments to root buffer. Free if matched, fragment/fixup root + * if necessary + * + * do *not* match using zero posts + */ +static long +i_iterate_stage2(cdrom_paranoia *p, v_fragment *v, sync_result *r, + void (*callback)(long, int)) +{ + root_block *root = &(p->root); + long matchbegin = -1; + long matchend = -1; + long offset; + long fbv; + long fev; + +#ifdef NOISY + fprintf(stderr, "Stage 2 search: fbv=%ld fev=%ld\n", fb(v), fe(v)); +#endif + + if (min(fe(v) + p->dynoverlap, re(root)) - + max(fb(v) - p->dynoverlap, rb(root)) <= 0) + return (0); + + if (callback) + (*callback) (fb(v), PARANOIA_CB_VERIFY); + + /* + * just a bit of v; determine the correct area + */ + fbv = max(fb(v), rb(root) - p->dynoverlap); + + /* + * we want to avoid zeroes + */ + while (fbv < fe(v) && fv(v)[fbv - fb(v)] == 0) + fbv++; + if (fbv == fe(v)) + return (0); + fev = min(min(fbv + 256, re(root) + p->dynoverlap), fe(v)); + + { + /* + * spread the search area a bit. We post from root, so + * containment must strictly adhere to root + */ + long searchend = min(fev + p->dynoverlap, re(root)); + long searchbegin = max(fbv - p->dynoverlap, rb(root)); + sort_info *i = p->sortcache; + long j; + + sort_setup(i, fv(v), &fb(v), fs(v), fbv, fev); + for (j = searchbegin; j < searchend; j += 23) { + while (j < searchend && rv(root)[j - rb(root)] == 0) + j++; + if (j == searchend) + break; + + if (try_sort_sync(p, i, (Uchar *)0, rc(root), j, + &matchbegin, &matchend, &offset, callback)) { + + r->begin = matchbegin; + r->end = matchend; + r->offset = -offset; + if (offset) + if (callback) + (*callback) (r->begin, PARANOIA_CB_FIXUP_EDGE); + return (1); + } + } + } + return (0); +} + +/* + * simple test for a root vector that ends in silence + */ +static void i_silence_test(root_block *root) +{ + Int16_t *vec = rv(root); + long end = re(root) - rb(root) - 1; + long j; + + for (j = end - 1; j >= 0; j--) + if (vec[j] != 0) + break; + if (j < 0 || end - j > MIN_SILENCE_BOUNDARY) { + if (j < 0) + j = 0; + root->silenceflag = 1; + root->silencebegin = rb(root) + j; + if (root->silencebegin < root->returnedlimit) + root->silencebegin = root->returnedlimit; + } +} + +/* + * match into silence vectors at offset zero if at all possible. This + * also must be called with vectors in ascending begin order in case + * there are nonzero islands + */ +static long +i_silence_match(root_block *root, v_fragment *v, void (*callback)(long, int)) +{ + + cdrom_paranoia *p = v->p; + Int16_t *vec = fv(v); + long end = fs(v); + long begin; + long j; + + /* + * does this vector begin wet? + */ + if (end < MIN_SILENCE_BOUNDARY) + return (0); + for (j = 0; j < end; j++) + if (vec[j] != 0) + break; + if (j < MIN_SILENCE_BOUNDARY) + return (0); + j += fb(v); + + /* + * is the new silent section ahead of the end of the old + * by < p->dynoverlap? + */ + if (fb(v) >= re(root) && fb(v) - p->dynoverlap < re(root)) { + /* + * extend the zeroed area of root + * XXX dynarrays are not needed here. + */ + long addto = fb(v) + MIN_SILENCE_BOUNDARY - re(root); +/* Int16_t avec[addto];*/ +#ifdef HAVE_ALLOCA + Int16_t *avec = alloca(addto * sizeof (Int16_t)); +#else + Int16_t *avec = _pmalloc(addto * sizeof (Int16_t)); +#endif + + memset(avec, 0, sizeof (avec)); + c_append(rc(root), avec, addto); +#ifndef HAVE_ALLOCA + _pfree(avec); +#endif + } + /* + * do we have an 'effortless' overlap? + */ + begin = max(fb(v), root->silencebegin); + end = min(j, re(root)); + + if (begin < end) { + /* + * don't use it unless it will extend... + */ + if (fe(v) > re(root)) { + long voff = begin - fb(v); + + c_remove(rc(root), begin - rb(root), -1); + c_append(rc(root), vec + voff, fs(v) - voff); + } + offset_add_value(p, &p->stage2, 0, callback); + + } else { + if (j < begin) { + /* + * OK, we'll have to force it a bit as the root is + * jittered forward + */ + long voff = j - fb(v); + + /* + * don't use it unless it will extend... + */ + if (begin + fs(v) - voff > re(root)) { + c_remove(rc(root), root->silencebegin - rb(root), -1); + c_append(rc(root), vec + voff, fs(v) - voff); + } + offset_add_value(p, &p->stage2, end - begin, callback); + } else + return (0); + } + + /* + * test the new root vector for ending in silence + */ + root->silenceflag = 0; + i_silence_test(root); + + if (v->lastsector) + root->lastsector = 1; + free_v_fragment(v); + return (1); +} + +static long +i_stage2_each(root_block *root, v_fragment *v, void (*callback)(long, int)) +{ + + cdrom_paranoia *p = v->p; + long dynoverlap = p->dynoverlap / 2 * 2; + + if (!v || !v->one) + return (0); + + if (!rv(root)) { + return (0); + } else { + sync_result r; + + if (i_iterate_stage2(p, v, &r, callback)) { + + long begin = r.begin - rb(root); + long end = r.end - rb(root); + long offset = r.begin + r.offset - fb(v) - begin; + long temp; + c_block *l = NULL; + + /* + * we have a match! We don't rematch off rift, we chase + * the match all the way to both extremes doing rift + * analysis. + */ +#ifdef NOISY + fprintf(stderr, "Stage 2 match\n"); +#endif + + /* + * chase backward + * note that we don't extend back right now, only + * forward. + */ + while ((begin + offset > 0 && begin > 0)) { + long matchA = 0, + matchB = 0, + matchC = 0; + long beginL = begin + offset; + + if (l == NULL) { + Int16_t *buff = _pmalloc(fs(v) * sizeof (Int16_t)); + + l = c_alloc(buff, fb(v), fs(v)); + memcpy(buff, fv(v), fs(v) * sizeof (Int16_t)); + } + i_analyze_rift_r(rv(root), cv(l), + rs(root), cs(l), + begin - 1, beginL - 1, + &matchA, &matchB, &matchC); + +#ifdef NOISY + fprintf(stderr, "matching rootR: matchA:%ld matchB:%ld matchC:%ld\n", + matchA, matchB, matchC); +#endif + + if (matchA) { + /* + * a problem with root + */ + if (matchA > 0) { + /* + * dropped bytes; add back from v + */ + if (callback) + (*callback) (begin + rb(root) - 1, PARANOIA_CB_FIXUP_DROPPED); + if (rb(root) + begin < p->root.returnedlimit) + break; + else { + c_insert(rc(root), begin, cv(l) + beginL - matchA, + matchA); + offset -= matchA; + begin += matchA; + end += matchA; + } + } else { + /* + * duplicate bytes; drop from root + */ + if (callback) + (*callback) (begin + rb(root) - 1, PARANOIA_CB_FIXUP_DUPED); + if (rb(root) + begin + matchA < p->root.returnedlimit) + break; + else { + c_remove(rc(root), begin + matchA, -matchA); + offset -= matchA; + begin += matchA; + end += matchA; + } + } + } else if (matchB) { + /* + * a problem with the fragment + */ + if (matchB > 0) { + /* + * dropped bytes + */ + if (callback) + (*callback) (begin + rb(root) - 1, PARANOIA_CB_FIXUP_DROPPED); + c_insert(l, beginL, rv(root) + begin - matchB, + matchB); + offset += matchB; + } else { + /* + * duplicate bytes + */ + if (callback) + (*callback) (begin + rb(root) - 1, PARANOIA_CB_FIXUP_DUPED); + c_remove(l, beginL + matchB, -matchB); + offset += matchB; + } + } else if (matchC) { + /* + * Uhh... problem with both + * Set 'disagree' flags in root + */ + if (rb(root) + begin - matchC < p->root.returnedlimit) + break; + c_overwrite(rc(root), begin - matchC, + cv(l) + beginL - matchC, matchC); + + } else { + /* + * do we have a mismatch due to silence + * beginning/end case? + * in the 'chase back' case, we don't + * do anything. + * Did not determine nature of + * difficulty... report and bail + */ + /* RRR(*callback)(post,PARANOIA_CB_XXX); */ + break; + } + /* + * not the most efficient way, but it will do + * for now + */ + beginL = begin + offset; + i_paranoia_overlap(rv(root), cv(l), + begin, beginL, + rs(root), cs(l), + &begin, &end); + } + + /* + * chase forward + */ + temp = l ? cs(l) : fs(v); + while (end + offset < temp && end < rs(root)) { + long matchA = 0, + matchB = 0, + matchC = 0; + long beginL = begin + offset; + long endL = end + offset; + + if (l == NULL) { + Int16_t *buff = _pmalloc(fs(v) * sizeof (Int16_t)); + + l = c_alloc(buff, fb(v), fs(v)); + memcpy(buff, fv(v), fs(v) * sizeof (Int16_t)); + } + i_analyze_rift_f(rv(root), cv(l), + rs(root), cs(l), + end, endL, + &matchA, &matchB, &matchC); + +#ifdef NOISY + fprintf(stderr, "matching rootF: matchA:%ld matchB:%ld matchC:%ld\n", + matchA, matchB, matchC); +#endif + + if (matchA) { + /* + * a problem with root + */ + if (matchA > 0) { + /* + * dropped bytes; add back from v + */ + if (callback) + (*callback) (end + rb(root), PARANOIA_CB_FIXUP_DROPPED); + if (end + rb(root) < p->root.returnedlimit) + break; + c_insert(rc(root), end, cv(l) + endL, matchA); + } else { + /* + * duplicate bytes; drop from root + */ + if (callback) + (*callback) (end + rb(root), PARANOIA_CB_FIXUP_DUPED); + if (end + rb(root) < p->root.returnedlimit) + break; + c_remove(rc(root), end, -matchA); + } + } else if (matchB) { + /* + * a problem with the fragment + */ + if (matchB > 0) { + /* + * dropped bytes + */ + if (callback) + (*callback) (end + rb(root), PARANOIA_CB_FIXUP_DROPPED); + c_insert(l, endL, rv(root) + end, matchB); + } else { + /* + * duplicate bytes + */ + if (callback) + (*callback) (end + rb(root), PARANOIA_CB_FIXUP_DUPED); + c_remove(l, endL, -matchB); + } + } else if (matchC) { + /* + * Uhh... problem with both + * Set 'disagree' flags in root + */ + if (end + rb(root) < p->root.returnedlimit) + break; + c_overwrite(rc(root), end, cv(l) + endL, matchC); + } else { + analyze_rift_silence_f(rv(root), cv(l), + rs(root), cs(l), + end, endL, + &matchA, &matchB); + if (matchA) { + /* + * silence in root + * Can only do this if we haven't + * already returned data + */ + if (end + rb(root) >= p->root.returnedlimit) { + c_remove(rc(root), end, -1); + } + } else if (matchB) { + /* + * silence in fragment; lose it + */ + if (l) + i_cblock_destructor(l); + free_v_fragment(v); + return (1); + + } else { + /* + * Could not determine nature of + * difficulty... report and bail + */ + /* RRR(*callback)(post,PARANOIA_CB_XXX); */ + } + break; + } + /* + * not the most efficient way, but it will do for now + */ + i_paranoia_overlap(rv(root), cv(l), + begin, beginL, + rs(root), cs(l), + (long *)0, &end); + } + + /* + * if this extends our range, let's glom + */ + { + long sizeA = rs(root); + long sizeB; + long vecbegin; + Int16_t *vector; + + if (l) { + sizeB = cs(l); + vector = cv(l); + vecbegin = cb(l); + } else { + sizeB = fs(v); + vector = fv(v); + vecbegin = fb(v); + } + + if (sizeB - offset > sizeA || v->lastsector) { + if (v->lastsector) { + root->lastsector = 1; + } + if (end < sizeA) + c_remove(rc(root), end, -1); + + if (sizeB - offset - end) + c_append(rc(root), vector + end + offset, + sizeB - offset - end); + + i_silence_test(root); + + /* + * add offset into dynoverlap stats + */ + offset_add_value(p, &p->stage2, offset + vecbegin - rb(root), callback); + } + } + if (l) + i_cblock_destructor(l); + free_v_fragment(v); + return (1); + + } else { + /* + * D'oh. No match. What to do with the fragment? + */ + if (fe(v) + dynoverlap < re(root) && !root->silenceflag) { + /* + * It *should* have matched. No good; free it. + */ + free_v_fragment(v); + } + /* + * otherwise, we likely want this for an upcoming match + * we don't free the sort info (if it was collected) + */ + return (0); + + } + } +} + +static int +i_init_root(root_block *root, v_fragment *v, long begin, + void (*callback)(long, int)) +{ + if (fb(v) <= begin && fe(v) > begin) { + + root->lastsector = v->lastsector; + root->returnedlimit = begin; + + if (rv(root)) { + i_cblock_destructor(rc(root)); + rc(root) = NULL; + } + { + Int16_t *buff = _pmalloc(fs(v) * sizeof (Int16_t)); + + memcpy(buff, fv(v), fs(v) * sizeof (Int16_t)); + root->vector = c_alloc(buff, fb(v), fs(v)); + } + i_silence_test(root); + + return (1); + } else + return (0); +} + +static int vsort(const void *a, const void *b) +{ + return ((*(v_fragment **) a)->begin - (*(v_fragment **) b)->begin); +} + +static int +i_stage2(cdrom_paranoia *p, long beginword, long endword, + void (*callback)(long, int)) +{ + + int flag = 1; + int ret = 0; + root_block *root = &(p->root); + +#ifdef NOISY + fprintf(stderr, "Fragments:%ld\n", p->fragments->active); + fflush(stderr); +#endif + + /* + * even when the 'silence flag' is lit, we try to do non-silence + * matching in the event that there are still audio vectors with + * content to be sunk before the silence + */ + while (flag) { + /* + * loop through all the current fragments + */ + v_fragment *first = v_first(p); + long active = p->fragments->active, + count = 0; +#ifdef HAVE_DYN_ARRAYS + v_fragment *list[active]; +#else + v_fragment **list = _pmalloc(active * sizeof (v_fragment *)); +#endif + + while (first) { + v_fragment *next = v_next(first); + + list[count++] = first; + first = next; + } + + flag = 0; + if (count) { + /* + * sorted in ascending order of beginning + */ + qsort(list, active, sizeof (v_fragment *), vsort); + + /* + * we try a nonzero based match even if in silent mode + * in the case that there are still cached vectors to + * sink behind continent->ocean boundary + */ + for (count = 0; count < active; count++) { + first = list[count]; + if (first->one) { + if (rv(root) == NULL) { + if (i_init_root(&(p->root), first, beginword, callback)) { + free_v_fragment(first); + flag = 1; + ret++; + } + } else { + if (i_stage2_each(root, first, callback)) { + ret++; + flag = 1; + } + } + } + } + + /* + * silence handling + */ + if (!flag && p->root.silenceflag) { + for (count = 0; count < active; count++) { + first = list[count]; + if (first->one) { + if (rv(root) != NULL) { + if (i_silence_match(root, first, callback)) { + ret++; + flag = 1; + } + } + } + } + } + } +#ifndef HAVE_DYN_ARRAYS + _pfree(list); +#endif + } + return (ret); +} + +static void +i_end_case(cdrom_paranoia *p, long endword, void (*callback)(long, int)) +{ + + root_block *root = &p->root; + + /* + * have an 'end' flag; if we've just read in the last sector in a + * session, set the flag. If we verify to the end of a fragment + * which has the end flag set, we're done (set a done flag). + * Pad zeroes to the end of the read + */ + if (root->lastsector == 0) + return; + if (endword < re(root)) + return; + + { + long addto = endword - re(root); + char *temp = _pcalloc(addto, sizeof (char) * 2); + + c_append(rc(root), (void *) temp, addto); + _pfree(temp); + + /* + * trash da cache + */ + paranoia_resetcache(p); + + } +} + +/* + * We want to add a sector. Look through the caches for something that + * spans. Also look at the flags on the c_block... if this is an + * obliterated sector, get a bit of a chunk past the obliteration. + * + * Not terribly smart right now, actually. We can probably find + * *some* match with a cache block somewhere. Take it and continue it + * through the skip + */ +static void +verify_skip_case(cdrom_paranoia *p, void (*callback)(long, int)) +{ + + root_block *root = &(p->root); + c_block *graft = NULL; + int vflag = 0; + int gend = 0; + long post; + +#ifdef NOISY + fprintf(stderr, "\nskipping\n"); +#endif + + if (rv(root) == NULL) { + post = 0; + } else { + post = re(root); + } + if (post == -1) + post = 0; + + if (callback) + (*callback) (post, PARANOIA_CB_SKIP); + + if (p->enable & PARANOIA_MODE_NEVERSKIP) + return; + + /* + * We want to add a sector. Look for a c_block that spans, + * preferrably a verified area + */ + { + c_block *c = c_first(p); + + while (c) { + long cbegin = cb(c); + long cend = ce(c); + + if (cbegin <= post && cend > post) { + long vend = post; + + if (c->flags[post - cbegin] & 4) { + /* + * verified area! + */ + while (vend < cend && (c->flags[vend - cbegin] & 4)) + vend++; + if (!vflag || vend > vflag) { + graft = c; + gend = vend; + } + vflag = 1; + } else { + /* + * not a verified area + */ + if (!vflag) { + while (vend < cend && (c->flags[vend - cbegin] & 4) == 0) + vend++; + if (graft == NULL || gend > vend) { + /* + * smallest unverified area + */ + graft = c; + gend = vend; + } + } + } + } + c = c_next(c); + } + + if (graft) { + long cbegin = cb(graft); + long cend = ce(graft); + + while (gend < cend && (graft->flags[gend - cbegin] & 4)) + gend++; + gend = min(gend + OVERLAP_ADJ, cend); + + if (rv(root) == NULL) { + Int16_t *buff = _pmalloc(cs(graft)); + + memcpy(buff, cv(graft), cs(graft)); + rc(root) = c_alloc(buff, cb(graft), cs(graft)); + } else { + c_append(rc(root), cv(graft) + post - cbegin, + gend - post); + } + + root->returnedlimit = re(root); + return; + } + } + + /* + * No? Fine. Great. Write in some zeroes :-P + */ + { + void *temp = _pcalloc(CD_FRAMESIZE_RAW, sizeof (Int16_t)); + + if (rv(root) == NULL) { + rc(root) = c_alloc(temp, post, CD_FRAMESIZE_RAW); + } else { + c_append(rc(root), temp, CD_FRAMESIZE_RAW); + _pfree(temp); + } + root->returnedlimit = re(root); + } +} + +/* + * toplevel + */ +void paranoia_free(cdrom_paranoia *p) +{ + paranoia_resetall(p); + sort_free(p->sortcache); + _pfree(p); +} + +void paranoia_modeset(cdrom_paranoia *p, int enable) +{ + p->enable = enable; +} + +long paranoia_seek(cdrom_paranoia *p, long seek, int mode) +{ + long sector; + long ret; + + switch (mode) { + case SEEK_SET: + sector = seek; + break; + case SEEK_END: + sector = cdda_disc_lastsector(p->d) + seek; + break; + default: + sector = p->cursor + seek; + break; + } + + if (cdda_sector_gettrack(p->d, sector) == -1) + return (-1); + + i_cblock_destructor(p->root.vector); + p->root.vector = NULL; + p->root.lastsector = 0; + p->root.returnedlimit = 0; + + ret = p->cursor; + p->cursor = sector; + + i_paranoia_firstlast(p); + + /* + * Evil hack to fix pregap patch for NEC drives! To be rooted out in a10 + */ + p->current_firstsector = sector; + + return (ret); +} + +/* + * returns last block read, -1 on error + */ +c_block *i_read_c_block(cdrom_paranoia *p, long beginword, long endword, + void (*callback)(long, int)) +{ + /* + * why do it this way? We need to read lots of sectors to kludge + * around stupid read ahead buffers on cheap drives, as well as avoid + * expensive back-seeking. We also want to 'jiggle' the start address + * to try to break borderline drives more noticeably (and make broken + * drives with unaddressable sectors behave more often). + */ + long readat; + long firstread; + long totaltoread = p->readahead; + long sectatonce = p->nsectors; + long driftcomp = (float) p->dyndrift / CD_FRAMEWORDS + .5; + c_block *new = NULL; + root_block *root = &p->root; + Int16_t *buffer = NULL; + void *bufbase = NULL; + Uchar *flags = NULL; + long sofar; + long dynoverlap = (p->dynoverlap + CD_FRAMEWORDS - 1) / CD_FRAMEWORDS; + long anyflag = 0; + int reduce = 0; +static int pagesize = -1; +#define valign(x, a) (((char *)(x)) + ((a) - 1 - ((((UIntptr_t)(x))-1)%(a)))) + + /* + * What is the first sector to read? want some pre-buffer if we're not + * at the extreme beginning of the disc + */ + if (p->enable & (PARANOIA_MODE_VERIFY | PARANOIA_MODE_OVERLAP)) { + + /* + * we want to jitter the read alignment boundary + */ + long target; + + if (rv(root) == NULL || rb(root) > beginword) + target = p->cursor - dynoverlap; + else + target = re(root) / (CD_FRAMEWORDS) - dynoverlap; + + if (target + MIN_SECTOR_BACKUP > p->lastread && target <= p->lastread) + target = p->lastread - MIN_SECTOR_BACKUP; + + /* + * we want to jitter the read alignment boundary, as some + * drives, beginning from a specific point, will tend to + * lose bytes between sectors in the same place. Also, as + * our vectors are being made up of multiple reads, we want + * the overlap boundaries to move.... + */ + readat = (target & (~((long) JIGGLE_MODULO - 1))) + p->jitter; + if (readat > target) + readat -= JIGGLE_MODULO; + p->jitter++; + if (p->jitter >= JIGGLE_MODULO) + p->jitter = 0; + + } else { + readat = p->cursor; + } + + readat += driftcomp; + + if (p->enable & (PARANOIA_MODE_OVERLAP | PARANOIA_MODE_VERIFY)) { + flags = _pcalloc(totaltoread * CD_FRAMEWORDS, 1); + new = new_c_block(p); + recover_cache(p); + } else { + /* + * in the case of root it's just the buffer + */ + paranoia_resetall(p); + new = new_c_block(p); + } + + /* + * Do not use valloc() as valloc() in glibc is buggy and returns memory + * that cannot be passed to free(). + */ + if (pagesize < 0) { + pagesize = getpagesize(); + if (pagesize < 0) + pagesize = 4096; /* Just a guess */ + } + reduce = pagesize / CD_FRAMESIZE_RAW; + bufbase = _pmalloc(totaltoread * CD_FRAMESIZE_RAW + pagesize); + buffer = (Int16_t *)valign(bufbase, pagesize); + sofar = 0; + firstread = -1; + + /* + * actual read loop + */ + while (sofar < totaltoread) { + long secread = sectatonce; + long adjread = readat; + long thisread; + + /* + * don't under/overflow the audio session + */ + if (adjread < p->current_firstsector) { + secread -= p->current_firstsector - adjread; + adjread = p->current_firstsector; + } + if (adjread + secread - 1 > p->current_lastsector) + secread = p->current_lastsector - adjread + 1; + + if (sofar + secread > totaltoread) + secread = totaltoread - sofar; + + /* + * If we are inside the buffer, the transfers are no longer + * page aligned. Reduce the transfer size to avoid problems. + * Such problems are definitely known to appear on FreeBSD. + */ + if ((sofar > 0) && (secread > (sectatonce - reduce))) + secread = sectatonce - reduce; + + if (secread > 0) { + + if (firstread < 0) + firstread = adjread; + if ((thisread = cdda_read(p->d, buffer + sofar * CD_FRAMEWORDS, adjread, + secread)) < secread) { + + if (thisread < 0) + thisread = 0; + + /* + * Uhhh... right. Make something up. But + * don't make us seek backward! + */ + if (callback) + (*callback) ((adjread + thisread) * CD_FRAMEWORDS, PARANOIA_CB_READERR); + memset(buffer + (sofar + thisread) * CD_FRAMEWORDS, 0, + CD_FRAMESIZE_RAW * (secread - thisread)); + if (flags) + memset(flags + (sofar + thisread) * CD_FRAMEWORDS, 2, + CD_FRAMEWORDS * (secread - thisread)); + } + if (thisread != 0) + anyflag = 1; + + if (flags && sofar != 0) { + /* + * Don't verify across overlaps that are too + * close to one another + */ + int i = 0; + + for (i = -MIN_WORDS_OVERLAP / 2; i < MIN_WORDS_OVERLAP / 2; i++) + flags[sofar * CD_FRAMEWORDS + i] |= 1; + } + p->lastread = adjread + secread; + + if (adjread + secread - 1 == p->current_lastsector) + new->lastsector = -1; + + if (callback) + (*callback) ((adjread + secread - 1) * CD_FRAMEWORDS, PARANOIA_CB_READ); + + sofar += secread; + readat = adjread + secread; + } else if (readat < p->current_firstsector) { + readat += sectatonce; + /* + * due to being before the + * readable area + */ + } else { + break; /* due to being past the readable area */ + } + } + + if (anyflag) { + new->vector = _pmalloc(totaltoread * CD_FRAMESIZE_RAW); + memcpy(new->vector, buffer, totaltoread * CD_FRAMESIZE_RAW); + _pfree(bufbase); + + new->begin = firstread * CD_FRAMEWORDS - p->dyndrift; + new->size = sofar * CD_FRAMEWORDS; + new->flags = flags; + } else { + if (new) + free_c_block(new); + if (bufbase) + _pfree(bufbase); + if (flags) + _pfree(flags); + new = NULL; + bufbase = NULL; + flags = NULL; + } + return (new); +} + +/* + * The returned buffer is *not* to be freed by the caller. It will + * persist only until the next call to paranoia_read() for this p + */ +Int16_t *paranoia_read(cdrom_paranoia *p, void (*callback)(long, int)) +{ + return (paranoia_read_limited(p, callback, 20)); +} + +/* + * I added max_retry functionality this way in order to avoid breaking any + * old apps using the nerw libs. cdparanoia 9.8 will need the updated libs, + * but nothing else will require it. + */ +Int16_t *paranoia_read_limited(cdrom_paranoia *p, void (*callback)(long, int), + int max_retries) +{ + long beginword = p->cursor * (CD_FRAMEWORDS); + long endword = beginword + CD_FRAMEWORDS; + long retry_count = 0; + long lastend = -2; + root_block *root = &p->root; + + if (beginword > p->root.returnedlimit) + p->root.returnedlimit = beginword; + lastend = re(root); + + /* + * First, is the sector we want already in the root? + */ + while (rv(root) == NULL || + rb(root) > beginword || + (re(root) < endword + p->maxdynoverlap && + p->enable & (PARANOIA_MODE_VERIFY | PARANOIA_MODE_OVERLAP)) || + re(root) < endword) { + + /* + * Nope; we need to build or extend the root verified range + */ + if (p->enable & (PARANOIA_MODE_VERIFY | PARANOIA_MODE_OVERLAP)) { + i_paranoia_trim(p, beginword, endword); + recover_cache(p); + if (rb(root) != -1 && p->root.lastsector) + i_end_case(p, endword + p->maxdynoverlap, + callback); + else + i_stage2(p, beginword, + endword + p->maxdynoverlap, + callback); + } else + i_end_case(p, endword + p->maxdynoverlap, + callback); /* only trips if we're already */ + /* done */ + + if (!(rb(root) == -1 || rb(root) > beginword || + re(root) < endword + p->maxdynoverlap)) + break; + + /* + * Hmm, need more. Read another block + */ + { + c_block *new = i_read_c_block(p, beginword, endword, callback); + + if (new) { + if (p->enable & (PARANOIA_MODE_OVERLAP | PARANOIA_MODE_VERIFY)) { + + if (p->enable & PARANOIA_MODE_VERIFY) + i_stage1(p, new, callback); + else { + /* + * just make v_fragments from the + * boundary information. + */ + long begin = 0, + end = 0; + + while (begin < cs(new)) { + while (end < cs(new) && (new->flags[begin] & 1)) + begin++; + end = begin + 1; + while (end < cs(new) && (new->flags[end] & 1) == 0) + end++; + { + new_v_fragment(p, new, begin + cb(new), + end + cb(new), + (new->lastsector && cb(new) + end == ce(new))); + } + begin = end; + } + } + + } else { + + if (p->root.vector) + i_cblock_destructor(p->root.vector); + free_elem(new->e, 0); + p->root.vector = new; + + i_end_case(p, endword + p->maxdynoverlap, + callback); + + } + } + } + + /* + * Are we doing lots of retries? ********************** + * + * Check unaddressable sectors first. There's no backoff + * here; jiggle and minimum backseek handle that for us + */ + if (rb(root) != -1 && lastend + 588 < re(root)) { + /* + * If we've not grown half a sector + */ + lastend = re(root); + retry_count = 0; + } else { + /* + * increase overlap or bail + */ + retry_count++; + + /* + * The better way to do this is to look at how many + * actual matches we're getting and what kind of gap + */ + if (retry_count % 5 == 0) { + if (p->dynoverlap == p->maxdynoverlap || + retry_count == max_retries) { + verify_skip_case(p, callback); + retry_count = 0; + } else { + if (p->stage1.offpoints != -1) { /* hack */ + p->dynoverlap *= 1.5; + if (p->dynoverlap > p->maxdynoverlap) + p->dynoverlap = p->maxdynoverlap; + if (callback) + (*callback) (p->dynoverlap, PARANOIA_CB_OVERLAP); + } + } + } + } + } + p->cursor++; + + return (rv(root) + (beginword - rb(root))); +} + +/* + * a temporary hack + */ +void paranoia_overlapset(cdrom_paranoia *p, long overlap) +{ + p->dynoverlap = overlap * CD_FRAMEWORDS; + p->stage1.offpoints = -1; +} diff --git a/libparanoia/pmalloc.c b/libparanoia/pmalloc.c new file mode 100644 index 0000000..65dee97 --- /dev/null +++ b/libparanoia/pmalloc.c @@ -0,0 +1,89 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)pmalloc.c 1.3 04/05/15 Copyright 2004 J. Schilling */ +/* + * Paranoia malloc() functions + * + * Copyright (c) 2004 J. Schilling + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <mconfig.h> +#include <stdxlib.h> +#include <standard.h> +#include <schily.h> +#include "pmalloc.h" + +#ifdef PM_ADD_DEBUG +static int madd = 8192; +static int cadd = 8192; +static int radd = 8192; +#else +static int madd = 0; +/*static int cadd = 0;*/ +static int radd = 0; +#endif + +void _pfree(void *ptr) +{ + free(ptr); +} + +void *_pmalloc(size_t size) +{ + void *p; + + p = malloc(size + madd); + if (p == NULL) + raisecond("NO MEM", 0L); + return (p); +} + +void *_pcalloc(size_t nelem, size_t elsize) +{ + void *p; +#ifdef PM_ADD_DEBUG + size_t n = nelem * elsize; + + n += cadd; + p = calloc(1, n); +#else + p = calloc(nelem, elsize); +#endif + if (p == NULL) + raisecond("NO MEM", 0L); + return (p); +} + +void *_prealloc(void *ptr, size_t size) +{ + void *p; + + p = realloc(ptr, size + radd); + if (p == NULL) + raisecond("NO MEM", 0L); + return (p); +} diff --git a/libparanoia/pmalloc.h b/libparanoia/pmalloc.h new file mode 100644 index 0000000..f64ad2f --- /dev/null +++ b/libparanoia/pmalloc.h @@ -0,0 +1,42 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)pmalloc.h 1.1 04/02/20 Copyright 2004 J. Schilling */ +/* + * Paranoia malloc() functions + * + * Copyright (c) 2004 J. Schilling + */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _PMALLOC_H +#define _PMALLOC_H + +extern void _pfree(void *ptr); +extern void *_pmalloc(size_t size); +extern void *_pcalloc(size_t nelem, size_t elsize); +extern void *_prealloc(void *ptr, size_t size); + +#endif /* _PMALLOC_H */ |