view stitch/source.c @ 0:5242703e91d3 tip

Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author tomwalters
date Fri, 20 May 2011 15:19:45 +0100
parents
children
line wrap: on
line source
/*
	source.c
	========

	New super minimal source system.

	John Holdsworth, 2nd January 1989.

	edited: MAA 3-8-1993
                new options "review_aid" and "frameno_aid"

*/

#if 0

    What is a Source?
    =================

    Sources are a suggested style for coding  computational  processes  on
    continuous data streams.  Making the extra effort to code according to
    this style allows such processes to be easilly  cascaded  while  still
    able to operate on data streams of indefinite length.  This is done by
    performing the processing of the data stream in segments  and  passing
    intermediate  results systematically between processing stages using a
    simple convention.

    A source is like a file, in that it specifies the source of stream  of
    data.  The  data  can  be  read in segments of user-defined size, like
    reading from a file.  For example, the routines  sread()  and  sseek()
    operate  on  Sources  the same way that fread() and fseek() operate on
    file streams. (sseek() operations on sources can only move forward  in
    a data stream).

    Sources are different from files in  that  they  do  not  necessarilly
    just read data from disk. A Source may generally be a transformation
    of data, or some simple processing operation on data. In addition, the
    data may originate in any way the user may specify. The data may come
    from a previous (cascaded) "Source".

    The structured type "Source" contains enough information to generate
    a stream of data. One way of processing a stream of data is to perform
    a simple data-processing operation on another stream of data.
    The input stream can be specified using a source.
    Composite  data-processing  functions  can be built up by cascading a
    number of elementary functions.

    If  data  is  processed  in  large  enough  segments,  the overhead of
    performing the processing task in stages is not significant.


    How to use sources
    ==================

    There are two distinct phases in working with sources, analogous to
    opening a file (eg fopen()), and reading the file (eg fread()).

    The first phase uses "setup" functions, analogous to fopen().
    (See io.c for sources which read from disk. See model.c for sources
    which perform specialised auditory-modelling processes).
    Setup functions always return a Source. They may take one or more
    sources as arguments, as a specification of the input to the process
    the source performs. The user may define his own setup routine for
    any new process. This routine intialises the source structure with
    a pointer to a callback function, which performs the data processing
    when the source is used.

    The second phase uses pull/fill/roll functions, analogous to fread().
    Unlike the fread function, Sources do not require  you  to  supply  a
    buffer  for  the  data to be "read" into.
    There are three types of operation that can be  performed  on  sources
    depending on whether the users wishes to supply the data buffer.
    (These three operations are referred to as "methods", in the style of
    object-oriented programming. Sources are thought of as "objects").

    The Pull() method returns  a  pointer  to  a  buffer which has been
    allocated  by  the  source itself.
    In the following example, the buffer will contain the next 100 bytes
    of processed data.

	/* declarations */
	char     *buffer ;
	Source    source = OpenSource( somehow ) ;
	ByteCount bytes  = 100 ;

	/* call to "pull" 100 bytes of data out of Source "source" */
	buffer = Pull( source, bytes ) ;


    If the user has a buffer available for data and would prefer the  data
    be directly tranfered into it, the Fill() method can be used instead of
    the Pull() method.  The Fill method returns the address of the  buffer
    the user provides in case it is of use for the programmer.

	/* declarations */
	char      buffer[100] ;
	Source    source = OpenSource( somehow ) ;
	ByteCount bytes  = 100 ;

	/* call to "fill" the user's buffer with 100 bytes of data */
	(void) Fill( source, bytes, buffer ) ;


    In addition there is one further method of calling data from a  source
    that  works like Pull(), but keeps a given number of bytes from the
    previous call in the buffer.
    The pointer returned by roll points to the start of the new data, as
    in pull, but the space immediately before the pointer is still valid
    and contains a specified number of bytes of data kept from the previous
    call.
    Bytes at the end of the buffer can be kept for the following roll call.

	/* declarations */
	char     *buffer ;
	Source    source = OpenSource( somehow ) ;
	ByteCount bytes  = 100 ;

	/* call to "pull" 100 bytes of data out of Source "source"    */
	buffer = Roll( source, bytes, 50 ) ;

	/* "pull" another 100 bytes out of the source.                */
	/* The last 50 bytes of the previous call will be kept in the */
	/* buffer, and will immediately precede the new 100 bytes.    */
	buffer = Roll( source, &bytes, 50 ) ;


    In order for Roll to work properly though it MUST be  the  only  call
    made  on  a  given  source.  This  is  as  it retains more information
    between calls than the Pull() and Fill() methods.


    More recent versions are defined as: PullSome, FillSome, RollSome.
    In these methods, a pointer to the number of bytes (eg. &bytes) is
    used, so that the source can indicate that less data was available
    than was requested.

    Remember also that requesting a negative number of bytes is interpreted
    as requesting a skip without processing of the negative of that number
    of bytes. This works as most processing sources will simply ignore
    a request to process a negative number of bytes but will pass on the
    request to their input sources which may which to actually act on the
    skip operation.

    More importantly A request for zero bytes it taken to mean that the
    source is not required anymore and should close itself and free any
    resources it is using. If the request is passed on to any input sources
    a close operation cascades the close request to it's' input sources. This
    means in order to close a cascade of process only the final source need be closed.
    N.B. The close operation is only performed if the number of bytes requested is
    zero not if the number of bytes returned is zero.



