Logo Search packages:      
Sourcecode: wavpack version File versions  Download package

wvgain.c

////////////////////////////////////////////////////////////////////////////
//                           **** WAVPACK ****                            //
//                  Hybrid Lossless Wavefile Compressor                   //
//              Copyright (c) 1998 - 2005 Conifer Software.               //
//                          All Rights Reserved.                          //
//      Distributed under the BSD Software License (see license.txt)      //
////////////////////////////////////////////////////////////////////////////

// wvgain.c

// This is the main module for the WavPack command-line ReplayGain Scanner/Tagger.

// This implementation is based on the ReplayGain proposal by David Robinson
// with table values copied from the Foobar2000 source code. Many thanks are
// due David Robinson and the others who contributed to ReplayGain.

// ReplayGain's [somewhat outdated] website: http://replaygain.org/

#if defined(WIN32)
#include <windows.h>
#include <io.h>
#else
#include <sys/stat.h>
#include <sys/param.h>
#include <locale.h>
#include <iconv.h>
#if defined (__GNUC__)
#include <unistd.h>
#include <glob.h>
#endif
#endif

#if defined(__GNUC__) && !defined(WIN32)
#include <sys/time.h>
#else
#include <sys/timeb.h>
#endif

#include <math.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>

#include "wavpack.h"

#ifdef DEBUG_ALLOC
#define malloc malloc_db
#define realloc realloc_db
#define free free_db
void *malloc_db (uint32_t size);
void *realloc_db (void *ptr, uint32_t size);
void free_db (void *ptr);
int32_t dump_alloc (void);
static char *strdup (const char *s)
 { char *d = malloc (strlen (s) + 1); return strcpy (d, s); }
#endif

///////////////////////////// local variable storage //////////////////////////

static const char *sign_on = "\n"
" WVGAIN  ReplayGain Scanner/Tagger for WavPack  %s Version %s  %s\n"
" Copyright (c) 2005 Conifer Software.  All Rights Reserved.\n\n";

static const char *usage =
" Usage:   WVGAIN [-options] [@]infile[.wv] [...]\n"
"             (infiles may contain wildcards: ?,*)\n\n"
" Options: -a  = album mode (all files scanned are considered an album)\n"
"          -c  = clean ReplayGain values from all files (no analysis)\n"
"          -d  = display calculated values only (no files are modified)\n"
"          -i  = ignore .wvc file (forces hybrid lossy)\n"
#if defined (WIN32)
"          -l  = run at low priority (for smoother multitasking)\n"
#endif
"          -s  = show stored values only (no analysis)\n"
"          -q  = quiet (keep console output to a minimum)\n\n"
" Web:     Visit www.wavpack.com for latest version and info\n";

// this global is used to indicate the special "debug" mode where extra debug messages
// are displayed and all messages are logged to the file wavpack.log

int debug_logging_mode;

#define HISTOGRAM_SLOTS 12000
static uint32_t track_histogram [HISTOGRAM_SLOTS], album_histogram [HISTOGRAM_SLOTS];

static char album_mode, clean_mode, display_mode, ignore_wvc, quiet_mode, show_mode;
static int num_files, file_index;

/////////////////////////// local function declarations ///////////////////////

static int update_file (char *infilename, float track_gain, float track_peak, float album_gain, float album_peak);
static int analyze_file (char *infilename, uint32_t *histogram, float *peak);
static int show_file_info (char *infilename, FILE *dst);
static float calc_replaygain (uint32_t *histogram);
static void display_progress (double file_progress);

#define NO_ERROR 0L
#define SOFT_ERROR 1
#define HARD_ERROR 2

///////////////////////////////////////////////////////////////////////////////////
// The "main" function for the command-line WavPack ReplayGain Scanner/Processor //
///////////////////////////////////////////////////////////////////////////////////

