PVTool v1.0 - Copyright 1996 Richard W.E. Furse (except MIT code)
-----------------------------------------------------------------

	pvcom <input script> <output executable>
	pvtool <input script> [<further parameters for script>]

The input script should be a fragment of valid C code including a
function with prototype

	int pvaction(void);

From this, 'pvcom' compiles an executable program that manipulates
phase vocoder files produced by Csound's 'pvanal' utilities. The
nature of the manipulation is decided by the script. The 'pvtool'
utility goes further than this: the script is compiled, run (with any
additional parameters passed) and then the compiled binary is
removed. On many systems scripts can be written in such a way that
they call pvtool automatically so that explicit compilation is not
necessary.

A (Stupidly) Simple Script:
---------------------------
In case that made no sense, here's an example. Here is a simple script
that takes in a phase vocoder file and writes out the same file. It is
essentially C:

	#!/usr/local/bin/pvtool

	int pvaction(void)
	{
	  int i,j;
	  for (i=0;i<INPUT1_FRAMES;i++)
	    for (j=0;j<bins;j++)
	     {
	       OUTPUT_PHIDOT(i,j)=INPUT1_PHIDOT(i,j);
	       OUTPUT_MAG(i,j)=INPUT1_MAG(i,j);
	     }
	  return(0);
	}

Note that INPUT1_FRAMES, bins, OUTPUT_PHIDOT, INPUT1_PHIDOT,
OUTPUT_MAG, INPUT1_MAG and others are not built in to C but are
provided by the system. These are documented below. The initial line
is not valid C but is ignored during compilation.

Using the Program:
==================

Compilation (pvcom)
-------------------
If this script is called 'identity.pvc' then it would be possible to
produce an executable binary 'identity' but typing the line

	% pvcom identity.pvc identity

This new program 'identity' could then be applied to phase vocoder
files: In other words, if a sound snd.aiff was analysed with

	% pvanal snd.aiff snd.pv

Then the instruction

	% identity snd.pv same.pv

Would produce a phase vocoder file 'same.pv' which Csound would
consider equivalent to 'snd.pv'.

Running Scripts with pvtool
---------------------------
Alternatively the script could be compiled and run in one instruction,
without using an intermediary program 'identity':

	% pvtool identity.pvc snd.pv same.pv

Running Scripts with #!
-----------------------
On systems supporting the #! notation, if a script is chmod'd to be
executable, for example using

	% chmod 755 identity.pvc

Then it is possible to run the script directly:

	% identity.pvc snd.pv same.pv

The Script
==========

This material isn't really intended as a tutorial: it may be easier to
learn to use the system from example scripts.

Inputs and Outputs:
-------------------
By default, pvtool takes a mono phase-vocoder input and writes out a
mono phase-vocoder output, and the script takes just these two
parameters.

It is possible to have two rather than one phase vocoder input files
by including the line

	#define DOUBLE_INPUT 1
 
at the top of the script. Including the line

	#define NO_OUTPUT 1

disables the need for an output file. These two flags mean that the
'file' parameters can vary in number from one to three. Further

Extra Parameters:
-----------------
parameters can be taken by the script - these are passed to pvaction()
so that the script code can interpret them as it will. The exact
number of further parameters can be controlled by a line of form

	#define EXTRA_PARAMETERS <number>

where <number> is an integer and does NOT include the file parameters
(which will be between one and three in number.) If no
EXTRA_PARAMETERS line is included (as in the example script above)
then pvtool will not check the number of additional parameters. The
script can examine the parameters passed to it using the variables

	parameter_count

which is an integer and is the number of parameters provided after the
phase vocoder files handled automatically by pvtool. If an
EXTRA_PARAMETERS value was given then parameter_count must equal this
or pvtool will generate an error message. (See usage message below.)

The parameters provided are stored in an array of strings

	parameter[]

so that parameter[0] provides the first parameter after the file names
etc. Parameters with numerical values are automatically cast where
possible and are stored in the array of floats

	fparameter[].

Usage Message:
--------------
If the wrong number of parameters is passed to a script then an usage
message is automatically generated. As the system does not know how
parameters are to be used these may not be very informative. To set
another, define USAGE_MESSAGE, for example:

	#define USAGE_MESSAGE "\
	ttimeshft <input pv file> <output pv file> <time ratio>\n\
	\n\
	This program attempts to timeshift an fft file.\n"

