Blame lib/paranoia/overlap.c

Packit cb6d3d
/*
Packit cb6d3d
  Copyright (C) 2004, 2005, 2008, 2011, 2017 Rocky Bernstein <rocky@gnu.org>
Packit cb6d3d
  Copyright (C) 1998 Monty xiphmont@mit.edu
Packit cb6d3d
Packit cb6d3d
  This program is free software: you can redistribute it and/or modify
Packit cb6d3d
  it under the terms of the GNU General Public License as published by
Packit cb6d3d
  the Free Software Foundation, either version 3 of the License, or
Packit cb6d3d
  (at your option) any later version.
Packit cb6d3d
Packit cb6d3d
  This program is distributed in the hope that it will be useful,
Packit cb6d3d
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit cb6d3d
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit cb6d3d
  GNU General Public License for more details.
Packit cb6d3d
Packit cb6d3d
  You should have received a copy of the GNU General Public License
Packit cb6d3d
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit cb6d3d
*/
Packit cb6d3d
Packit cb6d3d
/***
Packit cb6d3d
 *
Packit cb6d3d
 * Statistic code and cache management for overlap settings
Packit cb6d3d
 *
Packit cb6d3d
 ***/
Packit cb6d3d
Packit cb6d3d
#ifdef HAVE_CONFIG_H
Packit cb6d3d
# include "config.h"
Packit cb6d3d
# define __CDIO_CONFIG_H__ 1
Packit cb6d3d
#endif
Packit cb6d3d
Packit cb6d3d
#ifdef HAVE_STDLIB_H
Packit cb6d3d
#include <stdlib.h>
Packit cb6d3d
#endif
Packit cb6d3d
#include <stdio.h>
Packit cb6d3d
#ifdef HAVE_STRING_H
Packit cb6d3d
#include <string.h>
Packit cb6d3d
#endif
Packit cb6d3d
#include <cdio/paranoia/paranoia.h>
Packit cb6d3d
#include "p_block.h"
Packit cb6d3d
#include "overlap.h"
Packit cb6d3d
#include "isort.h"
Packit cb6d3d
Packit cb6d3d
/**** Internal cache management *****************************************/
Packit cb6d3d
Packit cb6d3d
void
Packit cb6d3d
paranoia_resetcache(cdrom_paranoia_t *p)
Packit cb6d3d
{
Packit cb6d3d
  c_block_t *c=c_first(p);
Packit cb6d3d
  v_fragment_t *v;
Packit cb6d3d
Packit cb6d3d
  while(c){
Packit cb6d3d
    free_c_block(c);
Packit cb6d3d
    c=c_first(p);
Packit cb6d3d
  }
Packit cb6d3d
Packit cb6d3d
  v=v_first(p);
Packit cb6d3d
  while(v){
Packit cb6d3d
    free_v_fragment(v);
Packit cb6d3d
    v=v_first(p);
Packit cb6d3d
  }
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
void
Packit cb6d3d
paranoia_resetall(cdrom_paranoia_t *p)
Packit cb6d3d
{
Packit cb6d3d
  p->root.returnedlimit=0;
Packit cb6d3d
  p->dyndrift=0;
Packit cb6d3d
  p->root.lastsector=0;
Packit cb6d3d
Packit cb6d3d
  if(p->root.vector){
Packit cb6d3d
    i_cblock_destructor(p->root.vector);
Packit cb6d3d
    p->root.vector=NULL;
Packit cb6d3d
  }
Packit cb6d3d
Packit cb6d3d
  paranoia_resetcache(p);
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
void
Packit cb6d3d
i_paranoia_trim(cdrom_paranoia_t *p, long int beginword, long int endword)
Packit cb6d3d
{
Packit cb6d3d
  root_block *root=&(p->root);
Packit cb6d3d
  if(root->vector!=NULL){
Packit cb6d3d
    long target=beginword-MAX_SECTOR_OVERLAP*CD_FRAMEWORDS;
Packit cb6d3d
    long rbegin=cb(root->vector);
Packit cb6d3d
    long rend=ce(root->vector);
Packit cb6d3d
Packit cb6d3d
    if(rbegin>beginword)
Packit cb6d3d
      goto rootfree;
Packit cb6d3d
Packit cb6d3d
    if(rbegin+MAX_SECTOR_OVERLAP*CD_FRAMEWORDS
Packit cb6d3d
      if(target+MIN_WORDS_OVERLAP>rend)
Packit cb6d3d
	goto rootfree;
Packit cb6d3d
Packit cb6d3d
      {
Packit cb6d3d
	long int offset=target-rbegin;
Packit cb6d3d
	c_removef(root->vector,offset);
Packit cb6d3d
      }
Packit cb6d3d
    }
Packit cb6d3d
Packit cb6d3d
    {
Packit cb6d3d
      c_block_t *c=c_first(p);
Packit cb6d3d
      while(c){
Packit cb6d3d
	c_block_t *next=c_next(c);
Packit cb6d3d
	if(ce(c)
Packit cb6d3d
	  free_c_block(c);
Packit cb6d3d
	c=next;
Packit cb6d3d
      }
Packit cb6d3d
    }
Packit cb6d3d
Packit cb6d3d
  }
Packit cb6d3d
  return;
Packit cb6d3d
Packit cb6d3d
rootfree:
Packit cb6d3d
Packit cb6d3d
  i_cblock_destructor(root->vector);
Packit cb6d3d
  root->vector=NULL;
Packit cb6d3d
  root->returnedlimit=-1;
Packit cb6d3d
  root->lastsector=0;
Packit cb6d3d
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
/**** Statistical and heuristic[al? :-] management ************************/
Packit cb6d3d
Packit cb6d3d
/* ===========================================================================
Packit cb6d3d
 * offset_adjust_settings (internal)
Packit cb6d3d
 *
Packit cb6d3d
 * This function is called by offset_add_value() every time 10 samples have
Packit cb6d3d
 * been accumulated.  This function updates the internal statistics for
Packit cb6d3d
 * paranoia (dynoverlap, dyndrift) that compensate for jitter and drift.
Packit cb6d3d
 *
Packit cb6d3d
 * (dynoverlap) influences how far stage 1 and stage 2 search for matching
Packit cb6d3d
 * runs.  In low-jitter conditions, it will be very small (or even 0),
Packit cb6d3d
 * narrowing our search.  In high-jitter conditions, it will be much larger,
Packit cb6d3d
 * widening our search at the cost of speed.
Packit cb6d3d
 *
Packit cb6d3d
 * ???: To be studied further.
Packit cb6d3d
 */
Packit cb6d3d
void
Packit cb6d3d
offset_adjust_settings(cdrom_paranoia_t *p,
Packit cb6d3d
		       void(*callback)(long int, paranoia_cb_mode_t))
Packit cb6d3d
{
Packit cb6d3d
  if(p->stage2.offpoints>=10){
Packit cb6d3d
    /* drift: look at the average offset value.  If it's over one
Packit cb6d3d
       sector, frob it.  We just want a little hysteresis [sp?]*/
Packit cb6d3d
    long av=(p->stage2.offpoints?p->stage2.offaccum/p->stage2.offpoints:0);
Packit cb6d3d
Packit cb6d3d
    if(labs(av)>p->dynoverlap/4){
Packit cb6d3d
      av=(av/MIN_SECTOR_EPSILON)*MIN_SECTOR_EPSILON;
Packit cb6d3d
Packit cb6d3d
      if(callback)(*callback)(ce(p->root.vector),PARANOIA_CB_DRIFT);
Packit cb6d3d
      p->dyndrift+=av;
Packit cb6d3d
Packit cb6d3d
      /* Adjust all the values in the cache otherwise we get a
Packit cb6d3d
	 (potentially unstable) feedback loop */
Packit cb6d3d
      {
Packit cb6d3d
	c_block_t *c=c_first(p);
Packit cb6d3d
	v_fragment_t *v=v_first(p);
Packit cb6d3d
Packit cb6d3d
	while(v && v->one){
Packit cb6d3d
	  /* safeguard beginning bounds case with a hammer */
Packit cb6d3d
	  if(fb(v)<av || cb(v->one)
Packit cb6d3d
	    v->one=NULL;
Packit cb6d3d
	  }else{
Packit cb6d3d
	    fb(v)-=av;
Packit cb6d3d
	  }
Packit cb6d3d
	  v=v_next(v);
Packit cb6d3d
	}
Packit cb6d3d
	while(c){
Packit cb6d3d
	  long adj=min(av,cb(c));
Packit cb6d3d
	  c_set(c,cb(c)-adj);
Packit cb6d3d
	  c=c_next(c);
Packit cb6d3d
	}
Packit cb6d3d
      }
Packit cb6d3d
Packit cb6d3d
      p->stage2.offaccum=0;
Packit cb6d3d
      p->stage2.offmin=0;
Packit cb6d3d
      p->stage2.offmax=0;
Packit cb6d3d
      p->stage2.offpoints=0;
Packit cb6d3d
      p->stage2.newpoints=0;
Packit cb6d3d
      p->stage2.offdiff=0;
Packit cb6d3d
    }
Packit cb6d3d
  }
Packit cb6d3d
Packit cb6d3d
  if(p->stage1.offpoints>=10){
Packit cb6d3d
    /* dynoverlap: we arbitrarily set it to 4x the running difference
Packit cb6d3d
       value, unless min/max are more */
Packit cb6d3d
Packit cb6d3d
    p->dynoverlap=(p->stage1.offpoints?p->stage1.offdiff/
Packit cb6d3d
		   p->stage1.offpoints*3:CD_FRAMEWORDS);
Packit cb6d3d
Packit cb6d3d
    if(p->dynoverlap<-p->stage1.offmin*1.5)
Packit cb6d3d
      p->dynoverlap=-p->stage1.offmin*1.5;
Packit cb6d3d
Packit cb6d3d
    if(p->dynoverlap<p->stage1.offmax*1.5)
Packit cb6d3d
      p->dynoverlap=p->stage1.offmax*1.5;
Packit cb6d3d
Packit cb6d3d
    if(p->dynoverlap<MIN_SECTOR_EPSILON)p->dynoverlap=MIN_SECTOR_EPSILON;
Packit cb6d3d
    if(p->dynoverlap>MAX_SECTOR_OVERLAP*CD_FRAMEWORDS)
Packit cb6d3d
      p->dynoverlap=MAX_SECTOR_OVERLAP*CD_FRAMEWORDS;
Packit cb6d3d
Packit cb6d3d
    if(callback)(*callback)(p->dynoverlap,PARANOIA_CB_OVERLAP);
Packit cb6d3d
Packit cb6d3d
    if(p->stage1.offpoints>600){ /* bit of a bug; this routine is
Packit cb6d3d
				    called too often due to the overlap
Packit cb6d3d
				    mesh alg we use in stage 1 */
Packit cb6d3d
      p->stage1.offpoints/=1.2;
Packit cb6d3d
      p->stage1.offaccum/=1.2;
Packit cb6d3d
      p->stage1.offdiff/=1.2;
Packit cb6d3d
    }
Packit cb6d3d
    p->stage1.offmin=0;
Packit cb6d3d
    p->stage1.offmax=0;
Packit cb6d3d
    p->stage1.newpoints=0;
Packit cb6d3d
  }
Packit cb6d3d
}
Packit cb6d3d
Packit cb6d3d
Packit cb6d3d
/* ===========================================================================
Packit cb6d3d
 * offset_add_value (internal)
Packit cb6d3d
 *
Packit cb6d3d
 * This function adds the given jitter detected (value) to the statistics
Packit cb6d3d
 * for the given stage (o).  It is called whenever jitter has been identified
Packit cb6d3d
 * by stage 1 or 2.  After every 10 samples, we update the overall jitter-
Packit cb6d3d
 * compensation settings (e.g. dynoverlap).  This allows us to narrow our
Packit cb6d3d
 * search for matching runs (in both stages) in low-jitter conditions
Packit cb6d3d
 * and also widen our search appropriately when there is jitter.
Packit cb6d3d
 *
Packit cb6d3d
 *
Packit cb6d3d
 * "???BUG???:
Packit cb6d3d
 * Note that there is a bug in the way that this is called by try_sort_sync().
Packit cb6d3d
 * Silence looks like zero jitter, and dynoverlap may be incorrectly reduced
Packit cb6d3d
 * when there's lots of silence but also jitter."
Packit cb6d3d
 *
Packit cb6d3d
 * Strictly speaking, this is only sort-of true.  The silence will
Packit cb6d3d
 * incorrectly somewhat reduce dynoverlap.  However, it will increase
Packit cb6d3d
 * again once past the silence (even if reduced to zero, it will be
Packit cb6d3d
 * increased by the block read loop if we're not getting matches).
Packit cb6d3d
 * In reality, silence usually passes rapidly.  Anyway, long streaks
Packit cb6d3d
 * of silence benefit performance-wise from having dynoverlap decrease
Packit cb6d3d
 * momentarily. There is no correctness bug. --Monty
Packit cb6d3d
 *
Packit cb6d3d
 */
Packit cb6d3d
void
Packit cb6d3d
offset_add_value(cdrom_paranoia_t *p,offsets *o,long value,
Packit cb6d3d
		 void(*callback)(long int, paranoia_cb_mode_t))
Packit cb6d3d
{
Packit cb6d3d
  if(o->offpoints!=-1){
Packit cb6d3d
Packit cb6d3d
    /* Track the average magnitude of jitter (in either direction) */
Packit cb6d3d
    o->offdiff += labs(value);
Packit cb6d3d
    o->offpoints++;
Packit cb6d3d
    o->newpoints++;
Packit cb6d3d
Packit cb6d3d
    /* Track the net value of the jitter (to track drift) */
Packit cb6d3d
    o->offaccum+=value;
Packit cb6d3d
Packit cb6d3d
    /* Track the largest jitter we've encountered in each direction */
Packit cb6d3d
    if(value<o->offmin)o->offmin=value;
Packit cb6d3d
    if(value>o->offmax)o->offmax=value;
Packit cb6d3d
Packit cb6d3d
    /* After 10 samples, update dynoverlap, etc. */
Packit cb6d3d
    if(o->newpoints>=10)offset_adjust_settings(p,callback);
Packit cb6d3d
  }
Packit cb6d3d
}