int main (argc, argv) int argc; char **argv;
{
    int  error_count = 0;
    char **matches = NULL;
    int result, i;

#if defined(WIN32)
    struct _finddata_t _finddata_t;
    char selfname [MAX_PATH];

    if (GetModuleFileName (NULL, selfname, sizeof (selfname)) && filespec_name (selfname) &&
      strupr (filespec_name (selfname)) && strstr (filespec_name (selfname), "DEBUG")) {
          char **argv_t = argv;
          int argc_t = argc;

          debug_logging_mode = TRUE;

          while (--argc_t)
            error_line ("arg %d: %s", argc - argc_t, *++argv_t);
    }
#else
    if (filespec_name (*argv))
      if (strstr (filespec_name (*argv), "ebug") || strstr (filespec_name (*argv), "DEBUG")) {
          char **argv_t = argv;
          int argc_t = argc;

          debug_logging_mode = TRUE;

          while (--argc_t)
            error_line ("arg %d: %s", argc - argc_t, *++argv_t);
    }
#endif

    // loop through command-line arguments

    while (--argc) {
#if defined (WIN32)
      if ((**++argv == '-' || **argv == '/') && (*argv)[1])
#else
      if ((**++argv == '-') && (*argv)[1])
#endif
          while (*++*argv)
            switch (**argv) {
                case 'A': case 'a':
                  album_mode = 1;
                  break;

                case 'C': case 'c':
                  clean_mode = 1;
                  break;

                case 'D': case 'd':
                  display_mode = 1;
                  break;

#if defined (WIN32)
                case 'L': case 'l':
                  SetPriorityClass (GetCurrentProcess(), IDLE_PRIORITY_CLASS);
                  break;
#endif
                case 'Q': case 'q':
                  quiet_mode = 1;
                  break;

                case 'I': case 'i':
                  ignore_wvc = 1;
                  break;

                case 'S': case 's':
                  show_mode = 1;
                  break;

                default:
                  error_line ("illegal option: %c !", **argv);
                  ++error_count;
            }
      else {
            matches = realloc (matches, (num_files + 1) * sizeof (*matches));
            matches [num_files] = malloc (strlen (*argv) + 10);
            strcpy (matches [num_files], *argv);

            if (*(matches [num_files]) != '-' && *(matches [num_files]) != '@' &&
                !filespec_ext (matches [num_files]))
                    strcat (matches [num_files], ".wv");

            num_files++;
      }
    }

    // check for various command-line argument problems

    if (clean_mode && (album_mode || display_mode || show_mode)) {
      error_line ("clean mode can't be used with album, show, or display mode!");
      ++error_count;
    }
    else if (show_mode && (album_mode || display_mode)) {
      error_line ("show mode can't be used with album or display mode!");
      ++error_count;
    }

    if (!quiet_mode && !error_count)
      fprintf (stderr, sign_on, VERSION_OS, VERSION_STR, DATE_STR);

    if (!num_files) {
      printf ("%s", usage);
      return 1;
    }

    if (error_count)
      return 1;

    setup_break ();

    for (file_index = 0; file_index < num_files; ++file_index) {
      char *infilename = matches [file_index];

      // If the single infile specification begins with a '@', then it
      // actually points to a file that contains the names of the files
      // to be converted. This was included for use by Wim Speekenbrink's
      // frontends, but could be used for other purposes.

      if (*infilename == '@') {
          FILE *list = fopen (infilename+1, "rt");
          int di, c;

          for (di = file_index; di < num_files - 1; di++)
            matches [di] = matches [di + 1];

          file_index--;
          num_files--;

          if (list == NULL) {
            error_line ("file %s not found!", infilename+1);
            free (infilename);
            return 1;
          }

          while ((c = getc (list)) != EOF) {

            while (c == '\n')
                c = getc (list);

            if (c != EOF) {
                char *fname = malloc (PATH_MAX);
                int ci = 0;

                do
                  fname [ci++] = c;
                while ((c = getc (list)) != '\n' && c != EOF && ci < PATH_MAX);

                fname [ci++] = '\0';
                fname = realloc (fname, ci);
                matches = realloc (matches, ++num_files * sizeof (*matches));

                for (di = num_files - 1; di > file_index + 1; di--)
                  matches [di] = matches [di - 1];

                matches [++file_index] = fname;
            }
          }

          fclose (list);
          free (infilename);
      }
#if defined (WIN32)
      else if (filespec_wild (infilename)) {
          FILE *list = fopen (infilename+1, "rt");
          int di;

          for (di = file_index; di < num_files - 1; di++)
            matches [di] = matches [di + 1];

          file_index--;
          num_files--;

          if ((i = _findfirst (infilename, &_finddata_t)) != -1L) {
            do {
                if (!(_finddata_t.attrib & _A_SUBDIR)) {
                  matches = realloc (matches, ++num_files * sizeof (*matches));

                  for (di = num_files - 1; di > file_index + 1; di--)
                      matches [di] = matches [di - 1];

                  matches [++file_index] = malloc (strlen (infilename) + strlen (_finddata_t.name) + 10);
                  strcpy (matches [file_index], infilename);
                  *filespec_name (matches [file_index]) = '\0';
                  strcat (matches [file_index], _finddata_t.name);
                }
            } while (_findnext (i, &_finddata_t) == 0);

            _findclose (i);
          }

          free (infilename);
      }
#endif
    }

    // if we found any files to process, this is where we start

    if (num_files) {
      float *track_gains, *track_peaks, album_gain;
      float track_peak, album_peak = 0.0;
      int i;

      track_gains = malloc (sizeof (*track_gains) * num_files);
      track_peaks = malloc (sizeof (*track_peaks) * num_files);

      // Loop through and analyze files in list. If we are in album mode we just keep
        // track of everything and modify the tags in another pass. If we're not in
        // album mode then we can update the files here.

      for (file_index = 0; !clean_mode && !show_mode && file_index < num_files; ++file_index) {
          if (check_break ())
            break;

          if (num_files > 1)
            fprintf (stderr, "\n%s:\n", matches [file_index]);

          result = analyze_file (matches [file_index], track_histogram, &track_peak);

          if (result != NO_ERROR) {
            ++error_count;

            if (album_mode || result == HARD_ERROR) {
                result = HARD_ERROR;
                break;
            }
            else
                continue;
          }

          track_gains [file_index] = calc_replaygain (track_histogram);
          track_peaks [file_index] = track_peak;

          if (!quiet_mode) {
            error_line ("replaygain_track_gain = %+.2f dB", track_gains [file_index]);
            error_line ("replaygain_track_peak = %.6f", track_peaks [file_index]);
          }

          if (album_mode) {
            for (i = 0; i < HISTOGRAM_SLOTS; ++i)
                album_histogram [i] += track_histogram [i];

            if (track_peak > album_peak)
                album_peak = track_peak;
          }
          else if (!display_mode) {
            result = update_file (matches [file_index], track_gains [file_index], track_peaks [file_index], 0, 0);

            if (result != NO_ERROR) {
                ++error_count;

                if (result == HARD_ERROR)
                  break;
            }
          }
      }

      if (result != HARD_ERROR) {
          album_gain = calc_replaygain (album_histogram);

          if (album_mode && !quiet_mode && num_files > 1) {
            error_line ("\nalbum results:");
            error_line ("replaygain_album_gain = %+.2f dB", album_gain);
            error_line ("replaygain_album_peak = %.6f", album_peak);
          }
      }

      // If we are in album mode or clear mode, this is where we loop through and modify
        // the tags (or just show existing stored values).

      if (result != HARD_ERROR)
          for (file_index = 0; (clean_mode || album_mode || show_mode) && !display_mode && file_index < num_files; ++file_index) {
            if (check_break ())
                break;

            if (num_files > 1)
                fprintf (stderr, "\n%s:\n", matches [file_index]);

                if (show_mode)
                    result = show_file_info (matches [file_index], stdout);
                else
                    result = update_file (matches [file_index], track_gains [file_index], track_peaks [file_index], album_gain, album_peak);

            free (matches [file_index]);

            if (result != NO_ERROR) {
                ++error_count;

                if (result == HARD_ERROR)
                  break;
            }
          }

      if (num_files > 1) {
          if (error_count)
            fprintf (stderr, "\n **** warning: errors occurred in %d of %d files! ****\n", error_count, num_files);
          else if (!quiet_mode) {
            fprintf (stderr, "\n **** %d files successfully processed ****\n", num_files);
          }
      }

      free (matches);
    }
    else {
      ++error_count;
      error_line ("nothing to do!");
    }

#ifdef DEBUG_ALLOC
    error_line ("malloc_count = %d", dump_alloc ());
#endif

    return error_count ? 1 : 0;
}

