content: /**
   * Copyright 2007 DFKI GmbH.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * This file is part of MARY TTS.
   * <p/>
   * MARY TTS is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, version 3 of the License.
   * <p/>
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   * <p/>
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package marytts.signalproc.filter;
  
  import java.io.File;
  import java.io.IOException;
  
  import lib.sound.sampled.AudioFileFormat;
  import lib.sound.sampled.AudioFormat;
  import lib.sound.sampled.AudioInputStream;
  import lib.sound.sampled.AudioSystem;
  import lib.sound.sampled.UnsupportedAudioFileException;
  import marytts.util.data.BufferedDoubleDataSource;
  import marytts.util.data.audio.AudioDoubleDataSource;
  import marytts.util.data.audio.DDSAudioInputStream;
  import marytts.util.signal.SignalProcUtils;
  
  
  /**
   * This class implements a single channel of the complementary filter bank used in
   * [Levine, et. al., 1999] for multiresolution sinusoidal modeling.
   * <p/>
   * [Levine, et. al., 1999] Levine, S. N., Verma, T. S., and Smith III, J. O., "Multiresolution sinusoidal
   * modeling for wideband audio with modifications", in Proc. of the IEEE ICASSP 1998,
   * Volume 6, Issue , 12-15 May 1998, pp. 3585-3588.
   *
   * @author Oytun T&uumlrk
   */
  public class ComplementaryFilterBankChannelAnalyser {
      public double[] lpfOut; //Output of lowpass sub-channel
      public double[] hpfOut; //Output of highpass sub-channel
      public double[] lpfOutInterpolated; //Upsampled version of lowpass sub-channel output
      public double lpfOutEnergy; //Energy of output lowpass sub-channel
  
      protected LowPassFilter Hd;
      protected LowPassFilter Hb;
      protected LowPassFilter Hi;
      protected int filterLengthMinusOne;
      protected double[] filterNumerator;
  
      public ComplementaryFilterBankChannelAnalyser(int N) {
          if (N % 2 != 0)
              N++;
  
          filterLengthMinusOne = N;
          Hd = new LowPassFilter(0.5 * 0.5, filterLengthMinusOne + 1);
          Hb = new LowPassFilter(0.5 * 0.95, (int) (0.5 * filterLengthMinusOne + 1));
          Hi = new LowPassFilter(0.5 * 0.5, filterLengthMinusOne + 1);
  
          filterNumerator = new double[1];
          filterNumerator[0] = 1.0;
      }
  
      public static void main(String[] args) throws UnsupportedAudioFileException, IOException {
          AudioInputStream inputAudio = AudioSystem.getAudioInputStream(new File(args[0]));
          int samplingRate = (int) inputAudio.getFormat().getSampleRate();
          AudioDoubleDataSource signal = new AudioDoubleDataSource(inputAudio);
          double[] x = signal.getAllData();
  
          int N = 512;
          ComplementaryFilterBankChannelAnalyser channel = new ComplementaryFilterBankChannelAnalyser(N);
          channel.apply(x);
  
          DDSAudioInputStream outputAudio;
          String outFileName;
  
          //Lowpass output
          AudioFormat loFormat = new AudioFormat(inputAudio.getFormat().getSampleRate() * 0.5f, inputAudio.getFormat().getSampleSizeInBits(), inputAudio.getFormat().getChannels(), true, true);
          outputAudio = new DDSAudioInputStream(new BufferedDoubleDataSource(channel.lpfOut), loFormat);
          outFileName = args[0].substring(0, args[0].length() - 4) + "_lo.wav";
          AudioSystem.write(outputAudio, AudioFileFormat.Type.WAVE, new File(outFileName));
          //
  
          //Lowpass interpolated output
          AudioFormat loiFormat = new AudioFormat(inputAudio.getFormat().getSampleRate(), inputAudio.getFormat().getSampleSizeInBits(), inputAudio.getFormat().getChannels(), true, true);
          outputAudio = new DDSAudioInputStream(new BufferedDoubleDataSource(channel.lpfOutInterpolated), loiFormat);
          outFileName = args[0].substring(0, args[0].length() - 4) + "_loi.wav";
          AudioSystem.write(outputAudio, AudioFileFormat.Type.WAVE, new File(outFileName));
          //
  
          //Highpass output
          outputAudio = new DDSAudioInputStream(new BufferedDoubleDataSource(channel.hpfOut), inputAudio.getFormat());
          outFileName = args[0].substring(0, args[0].length() - 4) + "_hi.wav";
          AudioSystem.write(outputAudio, AudioFileFormat.Type.WAVE, new File(outFileName));
          //
      }
  
      public double[] applyToOutputHighComponent(double[] x) {
          apply(x);
  
          return hpfOut;
      }
  
      public double[] applyToOutputLowComponent(double[] x) {
          apply(x);
  
          return lpfOut;
      }
  
      public double[] applyToOutputLowInterpolatedComponent(double[] x) {
          apply(x);
  
          return lpfOutInterpolated;
      }
  
      public void apply(double[] x) {
          int i;
          double[] lpfOutTmp;
  
          //lpfOutTmp = SignalProcUtils.filter(Hd.getDenumeratorCoefficients(), filterNumerator, x);
          lpfOutTmp = Hd.apply(x);
  
          lpfOutTmp = SignalProcUtils.decimate(lpfOutTmp, 2.0);
          //lpfOutTmp = SignalProcUtils.filter(Hb.getDenumeratorCoefficients(), filterNumerator, lpfOutTmp);
          lpfOutTmp = Hb.apply(lpfOutTmp);
  
          //Interpolation of lowpass channel
          lpfOutInterpolated = SignalProcUtils.interpolate(lpfOutTmp, 2.0);
          //lpfOutInterpolated = SignalProcUtils.filter(Hi.getDenumeratorCoefficients(), filterNumerator, lpfOutInterpolated);
          lpfOutInterpolated = Hi.apply(lpfOutInterpolated);
          //
  
          //Energy compensation
          double enx = SignalProcUtils.energy(x);
          double enxloi = SignalProcUtils.energy(lpfOutInterpolated);
          double gxloi = Math.sqrt(enx / enxloi);
  
          for (i = 0; i < lpfOutInterpolated.length; i++)
              lpfOutInterpolated[i] *= gxloi;
          //
  
          int delay = (int) Math.floor(1.5 * filterLengthMinusOne + 0.5) - 1;
  
          hpfOut = new double[x.length];
          for (i = 0; i < x.length - delay; i++)
              hpfOut[i] = x[i] - lpfOutInterpolated[i + delay];
          for (i = x.length - delay; i < x.length; i++)
              hpfOut[i] = 0.0;
  
          delay = (int) Math.floor(0.5 * filterLengthMinusOne + 0.5);
          lpfOut = new double[lpfOutTmp.length - delay];
          for (i = delay; i < lpfOutTmp.length; i++)
              lpfOut[i - delay] = lpfOutTmp[i];
  
          lpfOutEnergy = SignalProcUtils.energy(lpfOut);
      }
  }
  
  