home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga ACS 1997 #2
/
amigaacscoverdisc
/
utilities
/
shareware
/
music
/
gfft-2.03
/
source
/
gfft-2.03-source.lha
/
okwrite.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-01-02
|
11KB
|
426 lines
/***************************************************************************
* Copyright (C) 1994 Charles P. Peterson *
* 4007 Enchanted Sun, San Antonio, Texas 78244-1254 *
* Email: Charles_P_Peterson@fcircus.sat.tx.us *
* *
* This is free software with NO WARRANTY. *
* See gfft.c, or run program itself, for details. *
* Support is available for a fee. *
***************************************************************************
*
* Program: gfft--General FFT analysis
* File: okwrite.c
* Purpose: Write the results of a spectrum analysis
* Author: Charles Peterson (CPP)
* History: 23-August-1993 CPP; Created.
* 6-Aug-1994 CPP (1.10); Add spectrum file identifier #FFT
* 28-Aug-94 CPP (1.12); fix LowFrequency for FFP
* 7-Feb-95 CPP (1.31); Progress Requester
* 13-Feb-95 CPP (1.40); Header on spectrum files (moved to OK)
*
* Comment: This sure has gotten complicated. Sorry.
*/
/* #define PARSEVAL_DEBUG */
#define PROGRESS_INCREMENT 128
#include <stdio.h>
#include <math.h>
#ifdef _AMIGA
#ifdef _M68881
#include <m68881.h>
#endif
#endif
#include "gfft.h"
#include "settings.h" /* OkRate, WritePtr, Mean, Amplitude */
#include "errcodes.h"
#include "wbench.h" /* progress requester */
double LowestFrequencyWritten = 0;
double HighestFrequencyWritten = 0;
void ok_write (BIN_TYPE *bins, long number_bins, long number_segments)
{
long i;
double frequency;
FINAL_TYPE value;
/* double two_doubles[2]; */
double nyquist_frequency = OkRate / (2.0L * Interleave);
double delta_frequency = 0;
double value_divisor_temp = 1.0 * number_segments;
FINAL_TYPE multiply = Multiply;
FINAL_TYPE value_divisor;
double *mesh;
FINAL_TYPE smoothing_value_sum = 0.0L;
double smoothing_frequency_sum = 0.0L;
FINAL_TYPE save_value;
double save_frequency;
double test_frequency;
long mesh_index = 0;
long smoothing_count = 0;
BOOLEAN first_db_of_zero = TRUE;
double sum_of_bins;
extern double Ending_Progress;
double first_write_progress = 100.0 - Ending_Progress;
double incremental_progress_divisor = number_bins / Ending_Progress;
if (number_segments == 0) return;
/*
* reset index used internally in calibration
* because we're starting from lowest frequency (again, maybe)
*/
calibration_list__reset (&CalibrationList);
/*
* If 0 bins (i.e., the DC one only)
* avoid divide by zero problems
*/
if (number_bins)
{
delta_frequency = nyquist_frequency / number_bins;
mesh = ok_mesh (nyquist_frequency, delta_frequency);
if (Mean)
{
/*
* Mean normalization for power or amplitude.
* See Press, et. al, page 439.
* To get mean, we'll have to divide by N^2, then by number_segments
* Note: N is number_bins * 2 if number_bins > 0, thus
* (number_bins * 2)^2 == 4 * number_bins^2
*
* READ THIS: if Parseval option selected (now default)
* we recompute this later using Parseval's theorem. The
* results are remarkably similar...showing that I WAS doing
* the correct thing here. The new Parseval method is
* more accurate, but the old method is 2.5% faster overall
* because the squaring and summation in ok_read is not needed.
*/
value_divisor_temp = (((4.0 * number_bins) * number_bins) *
number_segments);
}
else /* !Mean */
{
/* value_divisor_temp = 2.0 * number_bins; */
/*
* We correct for overlapping bins, by multiplying by extra factor
* (number_segments * <segment size> / Sample_Frame_Count)
*/
/* value_divisor_temp = 2.0 * number_bins * number_segments *
2.0 * number_bins / Sample_Frame_Count */
/*
* which simplifies to the following:
*/
value_divisor_temp = 4.0 * number_bins * number_bins *
number_segments / Sample_Frame_Count;
}
}
if (WindowType)
/*
* Special extra correction if non-square window is used
* NOTE: as currently done, this is an estimate
* based on window's mean squared average gain...
* Parseval method works better.
*/
{
value_divisor_temp *= ok_window__gain2();
}
/*
* This is now where the normalization normally gets done
* Using Parseval's theorem, i.e.
* sum of power in the time domain equals sum of power in freq domain
*/
if (Parseval)
{
sum_of_bins = bins[0];
if (number_bins > 0)
{
sum_of_bins += bins[number_bins];
}
for (i = 1; i <= number_bins-1; i++)
{
sum_of_bins += bins[i] * 2.0;
}
if (Mean && Sample_Sum_Of_Squares != 0.0)
{
#ifdef PARSEVAL_DEBUG
fprintf (stderr,"Sample MSS: %g, Bin MSS: %g, Old Divisor: %g\n",
Sample_Sum_Of_Squares / Sample_Frame_Count,
sum_of_bins / value_divisor_temp,
value_divisor_temp);
#endif
value_divisor_temp = sum_of_bins * Sample_Frame_Count /
Sample_Sum_Of_Squares;
#ifdef PARSEVAL_DEBUG
fprintf (stderr,"Corrected Sample Bin MSS: %g\n",
sum_of_bins / value_divisor_temp);
#endif
}
else if (!Mean && Sample_Sum_Of_Squares != 0.0)
{
#ifdef PARSEVAL_DEBUG
fprintf (stderr,"Original divisor: %g\n",value_divisor_temp);
#endif
value_divisor_temp = sum_of_bins / Sample_Sum_Of_Squares;
#ifdef PARSEVAL_DEBUG
fprintf (stderr,
"Post correction: Sample SS: %g, Bin SS: %g, Divisor: %g\n",
Sample_Sum_Of_Squares,
sum_of_bins / value_divisor_temp,
value_divisor_temp);
#endif
}
/* else...all's zeros anyway */
}
if (PSDensity)
{
/*
* (Re-)Adjust for PSDensity:
* The effect of this is to allow you to splice together curves
* computed with different numbers of bins, which is useful in some
* applications.
*
* Ok, we could multiply back in an extra N,
* but, I've chosen to divide by the bin size
* giving more meaningful units of (.../Hz) or something like that,
* I think...
*/
value_divisor_temp *= delta_frequency;
}
value_divisor = value_divisor_temp; /* reduce to final precision */
LowestFrequencyWritten = -1.0;
/*
* Thanks to bug in FFP handing of DBL_MIN, LOWEST_FREQUENCY is now -1
* and I have to do this extra stuff
*/
if (LowFrequency == LOWEST_FREQUENCY)
{
i = 1;
}
else
{
i = 0;
}
for (; i <= number_bins; i++)
{
if (i % PROGRESS_INCREMENT == 1 && !Time3D)
{
progress_requester__update
((int) (first_write_progress +
(i / incremental_progress_divisor)));
}
frequency = delta_frequency * i; /* Note 888 below if changing */
if (frequency < LowFrequency)
{
continue;
}
if (frequency > HighFrequency)
{
continue;
}
value = bins[i];
if (i != 0 && i != number_bins)
{
value *= 2.0; /* Correct for one-sidedness */
}
value /= value_divisor;
if (Pink)
{
if (frequency == 0)
{
continue;
}
/*
* The following normalizes the spectrum values to about what
* would be reported by an octave band spectrum analyzer.
* ** Warning ** This is a guess, not known to be correct!
*/
value *= ((frequency + delta_frequency) / delta_frequency);
}
if (Amplitude)
{
value = sqrt (value);
}
if (multiply != 1.0)
{
value *= multiply;
}
if (mesh && frequency > 0.0L)
{
if (frequency > mesh[mesh_index] && smoothing_count == 0)
{
while (frequency > mesh[++mesh_index]); /* Catch up */
}
if (frequency < mesh[mesh_index])
{
smoothing_value_sum += (SquaredSmoothing) ?
value * value : value;
smoothing_frequency_sum += frequency;
smoothing_count++;
continue; /* Nothing to write this time */
}
else if (frequency == mesh[mesh_index])
{
/* Set up value and frequency for this write */
smoothing_value_sum += (SquaredSmoothing) ?
value * value : value;
smoothing_frequency_sum += frequency;
value = smoothing_value_sum / ++smoothing_count;
if (SquaredSmoothing) value = sqrt (value);
frequency = smoothing_frequency_sum / smoothing_count;
/* Set up for next pass */
smoothing_value_sum = 0.0;
smoothing_frequency_sum = 0.0;
smoothing_count = 0;
mesh_index++;
}
else if (frequency > mesh[mesh_index])
{
/* Set up value and frequency for this write */
save_value = value;
save_frequency = frequency;
value = smoothing_value_sum / smoothing_count;
if (SquaredSmoothing) value = sqrt (value);
frequency = smoothing_frequency_sum / smoothing_count;
/* Set up for next pass */
smoothing_value_sum = (SquaredSmoothing) ?
save_value * save_value : save_value;
smoothing_frequency_sum = save_frequency;
smoothing_count = 1;
test_frequency = (i+1) * delta_frequency; /*Note 888 above*/
if (test_frequency > mesh[++mesh_index])
{ /* Catch up for next pass */
while (test_frequency > mesh[++mesh_index]);
}
}
}
if (Db)
{
if (value > 0.0)
{
value = ((Amplitude) ? 20 : 10) * log10 (value);
}
else
{
if (first_db_of_zero)
{
error_message (DB_OF_ZERO);
first_db_of_zero = FALSE;
}
continue; /* Just skip (unless user decides not to) */
}
}
if (CalibrationList)
{
CATCH_ERROR
{
value = calibration_list__apply (&CalibrationList,
value, frequency);
}
ON_ERROR
{
if (first_db_of_zero)
{
error_message (DB_OF_ZERO);
first_db_of_zero = FALSE;
}
continue; /* Just skip (unless user decides not to) */
}
END_CATCH_ERROR;
} /* end if CalibrationList */
if (Quantization != MIN_QUANTIZATION)
{
long factor = value / Quantization;
double value1 = (factor-1) * Quantization;
double value2 = factor * Quantization;
double value3 = (factor+1) * Quantization;
double delta1 = value - value1;
double delta2 = fabs (value - value2);
double delta3 = value3 - value;
if (delta2 <= delta1 && delta2 <= delta3)
{
value = value2;
}
else if (delta1 <= delta3)
{
value = value1;
}
else
{
value = value3;
}
}
if (LowestFrequencyWritten < 0) LowestFrequencyWritten = frequency;
if (!OutputFormat.binary)
{
if (Time3D)
{
#ifdef _FFP
fprintf (WritePtr, "%-15.8g %-15.8g %-13.7g\n",
frequency, OkSegmentTime, value);
#else
fprintf (WritePtr, "%-19.12g %-19.12g %-13.7g\n",
frequency, OkSegmentTime, value);
#endif
}
else
{
#ifdef _FFP
fprintf (WritePtr, "%-15.8g %-13.7g\n", frequency, value);
#else
fprintf (WritePtr, "%-19.12g %-13.7g\n", frequency, value);
#endif
}
}
/* else ** This didn't work...need to figure binary formats out **
* {
* two_doubles[0] = frequency;
* two_doubles[1] = value;
* fwrite (two_doubles, sizeof(double), (size_t) 2, WritePtr);
* }
*/
}
if (mesh) gfree (mesh);
if (Time3D) fprintf (WritePtr, "\n");
HighestFrequencyWritten = frequency;
}