// Unpack the specified WavPack input file and analyze it for ReplayGain
// information.

static void calc_stereo_peak (float *samples, uint32_t samcnt, float *peak_p);
static double calc_stereo_rms (float *samples, uint32_t samcnt);
static int filter_init (uint32_t sample_rate);
static void filter_stereo_samples (float *samples, uint32_t samcnt);
static void float_samples (float *dst, int32_t *src, uint32_t samcnt, float scale);

static int analyze_file (char *infilename, uint32_t *histogram, float *peak)
{
    int result = NO_ERROR, open_flags = 0, num_channels, wvc_mode;
    uint32_t total_unpacked_samples = 0, window_samples;
    double progress = -1.0;
    int32_t *temp_buffer;
    WavpackContext *wpc;
    char error [80];

    memset (histogram, 0, sizeof (*histogram) * HISTOGRAM_SLOTS);
    *peak = 0.0;

    // use library to open WavPack file

    if (!ignore_wvc)
      open_flags |= OPEN_WVC;

    open_flags |= OPEN_TAGS | OPEN_NORMALIZE;

    wpc = WavpackOpenFileInput (infilename, error, open_flags, 0);

    if (!wpc) {
      error_line (error);
      return SOFT_ERROR;
    }

    wvc_mode = WavpackGetMode (wpc) & MODE_WVC;
    num_channels = WavpackGetNumChannels (wpc);

    if (num_channels > 2) {
      error_line ("can't handle multichannel files yet!");
      return SOFT_ERROR;
    }

    if (!quiet_mode)
      fprintf (stderr, "analyzing %s%s,", *infilename == '-' ? "stdin" :
          FN_FIT (infilename), wvc_mode ? " (+.wvc)" : "");

    window_samples = WavpackGetSampleRate (wpc) / 20;
    temp_buffer = malloc (window_samples * 8);

    if (!filter_init (WavpackGetSampleRate (wpc)))
      result = SOFT_ERROR;

    while (result == NO_ERROR) {
      uint32_t samples_to_unpack, samples_unpacked;
      int32_t level;

      samples_to_unpack = window_samples;
      samples_unpacked = WavpackUnpackSamples (wpc, temp_buffer, samples_to_unpack);
      total_unpacked_samples += samples_unpacked;

      if (samples_unpacked) {
          if (!(WavpackGetMode (wpc) & MODE_FLOAT))
            switch (WavpackGetBytesPerSample (wpc)) {
                case 1:
                  float_samples ((float *) temp_buffer, temp_buffer, samples_unpacked * num_channels, 1.0 / 128.0);
                  break;

                case 2:
                  float_samples ((float *) temp_buffer, temp_buffer, samples_unpacked * num_channels, 1.0 / 32768.0);
                  break;

                case 3:
                  float_samples ((float *) temp_buffer, temp_buffer, samples_unpacked * num_channels, 1.0 / 8388608.0);
                  break;

                case 4:
                  float_samples ((float *) temp_buffer, temp_buffer, samples_unpacked * num_channels, 1.0 / 2147483648.0);
                  break;
            }

          if (num_channels == 1) {
            int32_t *dst = temp_buffer + samples_unpacked * 2;
            int32_t *src = temp_buffer + samples_unpacked;
            uint32_t cnt = samples_unpacked;

            while (cnt--) {
                *--dst = *--src;
                *--dst = *src;
            }     
          }

          calc_stereo_peak ((float *) temp_buffer, samples_unpacked, peak);
          filter_stereo_samples ((float *) temp_buffer, samples_unpacked);
          level = floor (100 * calc_stereo_rms ((float *) temp_buffer, samples_unpacked));

          if (level < 0)
            histogram [0]++;
          else if (level >= HISTOGRAM_SLOTS)
            histogram [HISTOGRAM_SLOTS - 1]++;
          else
            histogram [level]++;
      }
      else
          break;

      if (check_break ()) {
          fprintf (stderr, "^C\n");
          result = HARD_ERROR;
          break;
      }

      if (WavpackGetProgress (wpc) != -1.0 &&
          progress != floor (WavpackGetProgress (wpc) * 100.0 + 0.5)) {
            int nobs = progress == -1.0;

            progress = WavpackGetProgress (wpc);
            display_progress (progress);
            progress = floor (progress * 100.0 + 0.5);

            if (!quiet_mode)
                fprintf (stderr, "%s%3d%% done...",
                  nobs ? " " : "\b\b\b\b\b\b\b\b\b\b\b\b", (int) progress);
      }
    }

    free (temp_buffer);

    if (result == NO_ERROR && WavpackGetNumSamples (wpc) != (uint32_t) -1 &&
      total_unpacked_samples != WavpackGetNumSamples (wpc)) {
          error_line ("incorrect number of samples!");
          result = SOFT_ERROR;
    }

    if (result == NO_ERROR && WavpackGetNumErrors (wpc)) {
      error_line ("crc errors detected in %d block(s)!", WavpackGetNumErrors (wpc));
      result = SOFT_ERROR;
    }

    WavpackCloseFile (wpc);
    return result;
}