Phase Vocoder Files - a Noddy Description:
------------------------------------------
In phase vocoder files, sounds are stored in 'frames' of sound, one
following after another. Each frame is found by FFT and is broken into
a number of frequency 'bins' each with a magnitude and 'phi-dot'
(roughly corresponding to a frequency within the bin's range). The
bins are spaced equally (linearly) across the frequency range in use.

The Size of Output Phase Vocoder Files:
---------------------------------------
If an output is produced by the script, a buffer is generated with a
number of frames equal to the number of frames in the input - or the
least number of frames if there are two inputs. If two inputs are used
then the two files MUST USE THE SAME NUMBER OF FREQUENCY BINS. This
number of bins is also used for the output and cannot be changed. The
number is available as the long integer

	bins

The number of frames in the various buffers are available as long
integers from the variables

	INPUT1_FRAMES
	INPUT2_FRAMES
	OUTPUT_FRAMES

The number of OUTPUT_FRAMES can be changed by calling the built-in
function resize() with prototype

	int resize(int frames);

This function returns 0 if it succeeds.

Input and Output Data:
----------------------
The actual information in the bins is available using the following
macros

	INPUT1_PHIDOT(frame,bin)
	INPUT1_MAG(frame,bin)
	INPUT2_PHIDOT(frame,bin)
	INPUT2_MAG(frame,bin)
	INPUT_PHIDOT(input_number,frame,bin)
	INPUT_MAG(input_number,frame,bin)

These can be read and return floats (INPUT2_PHIDOT and INPUT2_MAG are
only available when DOUBLE_INPUT has been defined.)

	OUTPUT_PHIDOT(frame,bin)
	OUTPUT_MAG(frame,bin)

These can be set by setting them equal to float values. See the
example for use of these macros.

Returning Errors:
-----------------
The function 'pvaction()' should return a value 0 when it runs
successfully and an error number otherwise.

Library Functions:
------------------
int pvtool_error(char *string)

	This prints the screen in a standard error format for pvtool
	utilities. It returns -1.

int resize(long int frames);

	This function changes the number of OUTPUT_FRAMES and returns
	0 if it succeeds.

float spectral_envelope_component (int input_number,
				   int frame,
				   int bin,
				   float threshold_scalar)

	This function behaves like INPUT_MAG() but with the additional
	parameter threshold_scalar. This provides a smoothed version
	of the required input data. The function is optimised so that
	it will run MUCH faster if you work on a frame at a time: in
	other words, you should write code in form

		for (frame = blah; blah blah)
		  for (bin = blah; blah blah)
		    etc etc etc

	Rather than nesting the loops the other way around. The two
	input channels are independent for these purposes.
	
	The smoothing algorithm is far from perfect. It currently
	searches for local maxima in a frame where the local maxima is
	above a certain threshold when compared to the maximum for the
	frame (this threshold is given by the threshold_scalar
	parameter multiplied by the greatest magnitude in the frame.)
	Regions between maxima is interpolated linearly, with
	threshold magnitudes being assumed in bin 0 and the top
	bin. Note there is no attempt to provide a smooth progression
	through time. This is probably not a good thing.

	A useful threshold_scalar is 0.1.

	Note that the minimum level present in a bin will be the
	maximum magnitude times the threshold_scalar. This will almost
	never be zero so it is alright to divide by
	spectral_envelope_component except in periods of total
	silence.

float spectral_envelope_independent_component (int input_number,
					       int frame,
					       int bin,
					       float threshold_scalar);

	This provides the bin's component independent to the spectral
	envelope component calculated above. i.e. the two multiply to
	give the bin magnitude content content.

float sampling_rate(void);

	Returns the sampling rate (if two input files and they differ
	in sampling rate then the first is taken.)

char *input_filename(int input_number);
	
	Returns a pointer to the filename of the requested input
	buffer.

float sound_length(int input_number);

	Returns the length of the sound in seconds. The frames are
	assumed to be equally spaced through this time.

float bin_to_freq (int bin);

	Returns the centre frequency for the given bin.

Acknowledgements:
=================

PVTool relies on two files from MIT Csound's source code (as of late
1994) these are pvoc.h and pvoc.c and these are copyright (etc) MIT.
Serious phase vocoder utilities could be quickly as assembled from
these files alone without these utilities. The whole system relies on
the hard work (the phase vocoder analysis and resynthesis) being done
by Csound. Thanks to Barry Vercoe, John Fitch and Co.

Thanks to Dylan Menzies-Gow and Tim Ward up at York for testing.

Future:
=======

This utility is not fantastically powerful but I find it an easy way
to write scripts that manipulate phase vocoder files. The system is
extensible: it is straightforward to add new functions to the library,
either directly or by re-making the library with additional object
files linked in.

Please send me any ideas, useful scripts + functions... or bugs to me,
Richard Furse, at richard@muse.demon.co.uk.

                ------------------------------------