...ctd jwh

    Writing new sources
    ===================

    At the lowest level a source is a pointer to a struct which contains
    pointers to three functions or methods to deal with the three types
    of request that can be issued to a source. Pull(), Fill(),and Roll()
    are #defines macros that convert the not quite C language calls into
    full C calls passing the source pointer as the first argument then
    the arguments the user specifies.

    For example:

	buffer = PullSome( source, bytes ) ;

    Becomes:

	buffer = source->methods->pull( source, bytes ) ;

    The #define macros is used simply as a convienience.

    The basic Source typedefs are as follows :

    typedef struct _methods {
	Pointer (*pull)(), (*fill)(), (*roll)() ;
      } Methods ;
    typedef struct _source  {
	Methods methods ; Pointer buffer ; ByteCount bsize, valid ;
      } Source ;

    All sources must point to a structure containing at least this information
    for Pull(), Fill() and Roll() operations to work on all sources.

    To create a source the user initialises a static variable of type struct _methods with
    the three routines written to support the three basic source operations.
    A pointer to this structure is then used to initialise the methods field of
    the struct _source section of the source's' state. A pointer to this structure
    becomes the new source.

    The function setSource() is provided to perform this minimal setup  of
    a  new  source  and  to  initialise  the  other  fields  in the source
    structure. The pointer to the new Source structure is allocated outside
    setSource and passed to it in order to alow the possibility of it containing
    more than the basic source information.

#endif

#include <stdio.h>           /* added: MAA 3-8-1993*/
#include "stitch.h"
#include "source.h"
#include "options.h"         /* added: MAA 3-8-1993*/

#ifndef DEBUG
#define DEBUG 0
#endif


/*
    initialise new Source structure

*/

Source setSource( source, puller, filler, roller, name )
struct _source *source ;
Pointer (*puller)(), (*filler)(), (*roller)() ;
char *name ;
{
    Source returned ;
    Source (*opener)() = 0 ;

    source->pull    =  puller ;
    source->fill    =  filler ;
    source->roll    =  roller ;
    source->open    =  opener ;

    source->type    =  "void" ;
    source->name    =   name  ;

    source->opened   =           0 ;
    source->returned = (Pointer) 0 ;
#if DEBUG
    printf( "Creating %s\n", SourceName( source ) ) ;
#endif
    _SPTR( returned ) = source ;

    return ( returned ) ;
}

Source typeSource( source, type )
Source source ;
char *type ;
{
    _SPTR( source )->type = type ;

    return ( source ) ;
}

char *sourceType( source )
Source source ;
{
    return ( _SPTR( source )->type ) ;
}

char *sourceName( source )
Source source ;
{
    return ( _SPTR( source )->name ) ;
}

Pointer deleteSource( source )
Source source ;
{
#if DEBUG
    printf( "Deleteing %s\n", SourceName( source ) ) ;
#endif
    Delete( source ) ;

    return ( (Pointer) 0 ) ;
}


#if 00
struct _source_operators NameOfClass = {
 setSource, deleteSource,
 typeSource, sourceType,
 sourceName
 } ;
#endif


/* MAA: 3-8-1993 */
    
void sinkSource( source, framebytes, frames )   
Source source ;
int framebytes ;
long frames ;
{
    long frame ;
    

    extern *reviewstr;
    extern *framenumberstr;

    for( frame=0 ; frame<frames ; frame++ ) {

      if ( isON (framenumberstr) || isON (reviewstr) ){
	fprintf(stderr, " %i ", frame+1);
	fflush(stderr);}

      (void) Pull( source, framebytes ) ;

      if ( isON (reviewstr)) {
	fflush(stdin);getchar();}
      }
    return ; 
}



void CloseSource( source )
Source source ;
{
    (void) Pull( source, 0 ) ;

    return ;
}