// Update the tag of the specified file to reflect the results of the ReplayGain analysis
// (or just to remove existing ReplayGain information).

static int update_file (char *infilename, float track_gain, float track_peak, float album_gain, float album_peak)
{
    int write_tag = FALSE;
    char error [80], value [20];
    WavpackContext *wpc;

    // use library to open WavPack file

    wpc = WavpackOpenFileInput (infilename, error, OPEN_EDIT_TAGS, 0);

    if (!wpc) {
      error_line (error);
      return SOFT_ERROR;
    }

    if (clean_mode) {
      int items_removed = 0;

      if (WavpackDeleteTagItem (wpc, "replaygain_track_gain"))
          ++items_removed;

      if (WavpackDeleteTagItem (wpc, "replaygain_track_peak"))
          ++items_removed;

      if (WavpackDeleteTagItem (wpc, "replaygain_album_gain"))
          ++items_removed;

      if (WavpackDeleteTagItem (wpc, "replaygain_album_peak"))
          ++items_removed;

      if (!quiet_mode && items_removed) {
          error_line ("%d ReplayGain values cleaned", items_removed);
          write_tag = TRUE;
      }
      else
          error_line ("no ReplayGain values found");
    }
    else {
      if ((WavpackGetMode (wpc) & (MODE_VALID_TAG | MODE_APETAG)) == MODE_VALID_TAG) {
          char title [40], artist [40], album [40], year [10], comment [40], track [10];

          WavpackGetTagItem (wpc, "title", title, sizeof (title));
          WavpackGetTagItem (wpc, "artist", artist, sizeof (artist));
          WavpackGetTagItem (wpc, "album", album, sizeof (album));
          WavpackGetTagItem (wpc, "year", year, sizeof (year));
          WavpackGetTagItem (wpc, "comment", comment, sizeof (comment));
          WavpackGetTagItem (wpc, "track", track, sizeof (track));
            
          if (title [0])
            WavpackAppendTagItem (wpc, "Title", title, strlen (title));
            
          if (artist [0])
            WavpackAppendTagItem (wpc, "Artist", artist, strlen (artist));
            
          if (album [0])
            WavpackAppendTagItem (wpc, "Album", album, strlen (album));
            
          if (year [0])
            WavpackAppendTagItem (wpc, "Year", year, strlen (year));
            
          if (comment [0])
            WavpackAppendTagItem (wpc, "Comment", comment, strlen (comment));
            
          if (track [0])
            WavpackAppendTagItem (wpc, "Track", track, strlen (track));
            
          error_line ("warning: ID3v1 tag converted to APEv2");
      }

      sprintf (value, "%+.2f dB", track_gain);
      WavpackAppendTagItem (wpc, "replaygain_track_gain", value, strlen (value));

      sprintf (value, "%.6f", track_peak);
      WavpackAppendTagItem (wpc, "replaygain_track_peak", value, strlen (value));

      if (album_mode) {
          sprintf (value, "%+.2f dB", album_gain);
          WavpackAppendTagItem (wpc, "replaygain_album_gain", value, strlen (value));
          sprintf (value, "%.6f", album_peak);
          WavpackAppendTagItem (wpc, "replaygain_album_peak", value, strlen (value));
      }

      if (!quiet_mode)
          error_line ("%d ReplayGain values appended", album_mode ? 4 : 2);

      write_tag = TRUE;
    }

    if (write_tag && !WavpackWriteTag (wpc)) {
      error_line ("%s", wpc->error_message);
      return SOFT_ERROR;
    }

    WavpackCloseFile (wpc);
    return NO_ERROR;
}

// Just show any ReplayGain tags for the specified file

static int show_file_info (char *infilename, FILE *dst)
{
    char error [80], value [20];
    WavpackContext *wpc;
    int items = 0;

    // use library to open WavPack file

    wpc = WavpackOpenFileInput (infilename, error, OPEN_TAGS, 0);

    if (!wpc) {
      error_line (error);
      return SOFT_ERROR;
    }

    fprintf (dst, "\nfile: %s\n", infilename);

    if (WavpackGetTagItem (wpc, "replaygain_track_gain", value, sizeof (value))) {
        fprintf (dst, "replaygain_track_gain = %s\n", value);
        ++items;
    }

    if (WavpackGetTagItem (wpc, "replaygain_track_peak", value, sizeof (value))) {
        fprintf (dst, "replaygain_track_peak = %s\n", value);
        ++items;
    }

    if (WavpackGetTagItem (wpc, "replaygain_album_gain", value, sizeof (value))) {
        fprintf (dst, "replaygain_album_gain = %s\n", value);
        ++items;
    }

    if (WavpackGetTagItem (wpc, "replaygain_album_peak", value, sizeof (value))) {
        fprintf (dst, "replaygain_album_peak = %s\n", value);
        ++items;
    }

    if (!items)
        fprintf (dst, "no ReplayGain values found\n");

    WavpackCloseFile (wpc);
    return NO_ERROR;
}

// Calculate the ReplayGain value from the specified loudness histogram; clip to +/- 24 dB

static float calc_replaygain (uint32_t *histogram)
{
    uint32_t loud_count = 0, total_windows = 0;
    float unclipped_gain;
    int i;

    for (i = 0; i < HISTOGRAM_SLOTS; i++)
      total_windows += histogram [i];

    while (i--)
      if ((loud_count += histogram [i]) * 20 >= total_windows)
          break;

    unclipped_gain = 64.54 - i / 100.0;

    if (unclipped_gain > 24.0)
        return 24.0;
    else if (unclipped_gain < -24.0)
        return -24.0;
    else
        return unclipped_gain;
}

// Convert the specified samples into floating-point using the specified scale factor.

static void float_samples (float *dst, int32_t *src, uint32_t samcnt, float scale)
{
    while (samcnt--)
      *dst++ = *src++ * scale;
}

// These are the filters used to calculate perceived loudness. The table data was copied
// from the Foobar2000 source code.

