ftp.nice.ch/pub/next/unix/audio/sndutil.1.3.s.tar.gz#/sndutil-1.3/sndtools/sndrmsenv.c

This is sndrmsenv.c in view mode; [Download] [Up]

/* 
 * sndrmsenv.c
 *	Compute rms level in dB (0 max) for each soundfile block.
 * 
 * A hacked version of sndrms.c that prints out the rms of
 * a soundfile at an interval of every argv[2] seconds.  Also prints decibel values
 * on a scale up to 0 dB.
 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sound/sound.h>
#include <architecture/byte_order.h>
#include <string.h>

#define amp_to_db(amp) (20 * (log10(fabs(amp))))
#define make_amps(struc, val, scl) struc.raw = val; struc.scaled = val * scl;\
  struc.db = amp_to_db(struc.scaled)
#define STRINGLENGTH 128

struct amps { double raw, scaled, db; };
struct extreme { char info[STRINGLENGTH]; double rms, time1, time2; };

/* Globals to hold highest and lowest RMS sections in the file.  0 is the RMS of
 * silence and 32767 is the RMS of a straight line at amp=1 so these are good starting
 * values for the two. */
struct extreme Highest = { "", 0, 0.0, 0.0 };
struct extreme Lowest = { "", 32767, 0.0, 0.0 };

/* Function prototypes */
short *get_data(char *, int *, int *, double *, int *);
void calc_and_print(double, double, double, double, int, double, double, int);



/*************************************************************************************/


void main(int argc, char *argv[])
{
  double sum = 0.0, sumsq = 0.0, max, min;
  double complete_sum = 0.0, complete_sumsq = 0.0;
  double complete_max = 0.0, complete_min = 0.0;
  double interval_secs, time1 = 0.0, time2, dur;
  int srate, scount, ch, interval_samples, next_print;
  register short *sp;
  register int temp1, temp2, i;

  if (argc != 3) {
      fprintf(stderr,
	      "%s - compute rms level in dB (0 max) for each soundfile block.\n",
	      argv[0]);
    fprintf(stderr, "\nUsage:  %s:  in.snd time-interval\n(prints amplitude and RMS \
information every <time-interval> seconds in \nsound file <in.snd>\n\n", argv[0]);
    exit(1);
  }
  
  sp = get_data(argv[1], &srate, &scount, &dur, &ch);
  interval_secs = time2 = atof(argv[2]);
  interval_samples = next_print = (int)(interval_secs * srate * ch);
  max = min = (signed short) NXSwapBigShortToHost(*sp);
  
  /* Main loop through sound file: */
  for (i = 0; i < scount; i++) {
    temp1 = (signed short) NXSwapBigShortToHost(*sp++);
    temp2 = temp1 * temp1;
    sum += temp1;
    complete_sum += temp1;
    sumsq += temp2;
    complete_sumsq += temp2;
    if (max < temp1)
      max = temp1;
    else if (min > temp1)
      min = temp1;
    if (i == next_print) {
      calc_and_print(sum, sumsq, max, min, interval_samples, time1, time2, 1);
      time1 = time2;
      time2 += interval_secs;
      if (complete_max < max)
	complete_max = max;
      if (complete_min > min)
	complete_min = min;
      next_print += interval_samples;
      sum = sumsq = max = min = 0.0;
    }
  }
  /* This time don't check for extremes as we're at the end of the sound and it 
   * could be a very low amplitude--what we want is the lowest point in the main
   * body of the sound. */
  calc_and_print(sum, sumsq, max, min, interval_samples - (next_print - scount), 
		 time1, dur, 0);
  printf("\n\t\t******** Overall values: **********");
  calc_and_print(complete_sum, complete_sumsq, complete_max, complete_min, scount,
		 0.0, dur, 0);
  printf("\n*** Lowest RMS is %s (between time %.4f and time %.4f--silent sections \
and final section ignored).\n",
	 Lowest.info, Lowest.time1, Lowest.time2);
  printf("*** Highest RMS is %s (between time %.4f and time %.4f).\n\n",
	 Highest.info, Highest.time1, Highest.time2);
}


/*************************************************************************************/


void calc_and_print(double sum, double sumsq, double max, double min, int scount,
		    double time1, double time2, int do_extremes)
{
  double mean = sum / ((double) scount);
  double meansq = sumsq / ((double) scount);
  double var = meansq - mean * mean;
  double rms = sqrt(var);
  const double scaler = pow(2, -15);
  struct amps RMS, MAX, MIN;
  char buf[STRINGLENGTH];
  
  make_amps(RMS, rms, scaler);
  make_amps(MAX, max, scaler);
  make_amps(MIN, min, scaler);
  sprintf(buf, "%.10g (%.10g) (%.10g dB)\n", RMS.raw, RMS.scaled, RMS.db);
  if (do_extremes) {
    if (rms > Highest.rms) {
      strcpy(Highest.info, buf);
      Highest.rms = rms;
      Highest.time1 = time1;
      Highest.time2 = time2;
    }
    if (rms < Lowest.rms && rms != 0.0) {  /* Ignore silence */
      strcpy(Lowest.info, buf);
      Lowest.rms = rms;
      Lowest.time1 = time1;
      Lowest.time2 = time2;
    }
  }
  printf("\n\t\t*** Time %.4f seconds to %.4f seconds: ***\n", time1, time2);
  printf("RMS = %s", buf);
  printf("Mean = %.10g (%.10g)\n", mean, mean * scaler);
  printf("Max  = %d (limit = 32767) (%.10g) (limit =  1.0) (%.10g dB)\n",
	 (int)MAX.raw, MAX.scaled, MAX.db);
  printf("Min  = %d (limit = -32768) (%.10g) (limit = -1.0) (%.10g dB)",
	 (int)MIN.raw, MIN.scaled, MIN.db);
}


/*************************************************************************************/


short *get_data (char *file, int *sampling_rate, int *sample_count, double *dur, 
		 int *channels) 
{
  int err;
  SNDSoundStruct *sndin;
  short *data;
  
  err = SNDReadSoundfile(file, &sndin);
  if (err) {
    fprintf(stderr, "Cannot read soundfile: %s\n", file);
    exit(1);
  }
  if (sndin->dataFormat != SND_FORMAT_LINEAR_16) {
    fprintf(stderr, "Soundfile must be 16 bit linear\n");
    exit(1);
  }
  *sampling_rate = sndin->samplingRate;
  *sample_count = sndin->dataSize / sizeof(short); /* channel independent */
  *channels = sndin->channelCount;
  *dur = ((double)*sample_count / (double)*sampling_rate) / (double)*channels;
  data = (short *) ((char *)sndin + sndin->dataLocation);
  return data;
}


/*************************************************************************************/

/* EOF sndrmsenv.c */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.