void sinkAndCloseSource( source, framebytes, frames )
Source source ;
int framebytes ;
long frames ;
{
    SinkSource( source, framebytes, frames ) ;

    CloseSource( source ) ;

    return ;
}

/* tapping derived source */

typedef struct {
    struct _source parent ;
    Pointer state ;
    void (*callback)(), (*close)() ;
    Source input ;
} *TappingSource ;

static Pointer tapping_callback( source, bytes, buffer, last )
TappingSource source ;
ByteCount *bytes ;
Pointer buffer ;
int last ;
{
    source->callback( source->state, bytes, buffer, buffer+*bytes ) ;

    if( !last )
	return ( buffer ) ;
    else {
	if( source->close != (void ( * )()) 0 )
	    source->close( source->state ) ;

	return ( DeleteSource( source ) ) ;
    }
}

static Pointer tappingPuller( source, bytes )
TappingSource source ;
ByteCount *bytes ;
{
    register int last = *bytes == 0 ;
    Pointer buffer = PullSome( source->input, bytes ) ;

#if DEBUG
    printf( "\"%s\" pull tapping %d from \"%s\"\n", SourceName( source ), *bytes, SourceName( source->input ) ) ;
#endif

    return ( tapping_callback( source, bytes, buffer, last ) ) ;
}

static Pointer tappingFiller( source, bytes, buffer )
TappingSource source ;
ByteCount *bytes ;
Pointer buffer ;
{
    register int last = *bytes == 0 ;

    FillSome( source->input, bytes, buffer ) ;

#if DEBUG
    printf( "\"%s\" fill tapping %d from \"%s\"\n", SourceName( source ), *bytes, SourceName( source->input ) ) ;
#endif

    return ( tapping_callback( source, bytes, buffer, last ) ) ;
}

static Pointer tappingRoller( source, bytes, keep )
TappingSource source ;
ByteCount *bytes, keep ;
{
    register int last = *bytes == 0 ;
    Pointer buffer = RollSome( source->input, bytes, keep ) ;

#if DEBUG
    printf( "\"%s\" roll tapping %d from \"%s\"\n", SourceName( source ), *bytes, SourceName( source->input ) ) ;
#endif

    return ( tapping_callback( source, bytes, buffer, last ) ) ;
}

Source newTappingSource( state, callback, close, input, name )
Pointer state ;
void (*callback)(), (*close)() ;
Source input ;
char *name ;
{
    DeclareNew( TappingSource, source ) ;

    source->state    = state    ;
    source->callback = callback ;
    source->close    = close    ;
    source->input    = input    ;

    return ( SetSource( source, tappingPuller, tappingFiller, tappingRoller, name ) ) ;
}

/* rollable derived source */

typedef struct {
    struct _source parent ;
    Pointer buffer ;
    ByteCount bsize, valid ;
    Source input ;
} *RollableSource ;

Pointer DeleteRollableSource( source )
RollableSource source ;
{
    if( source->bsize != 0 )
	Delete( source->buffer ) ;

    return ( DeleteSource( source ) ) ;
}

static Pointer rollablePuller( source, bytes )
RollableSource source ;
ByteCount *bytes ;
{
#if DEBUG
    printf( "rollable \"%s\" pulling %d from \"%s\"\n", SourceName( source ), *bytes, SourceName( source->input ) ) ;
#endif

    return ( RollSome( source->input, bytes, 0 ) ) ;
}

static Pointer rollableFiller( source, bytes, buffer )
RollableSource source ;
ByteCount *bytes ;
Pointer buffer ;
{
    register int last = *bytes == 0 ;

    FillSome( source->input, bytes, buffer ) ;

    source->valid = *bytes ;

#if DEBUG
    printf( "rollable \"%s\" filling %d from \"%s\"\n", SourceName( source ), *bytes, SourceName( source->input ) ) ;
#endif

    if( !last )
	return ( buffer ) ;
    else
	return ( DeleteRollableSource( source ) ) ;
}

static Pointer rollableRoller( source, bytes, keep )
RollableSource source ;
ByteCount *bytes, keep ;
{
    register int last = *bytes == 0 ;
    Pointer oldBuffer = source->buffer ;
    ByteCount toStore = *bytes + keep ;

    if( source->bsize < toStore ) {

	source->buffer = Allocate( toStore, "source.c for retaining buffer" ) ;

	source->bsize = toStore ;
    }

    if( oldBuffer == (Pointer) 0 )
	ZeroArray( (char *) source->buffer, keep ) ;
    else {
	CopyArray( (char *) oldBuffer+source->valid-keep, (char *) source->buffer, keep ) ;

	if( oldBuffer != source->buffer )
	    Delete( oldBuffer ) ;
    }

    FillSome( source->input, bytes, source->buffer + keep ) ;

    source->valid = keep + *bytes ;

    if( !last )
	return ( source->buffer + keep ) ;
    else
	return ( DeleteRollableSource( source ) ) ;
}