#define YULE_ORDER         10
#define BUTTER_ORDER        2

struct rg_freqinfo
{
    uint32_t rate;
    double BYule[YULE_ORDER+1],AYule[YULE_ORDER+1],BButter[BUTTER_ORDER+1],AButter[BUTTER_ORDER+1];
};

static struct rg_freqinfo freqinfos[] =
{
    {
        48000,
        { 0.03857599435200, -0.02160367184185, -0.00123395316851, -0.00009291677959, -0.01655260341619,  0.02161526843274, -0.02074045215285,  0.00594298065125,  0.00306428023191,  0.00012025322027,  0.00288463683916 },
        { 1., -3.84664617118067,  7.81501653005538,-11.34170355132042, 13.05504219327545,-12.28759895145294,  9.48293806319790, -5.87257861775999,  2.75465861874613, -0.86984376593551, 0.13919314567432 },
        { 0.98621192462708, -1.97242384925416, 0.98621192462708 },
        { 1., -1.97223372919527, 0.97261396931306 },
    },

    {
        44100,
        { 0.05418656406430, -0.02911007808948, -0.00848709379851, -0.00851165645469, -0.00834990904936,  0.02245293253339, -0.02596338512915,  0.01624864962975, -0.00240879051584,  0.00674613682247, -0.00187763777362 },
        { 1., -3.47845948550071,  6.36317777566148, -8.54751527471874,  9.47693607801280, -8.81498681370155,  6.85401540936998, -4.39470996079559,  2.19611684890774, -0.75104302451432, 0.13149317958808 },
        { 0.98500175787242, -1.97000351574484, 0.98500175787242 },
        { 1., -1.96977855582618, 0.97022847566350 },
    },

    {
        32000,
        { 0.15457299681924, -0.09331049056315, -0.06247880153653,  0.02163541888798, -0.05588393329856,  0.04781476674921,  0.00222312597743,  0.03174092540049, -0.01390589421898,  0.00651420667831, -0.00881362733839 },
        { 1., -2.37898834973084,  2.84868151156327, -2.64577170229825,  2.23697657451713, -1.67148153367602,  1.00595954808547, -0.45953458054983,  0.16378164858596, -0.05032077717131, 0.02347897407020 },
        { 0.97938932735214, -1.95877865470428, 0.97938932735214 },
        { 1., -1.95835380975398, 0.95920349965459 },
    },

    {
        24000,
        { 0.30296907319327, -0.22613988682123, -0.08587323730772,  0.03282930172664, -0.00915702933434, -0.02364141202522, -0.00584456039913,  0.06276101321749, -0.00000828086748,  0.00205861885564, -0.02950134983287 },
        { 1., -1.61273165137247,  1.07977492259970, -0.25656257754070, -0.16276719120440, -0.22638893773906,  0.39120800788284, -0.22138138954925,  0.04500235387352,  0.02005851806501, 0.00302439095741 },
        { 0.97531843204928, -1.95063686409857, 0.97531843204928 },
        { 1., -1.95002759149878, 0.95124613669835 },
    },

    {
        22050,
        { 0.33642304856132, -0.25572241425570, -0.11828570177555,  0.11921148675203, -0.07834489609479, -0.00469977914380, -0.00589500224440,  0.05724228140351,  0.00832043980773, -0.01635381384540, -0.01760176568150 },
        { 1., -1.49858979367799,  0.87350271418188,  0.12205022308084, -0.80774944671438,  0.47854794562326, -0.12453458140019, -0.04067510197014,  0.08333755284107, -0.04237348025746, 0.02977207319925 },
        { 0.97316523498161, -1.94633046996323, 0.97316523498161 },
        { 1., -1.94561023566527, 0.94705070426118 },
    },

    {
        16000,
        { 0.44915256608450, -0.14351757464547, -0.22784394429749, -0.01419140100551,  0.04078262797139, -0.12398163381748,  0.04097565135648,  0.10478503600251, -0.01863887810927, -0.03193428438915,  0.00541907748707 },
        { 1., -0.62820619233671,  0.29661783706366, -0.37256372942400,  0.00213767857124, -0.42029820170918,  0.22199650564824,  0.00613424350682,  0.06747620744683,  0.05784820375801, 0.03222754072173 },
        { 0.96454515552826, -1.92909031105652, 0.96454515552826 },
        { 1., -1.92783286977036, 0.93034775234268 },
    },

    {
        12000,
        { 0.56619470757641, -0.75464456939302,  0.16242137742230,  0.16744243493672, -0.18901604199609,  0.30931782841830, -0.27562961986224,  0.00647310677246,  0.08647503780351, -0.03788984554840, -0.00588215443421 },
        { 1., -1.04800335126349,  0.29156311971249, -0.26806001042947,  0.00819999645858,  0.45054734505008, -0.33032403314006,  0.06739368333110, -0.04784254229033,  0.01639907836189, 0.01807364323573 },
        { 0.96009142950541, -1.92018285901082, 0.96009142950541 },
        { 1., -1.91858953033784, 0.92177618768381 },
    },
    
    {
        11025,
        { 0.58100494960553, -0.53174909058578, -0.14289799034253,  0.17520704835522,  0.02377945217615,  0.15558449135573, -0.25344790059353,  0.01628462406333,  0.06920467763959, -0.03721611395801, -0.00749618797172 },
        { 1., -0.51035327095184, -0.31863563325245, -0.20256413484477,  0.14728154134330,  0.38952639978999, -0.23313271880868, -0.05246019024463, -0.02505961724053,  0.02442357316099, 0.01818801111503 },
        { 0.95856916599601, -1.91713833199203, 0.95856916599601 },
        { 1., -1.91542108074780, 0.91885558323625 },
    },

