Sunday, 22 April 2012

Simple SOLA algorithm Main.cpp


/////////////////////////////////////////////////////////////////////
//
// Simple SOLA algorithm example. The example reads a .wav sound
// file with mono-16bit-44100Hz sample format, process it with SOLA
// and writes output into another .wav file.
//
// Copyright (c) Olli Parviainen 2006 <oparviai@iki.fi>
//
/////////////////////////////////////////////////////////////////////

#include <stdexcept>
#include "wavfile.h"

using namespace std;

// Time scaling factor, values > 1.0 increase, values < 1.0 decrease tempo
#define TIME_SCALE      1   // 15% slower tempo
// Processing sequence size (100 msec with 44100Hz samplerate)
#define SEQUENCE        800//4410
// Overlapping size (20 msec)
#define OVERLAP         160//882
// Best overlap offset seeking window (15 msec)
#define SEEK_WINDOW     120//662
// Processing sequence flat mid-section duration
#define FLAT_DURATION   (SEQUENCE - 2 * (OVERLAP))
// Theoretical interval between the processing seqeuences
#define SEQUENCE_SKIP   ((int)((SEQUENCE - OVERLAP) * (TIME_SCALE)))

typedef short SAMPLE;   // sample type, 16bit signed integer

// Use cross-correlation function to find best overlapping offset
// where input_prev and input_new match best with each other
int seek_best_overlap(const SAMPLE *input_prev, const SAMPLE *input_new)
{
   int i;
   int bestoffset = 0;
   float bestcorr = -1e30f;
   float temp[OVERLAP];

   // Precalculate overlapping slopes with input_prev
   for (i = 0; i < OVERLAP; i ++)
   {
      temp[i] = (float)(input_prev[i] * i * (OVERLAP - i));
   }

   // Find best overlap offset within [0..SEEK_WINDOW]
   for (i = 0; i < SEEK_WINDOW; i ++)
   {
      int j;
      float crosscorr = 0;

      for (j = 0; j < OVERLAP; j ++)
      {
         crosscorr += (float)input_new[i + j] * temp[j];
      }
      if (crosscorr > bestcorr)
      {
         // found new best offset candidate
         bestcorr = crosscorr;
         bestoffset = i;
      }
   }
   return bestoffset;
}


// Overlap 'input_prev' with 'input_new' by sliding the amplitudes during
// OVERLAP samples. Store result to 'output'.
void overlap(SAMPLE *output, const SAMPLE *input_prev, const SAMPLE *input_new)
{
   int i;

   for (i = 0; i < OVERLAP; i ++)
   {
      output[i] = (input_prev[i] * (OVERLAP - i) + input_new[i] * i) / OVERLAP;
   }
}


// SOLA algorithm. Performs time scaling for sample data given in 'input',
// write result to 'output'. Return number of output samples.
int sola(SAMPLE *output, const SAMPLE *input, int num_in_samples)
{
   int num_out_samples = 0;
   const SAMPLE *seq_offset = input;
   const SAMPLE *prev_offset;

   int nTest = SEQUENCE_SKIP;
   while (num_in_samples > SEQUENCE_SKIP + SEEK_WINDOW)
   {
      // copy flat mid-sequence from current processing sequence to output
      memcpy(output, seq_offset, FLAT_DURATION * sizeof(SAMPLE));
      // calculate a pointer to overlap at end of the processing sequence
      prev_offset = seq_offset + FLAT_DURATION;

      // update input pointer to theoretical next processing sequence begin
      input += SEQUENCE_SKIP - OVERLAP;
      // seek actual best matching offset using cross-correlation
      seq_offset = input + seek_best_overlap(prev_offset, input);

      // do overlapping between previous & new sequence, copy result to output
      overlap(output + FLAT_DURATION, prev_offset, seq_offset);

      // Update input & sequence pointers by overlapping amount
      seq_offset += OVERLAP;
      input  += OVERLAP;

      // Update output pointer & sample counters
      output += SEQUENCE - OVERLAP;
      num_out_samples += SEQUENCE - OVERLAP;
      num_in_samples -= SEQUENCE_SKIP;
   }

   return num_out_samples;
}



// Buffers for input/output sample data. For sake of simplicity, these are
// just made 'big enough' for the example purpose.
SAMPLE inbuffer[10240000];
SAMPLE outbuffer[20240000];

int main(int numstr, char **pstr)
{

   if (numstr < 3)
   {
      printf("usage: solatest input.wav output.wav\n");
      return -1;
   }

   try
   {
      int insamples, outsamples;

      // Open input file
      WavInFile infile(pstr[1]);

      if ((infile.getSampleRate() != 44100) || (infile.getNumChannels() != 1))
      {
         printf("Sorry, this example processes mono audio sampled at 44100Hz.\n");
         return -1;
      }

      // Read data from input file
      insamples = infile.read(inbuffer, 10240000);

      // Process
      outsamples = sola(outbuffer, inbuffer, insamples);

      // Write result to output file
      WavOutFile outfile(pstr[2], infile.getSampleRate(), infile.getNumBits(), infile.getNumChannels());
      outfile.write(outbuffer, outsamples);
   }
   catch (exception &e)
   {
      printf("Error: %s\n", e.what());
   }

   return 0;
}

No comments:

Post a Comment