Source newRollableSource( input )
Source input ;
{
    DeclareNew( RollableSource, source ) ;

    source->buffer = (Pointer) 0 ;

    source->bsize =   0   ;
    source->valid =   0   ;

    source->input = input ;

    return ( SetSource( source, rollablePuller, rollableFiller, rollableRoller, "rollable" ) ) ;
}

/* all other sources don't support rolling */

Pointer nonRoller( source, bytes, keep )
Source source ;
ByteCount *bytes, keep ;
{
    stitch_error( "Sorry rolling not availiable on source \"%s\"\n", SourceName( source ) ) ;

    return ( Pull( source, bytes ) ) ;
}



/* for compatability */

typedef struct _auto_source {
    struct _fillable_source parent ; Pointer state ; void (*callback)() ;
  } *AutoSource ;

static Pointer autoFiller( source, bytes, buffer )
AutoSource source ;
ByteCount *bytes ;
Pointer buffer ;
{
    register int last = *bytes == 0 ;

    source->callback( source->state, *bytes, buffer ) ;

    if( !last )
	return ( buffer ) ;
    else
	return ( DeleteFillableSource( source ) ) ;
}

Source stdAutoSource( state, callback )
Pointer state ;
void (*callback)() ;
{
    DeclareNew( AutoSource, source ) ;

    source->state    = state    ;
    source->callback = callback ;

    return ( SetFillableSource( source, autoFiller, "stdAuto" ) ) ;
}

typedef struct _self_source {
 struct _pullable_source parent ; Pointer state, (*callback)() ;
 } *SelfSource ;

static Pointer selfGenerator( source, bytes )
SelfSource source ;
ByteCount *bytes ;
{
    register int last = *bytes == 0 ;
    Pointer ptr = source->callback( source->state, *bytes ) ;

    if( !last )
	return ( ptr ) ;
    else
	return ( DeleteSource( source ) ) ;
}

Source stdSelfSource( state, callback )
Pointer state ;
Pointer (*callback)() ;
{
    DeclareNew( SelfSource, source ) ;

    source->state    = state    ;
    source->callback = callback ;

    return ( SetPullableSource( source, selfGenerator, "stdSelf" ) ) ;
}

Source stdStaticSource( ptr )
Pointer ptr ;
{
    return ( NewStaticSource( ptr ) ) ;
}

Source stdSlaveSource( source )
Source source ;
{
    return ( NewSlaveSource( source ) ) ;
}

Pointer oldPull( source, bytes )
struct _source *source ;
ByteCount bytes ;
{
    ByteCount tmp = bytes ;

    return ( source->pull( source, &tmp ) ) ;
}

Pointer oldFill( source, bytes, buffer )
struct _source *source ;
ByteCount bytes ;
Pointer buffer ;
{
    ByteCount tmp = bytes ;
    Pointer bptr = buffer ;

    do {
	tmp = buffer + bytes - bptr ;

	source->fill( source, &tmp, bptr ) ;
	bptr += tmp ;

    } while( bptr < buffer + bytes && tmp != 0 ) ;

    return ( buffer ) ;
}

Pointer oldRoll( source, bytes, keep )
struct _source *source ;
ByteCount bytes, keep ;
{
    ByteCount tmp = bytes ;

    return ( source->roll( source, &tmp, keep ) ) ;
}

#if 00
static struct _source convertors = { oldPull, oldFill, oldRoll } ;
struct _source *call_conversions = &convertors ;
#endif

/* for binding multiple sources */

#if !defined(PC) && !defined(DSP32)
#include <varargs.h>

Source *BindSources( va_alist )
va_dcl
{
    Source *inputs ;
    va_list argp ;
    int count, c ;

    va_start( argp ) ;

    for( count=0 ; _SPTR( va_arg( argp, Source ) ) != (struct _source *) 0 ; count++ )
	;

    inputs = NewArray( Source, count+1, "for binding inputs in source.c" ) ;

    va_end(   argp ) ;
    va_start( argp ) ;

    for( c=0 ; c<=count ; c++ )
	inputs[c] = va_arg( argp, Source ) ;

    va_end(   argp ) ;

    return ( inputs ) ;
}
#endif

struct _source notRealySource ;

Source EmptySource ;