    {
        8000,
        { 0.53648789255105, -0.42163034350696, -0.00275953611929,  0.04267842219415, -0.10214864179676,  0.14590772289388, -0.02459864859345, -0.11202315195388, -0.04060034127000,  0.04788665548180, -0.02217936801134 },
        { 1., -0.25049871956020, -0.43193942311114, -0.03424681017675, -0.04678328784242,  0.26408300200955,  0.15113130533216, -0.17556493366449, -0.18823009262115,  0.05477720428674, 0.04704409688120 },
        { 0.94597685600279, -1.89195371200558, 0.94597685600279 },
        { 1., -1.88903307939452, 0.89487434461664 },
    },

    {
        18900,
        {0.38524531015142,  -0.27682212062067,  -0.09980181488805,   0.09951486755646,  -0.08934020156622,  -0.00322369330199,  -0.00110329090689,   0.03784509844682,   0.01683906213303,  -0.01147039862572,  -0.01941767987192 },
        {1.00000000000000,  -1.29708918404534,   0.90399339674203,  -0.29613799017877,  -0.42326645916207,   0.37934887402200,  -0.37919795944938,   0.23410283284785,  -0.03892971758879,   0.00403009552351,   0.03640166626278 },
        {0.96535326815829,  -1.93070653631658,   0.96535326815829 },
        {1.00000000000000,  -1.92950577983524,   0.93190729279793 },
    },

    {
        37800,
        {0.08717879977844,  -0.01000374016172,  -0.06265852122368,  -0.01119328800950,  -0.00114279372960,   0.02081333954769,  -0.01603261863207,   0.01936763028546,   0.00760044736442,  -0.00303979112271,  -0.00075088605788 },
        {1.00000000000000,  -2.62816311472146,   3.53734535817992,  -3.81003448678921,   3.91291636730132,  -3.53518605896288,   2.71356866157873,  -1.86723311846592,   1.12075382367659,  -0.48574086886890,   0.11330544663849 },
        {0.98252400815195,  -1.96504801630391,   0.98252400815195 },
        {1.00000000000000,  -1.96474258269041,   0.96535344991740 },
    },

    {
        56000,
        {0.03144914734085,  -0.06151729206963,   0.08066788708145,  -0.09737939921516,   0.08943210803999,  -0.06989984672010,   0.04926972841044,  -0.03161257848451,   0.01456837493506,  -0.00316015108496,   0.00132807215875 },
        {1.00000000000000,  -4.87377313090032,  12.03922160140209, -20.10151118381395,  25.10388534415171, -24.29065560815903,  18.27158469090663, -10.45249552560593,   4.30319491872003,  -1.13716992070185,   0.14510733527035 },
        {0.98816995007392,  -1.97633990014784,   0.98816995007392 },
        {1.00000000000000,  -1.97619994516973,   0.97647985512594 },
    },

    {
        64000,
        {0.02613056568174,  -0.08128786488109,   0.14937282347325,  -0.21695711675126,   0.25010286673402,  -0.23162283619278,   0.17424041833052,  -0.10299599216680,   0.04258696481981,  -0.00977952936493,   0.00105325558889 },
        {1.00000000000000,  -5.73625477092119,  16.15249794355035, -29.68654912464508,  39.55706155674083, -39.82524556246253,  30.50605345013009, -17.43051772821245,   7.05154573908017,  -1.80783839720514,   0.22127840210813 },
        {0.98964101933472,  -1.97928203866944,   0.98964101933472 },
        {1.00000000000000,  -1.97917472731009,   0.97938935002880 },
    },

    {
        88200,
        {0.02667482047416,  -0.11377479336097,   0.23063167910965,  -0.30726477945593,   0.33188520686529,  -0.33862680249063,   0.31807161531340,  -0.23730796929880,   0.12273894790371,  -0.03840017967282,   0.00549673387936 },
        {1.00000000000000,  -6.31836451657302,  18.31351310801799, -31.88210014815921,  36.53792146976740, -28.23393036467559,  14.24725258227189,  -4.04670980012854,   0.18865757280515,   0.25420333563908,  -0.06012333531065 },
        {0.99247255046129,  -1.98494510092259,   0.99247255046129 },
        {1.00000000000000,  -1.98488843762335,   0.98500176422183 },
    },

    {
        96000,
        {0.00588138296683,  -0.01613559730421,   0.02184798954216,  -0.01742490405317,   0.00464635643780,   0.01117772513205,  -0.02123865824368,   0.01959354413350,  -0.01079720643523,   0.00352183686289,  -0.00063124341421 },
        {1.00000000000000,  -5.97808823642008,  16.21362507964068, -25.72923730652599,  25.40470663139513, -14.66166287771134,   2.81597484359752,   2.51447125969733,  -2.23575306985286,   0.75788151036791,  -0.10078025199029 },
        {0.99308203517541,  -1.98616407035082,   0.99308203517541 },
        {1.00000000000000,  -1.98611621154089,   0.98621192916075 },
    },

    {
        112000,
        {0.00528778718259,  -0.01893240907245,   0.03185982561867,  -0.02926260297838,   0.00715743034072,   0.01985743355827,  -0.03222614850941,   0.02565681978192,  -0.01210662313473,   0.00325436284541,  -0.00044173593001 },
        {1.00000000000000,  -6.24932108456288,  17.42344320538476, -27.86819709054896,  26.79087344681326, -13.43711081485123,  -0.66023612948173,   6.03658091814935,  -4.24926577030310,   1.40829268709186,  -0.19480852628112 },
        {0.99406737810867,  -1.98813475621734,   0.99406737810867 },
        {1.00000000000000,  -1.98809955990514,   0.98816995252954 },
    },

    {
        128000,
        {0.00553120584305,  -0.02112620545016,   0.03549076243117,  -0.03362498312306,   0.01425867248183,   0.01344686928787,  -0.03392770787836,   0.03464136459530,  -0.02039116051549,   0.00667420794705,  -0.00093763762995 },
        {1.00000000000000,  -6.14581710839925,  16.04785903675838, -22.19089131407749,  15.24756471580286,  -0.52001440400238,  -8.00488641699940,   6.60916094768855,  -2.37856022810923,   0.33106947986101,   0.00459820832036 },
        {0.99480702681278,  -1.98961405362557,   0.99480702681278 },
        {1.00000000000000,  -1.98958708647324,   0.98964102077790 },
    },

    {
        144000,
        {0.00639682359450,  -0.02556437970955,   0.04230854400938,  -0.03722462201267,   0.01718514827295,   0.00610592243009,  -0.03065965747365,   0.04345745003539,  -0.03298592681309,   0.01320937236809,  -0.00220304127757 },
        {1.00000000000000,  -6.14814623523425,  15.80002457141566, -20.78487587686937,  11.98848552310315,   3.36462015062606, -10.22419868359470,   6.65599702146473,  -1.67141861110485,  -0.05417956536718,   0.07374767867406 },
        {0.99538268958706,  -1.99076537917413,   0.99538268958706 },
        {1.00000000000000,  -1.99074405950505,   0.99078669884321 },
    },

    {
        176400,
        {0.00268568524529,  -0.00852379426080,   0.00852704191347,   0.00146116310295,  -0.00950855828762,   0.00625449515499,   0.00116183868722,  -0.00362461417136,   0.00203961000134,  -0.00050664587933,   0.00004327455427 },
        {1.00000000000000,  -5.57512782763045,  12.44291056065794, -12.87462799681221,   3.08554846961576,   6.62493459880692,  -7.07662766313248,   2.51175542736441,   0.06731510802735,  -0.24567753819213,   0.03961404162376 },
        {0.99622916581118,  -1.99245833162236,   0.99622916581118 },
        {1.00000000000000,  -1.99244411238133,   0.99247255086339 },
    },

    {
        192000,
        {0.01184742123123,  -0.04631092400086,   0.06584226961238,  -0.02165588522478,  -0.05656260778952,   0.08607493592760,  -0.03375544339786,  -0.04216579932754,   0.06416711490648,  -0.03444708260844,   0.00697275872241 },
        {1.00000000000000,  -5.24727318348167,  10.60821585192244,  -8.74127665810413,  -1.33906071371683,   8.07972882096606,  -5.46179918950847,   0.54318070652536,   0.87450969224280,  -0.34656083539754,   0.03034796843589 },
        {0.99653501465135,  -1.99307002930271,   0.99653501465135 },
        {1.00000000000000,  -1.99305802314321,   0.99308203546221 },
    }
};

static double *yule_coeff_a, *yule_coeff_b, *butter_coeff_a, *butter_coeff_b;
static float yule_hist_a [256], yule_hist_b [256], butter_hist_a [256], butter_hist_b [256];
static int yule_hist_i, butter_hist_i;

// Initialize filters; return FALSE is specified sampling rate is not supported

static int filter_init (uint32_t sample_rate)
{
    int i, n = sizeof (freqinfos) / sizeof (freqinfos [0]);

    for (i = 0; i < n; ++i)
      if (freqinfos [i].rate == sample_rate)
          break;

    if (i == n) {
      error_line ("sample rate of %d is not supported!", sample_rate);
      return FALSE;
    }

    yule_coeff_a = freqinfos [i].AYule;
    yule_coeff_b = freqinfos [i].BYule;
    butter_coeff_a = freqinfos [i].AButter;
    butter_coeff_b = freqinfos [i].BButter;

    memset (yule_hist_a, 0, sizeof (yule_hist_a));
    memset (yule_hist_b, 0, sizeof (yule_hist_b));
    yule_hist_i = 20;

    memset (butter_hist_a, 0, sizeof (butter_hist_a));
    memset (butter_hist_b, 0, sizeof (butter_hist_b));
    butter_hist_i = 4;
    return TRUE;
}

// Optimized mplementation of 2nd-order IIR stereo filter

static void butter_filter_stereo_samples (float *samples, uint32_t samcnt)
{
    double left, right;
    int i, j;

    i = butter_hist_i;

    // If filter history is very small magnitude, clear it completely to prevent denormals
    // from rattling around in there forever (slowing us down).

    for (j = -4; j < 0; ++j)
      if (fabs (butter_hist_a [i + j]) > 1e-10 || fabs (butter_hist_b [i + j]) > 1e-10)
          break;

    if (!j) {
      memset (butter_hist_a, 0, sizeof (butter_hist_a));
      memset (butter_hist_b, 0, sizeof (butter_hist_b));
    }

    while (samcnt--) {
      left   = (butter_hist_b [i] = samples [0]) * butter_coeff_b [0];
      right  = (butter_hist_b [i + 1] = samples [1]) * butter_coeff_b [0];
      left  += butter_hist_b [i - 2] * butter_coeff_b [1] - butter_hist_a [i - 2] * butter_coeff_a [1];  
      right += butter_hist_b [i - 1] * butter_coeff_b [1] - butter_hist_a [i - 1] * butter_coeff_a [1];  
      left  += butter_hist_b [i - 4] * butter_coeff_b [2] - butter_hist_a [i - 4] * butter_coeff_a [2];  
      right += butter_hist_b [i - 3] * butter_coeff_b [2] - butter_hist_a [i - 3] * butter_coeff_a [2];  
      samples [0] = butter_hist_a [i] = left;
      samples [1] = butter_hist_a [i + 1] = right;
      samples += 2;

      if ((i += 2) == 256) {
          memcpy (butter_hist_a, butter_hist_a + 252, sizeof (butter_hist_a [0]) * 4);
          memcpy (butter_hist_b, butter_hist_b + 252, sizeof (butter_hist_b [0]) * 4);
          i = 4;
      }
    }

    butter_hist_i = i;
}

// Optimized mplementation of 10th-order IIR stereo filter

static void yule_filter_stereo_samples (float *samples, uint32_t samcnt)
{
    double left, right;
    int i, j;

    i = yule_hist_i;

    // If filter history is very small magnitude, clear it completely to prevent denormals
    // from rattling around in there forever (slowing us down).

    for (j = -20; j < 0; ++j)
      if (fabs (yule_hist_a [i + j]) > 1e-10 || fabs (yule_hist_b [i + j]) > 1e-10)
          break;

    if (!j) {
      memset (yule_hist_a, 0, sizeof (yule_hist_a));
      memset (yule_hist_b, 0, sizeof (yule_hist_b));
    }

    while (samcnt--) {
      left   = (yule_hist_b [i] = samples [0]) * yule_coeff_b [0];
      right  = (yule_hist_b [i + 1] = samples [1]) * yule_coeff_b [0];
      left  += yule_hist_b [i - 2] * yule_coeff_b [1] - yule_hist_a [i - 2] * yule_coeff_a [1];  
      right += yule_hist_b [i - 1] * yule_coeff_b [1] - yule_hist_a [i - 1] * yule_coeff_a [1];  
      left  += yule_hist_b [i - 4] * yule_coeff_b [2] - yule_hist_a [i - 4] * yule_coeff_a [2];  
      right += yule_hist_b [i - 3] * yule_coeff_b [2] - yule_hist_a [i - 3] * yule_coeff_a [2];  
      left  += yule_hist_b [i - 6] * yule_coeff_b [3] - yule_hist_a [i - 6] * yule_coeff_a [3];  
      right += yule_hist_b [i - 5] * yule_coeff_b [3] - yule_hist_a [i - 5] * yule_coeff_a [3];  
      left  += yule_hist_b [i - 8] * yule_coeff_b [4] - yule_hist_a [i - 8] * yule_coeff_a [4];  
      right += yule_hist_b [i - 7] * yule_coeff_b [4] - yule_hist_a [i - 7] * yule_coeff_a [4];  
      left  += yule_hist_b [i - 10] * yule_coeff_b [5] - yule_hist_a [i - 10] * yule_coeff_a [5];  
      right += yule_hist_b [i - 9] * yule_coeff_b [5] - yule_hist_a [i - 9] * yule_coeff_a [5];  
      left  += yule_hist_b [i - 12] * yule_coeff_b [6] - yule_hist_a [i - 12] * yule_coeff_a [6];  
      right += yule_hist_b [i - 11] * yule_coeff_b [6] - yule_hist_a [i - 11] * yule_coeff_a [6];  
      left  += yule_hist_b [i - 14] * yule_coeff_b [7] - yule_hist_a [i - 14] * yule_coeff_a [7];  
      right += yule_hist_b [i - 13] * yule_coeff_b [7] - yule_hist_a [i - 13] * yule_coeff_a [7];  
      left  += yule_hist_b [i - 16] * yule_coeff_b [8] - yule_hist_a [i - 16] * yule_coeff_a [8];  
      right += yule_hist_b [i - 15] * yule_coeff_b [8] - yule_hist_a [i - 15] * yule_coeff_a [8];  
      left  += yule_hist_b [i - 18] * yule_coeff_b [9] - yule_hist_a [i - 18] * yule_coeff_a [9];  
      right += yule_hist_b [i - 17] * yule_coeff_b [9] - yule_hist_a [i - 17] * yule_coeff_a [9];  
      left  += yule_hist_b [i - 20] * yule_coeff_b [10] - yule_hist_a [i - 20] * yule_coeff_a [10];  
      right += yule_hist_b [i - 19] * yule_coeff_b [10] - yule_hist_a [i - 19] * yule_coeff_a [10];  
      samples [0] = yule_hist_a [i] = left;
      samples [1] = yule_hist_a [i + 1] = right;
      samples += 2;

      if ((i += 2) == 256) {
          memcpy (yule_hist_a, yule_hist_a + 236, sizeof (yule_hist_a [0]) * 20);
          memcpy (yule_hist_b, yule_hist_b + 236, sizeof (yule_hist_b [0]) * 20);
          i = 20;
      }
    }

    yule_hist_i = i;
}

// Apply both filters sequentially

static void filter_stereo_samples (float *samples, uint32_t samcnt)
{
    yule_filter_stereo_samples (samples, samcnt);
    butter_filter_stereo_samples (samples, samcnt);
}

// Update largest absolute sample value

static void calc_stereo_peak (float *samples, uint32_t samcnt, float *peak_p)
{
    float peak = 0.0;

    while (samcnt--) {
      if (samples [0] > peak)
          peak = samples [0];
      else if (-samples [0] > peak)
          peak = -samples [0];

      if (samples [1] > peak)
          peak = samples [1];
      else if (-samples [1] > peak)
          peak = -samples [1];

      samples += 2;
    }

    if (peak > *peak_p)
      *peak_p = peak;
}

// Calculate stereo rms level. Minimum value is about -100 dB for digital silence. The 90 dB
// offset is to compensate for the normalized float range and 3 dB is for stereo samples.

static double calc_stereo_rms (float *samples, uint32_t samcnt)
{
    uint32_t cnt = samcnt;
    double sum = 1e-16;

    while (cnt--) {
      sum += samples [0] * samples [0] + samples [1] * samples [1];
      samples += 2;
    }

    return 10 * log10 (sum / samcnt) + 90.0 - 3.0;
}

//////////////////////////////////////////////////////////////////////////////
// This function displays the progress status on the title bar of the DOS   //
// window that WavPack is running in. The "file_progress" argument is for   //
// the current file only and ranges from 0 - 1; this function takes into    //
// account the total number of files to generate a batch progress number.   //
//////////////////////////////////////////////////////////////////////////////

void display_progress (double file_progress)
{
    char title [40];

    file_progress = (file_index + file_progress) / num_files;
    sprintf (title, "%d%% (WvGain)", (int) ((file_progress * 100.0) + 0.5));
    SetConsoleTitle (title);
}

Generated by  Doxygen 1.6.0   Back to index