⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fdmffingerprint.java

📁 dump3 morpheus 0.2.9 src
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
   * 
   * @param fftOutputArray Complex array output by FFT
   * @return array of band energies
   */
  private double[] calculateBandEnergies(final Complex[] fftOutputArray) {
    final double[] be = new double[NUM_BANDS];
    int b = 0;
    for (b = 0; b < NUM_BANDS; b++) {
      int i = 0;
      be[b] = 0.0;
      for (i = frequency[b]; i < frequency[b + 1]; i++) {
        be[b] += fftOutputArray[i].scale();
      }
      be[b] /= CHUNK_SAMPLES;
      be[b] = Math.sqrt(be[b]);
    }
    return be;
  }

  /**
   * This routine calculates some metrics on the band energies of a chunk. The metrics are written to arrays, with one array for each metric. The chunk argument
   * is the position in the output arrays where the metrics should be stored.
   * 
   * @param be band energies
   */
  void chunkMetrics(final double[] be) {
    final double energy;
    double ratio = 0.0;
    double twist = 0.0;
    double lows = 0.0;
    final double highs;
    final double evens;
    double odds = 0.0;
    lows = be[0] + be[1];
    highs = be[2] + be[3];
    evens = be[0] + be[2];
    odds = be[1] + be[3];
    /* trap zeros */
    lows = Math.abs(lows) < 0.001 ? 0.001 : lows;
    odds = Math.abs(odds) < 0.001 ? 0.001 : odds;
    energy = lows + highs;
    ratio = highs / lows;
    twist = evens / odds;
    ratio = Math.abs(ratio) > 20.0 ? 20 : ratio;
    twist = Math.abs(twist) > 20.0 ? 20 : twist;
    energyBuffer[chunkPos] = new Complex(energy, 0.0);
    ratioBuffer[chunkPos] = new Complex(ratio, 0.0);
    twistBuffer[chunkPos] = new Complex(twist, 0.0);
  }

  /**
   * This routine takes raw audio data and puts it into the data structure that FFT operates on.
   * 
   * @param buffer a byte array
   * @param in a Complex array that id passed to the FFT
   * @param offs offset in the byte array. i.e. starting position
   * @param length length of the byte array
   * @param pos offset in the Complex array
   * @return a Point with the positions of the fft Array and the byte array
   */
  private Point convertAudioToComplex(final byte[] buffer, final Complex[] in, final int offs, final int length, final int pos) {
    int j = offs;
    int i = pos;
    for (; i < CHUNK_SAMPLES && j < length; i++) {
      int l = 0;
      int r = 0;
      l = buffer[j++] & 0xff;
      l |= buffer[j++] << 8 & 0xff00;
      r = buffer[j++] & 0xff;
      r |= buffer[j++] << 8 & 0xff00;
      in[i] = new Complex(l + r, 0.0);
    }
    return new Point(i, j);
  }

  /**
   * @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#getClassName()
   */
  @Override
  public String getClassName() {
    final StringBuffer sb = new StringBuffer();
    sb.append(super.getClassName()).append("-");
    switch (config.getWindowType()) {
      default:
      case WINDOW_RECTANGULAR:
        sb.append("RECT");
        break;
      case WINDOW_HAMMING:
        sb.append("HAMM");
        break;
      case WINDOW_HANNING:
        sb.append("HANN");
        break;
      case WINDOW_RAISED_COSINE:
        sb.append("RACO-").append(config.getAlpha());
        break;
      case WINDOW_BARTLETT:
        sb.append("BART");
        break;
      case WINDOW_WELCH:
        sb.append("WELC");
        break;
      case WINDOW_KAISER:
        sb.append("KAIS-").append(config.getAlpha());
        break;
    }
    return sb.toString();
  }

  /**
   * @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#getSimilarityThreshhold()
   */
  @Override
  public float getSimilarityThreshhold() {
    return config.getSimilarityThreshhold();
  }

  /**
   * @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#makeFingerprintFromFile()
   */
  @Override
  protected void makeFingerprintFromFile() {
    final File f = new File(getFile().getPath());
    AudioFormat audioFormat = new AudioFormat(44100.0f, 16, 2, true, false);
    try {
      AudioInputStream convertedAIS = null;
      final AudioInputStream inFileAIS = AudioSystem.getAudioInputStream(f);
      if (audioFormat.equals(inFileAIS.getFormat())) {
        convertedAIS = inFileAIS;
      } else if (inFileAIS.getFormat().getEncoding() == AudioFormat.Encoding.PCM_SIGNED
          || inFileAIS.getFormat().getEncoding() == AudioFormat.Encoding.PCM_UNSIGNED) {
        // hack for WAV files
        audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100.0f, 16, 2, inFileAIS.getFormat().getFrameSize(), 44100.0f, false);
        convertedAIS = AudioSystem.getAudioInputStream(audioFormat, inFileAIS);
      } else if (AudioSystem.isConversionSupported(audioFormat, inFileAIS.getFormat())) {
        convertedAIS = AudioSystem.getAudioInputStream(audioFormat, inFileAIS);
      } else {
        throw new Exception("Could not convert from [" + inFileAIS.getFormat().toString() + "] to [" + audioFormat.toString() + "]");
      }
      final DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
      if (!AudioSystem.isLineSupported(info)) {
        throw new Exception("audio type not supported.");
      }
      final SourceDataLine dataLine = (SourceDataLine)AudioSystem.getLine(info);
      dataLine.open(audioFormat);
      dataLine.start();
      preRead();
      final int bufferSize = (int)audioFormat.getSampleRate() * audioFormat.getFrameSize();
      byte[] buffer = new byte[bufferSize];
      int bytesRead = 0;
      while ((bytesRead = convertedAIS.read(buffer, 0, bufferSize)) >= 0 && chunkPos < MAX_CHUNKS) {
        update(buffer, bytesRead);
      }
      // release memory as soon as possible
      buffer = null;
      dataLine.drain();
      dataLine.close();
      postRead();
    } catch (final Exception e) {
      getFile().setStatus(Status.FILE_CORRUPT);
      fingerprint = null;
      log.warn("error while reading file [" + getFile().getPath() + "]", e);
    }
  }

  /**
   * @param fi AbstractFingerprint
   * @return boolean
   * @modelguid {3E6D5504-3A75-4444-BE5B-C23BB0DF4DE8}
   */
  @Override
  public float matches(final AbstractFingerprint fi) {
    final FingerprintFile ff1 = getFile();
    final FingerprintFile ff2 = fi.getFile();
    if (ff1.getStatus() != Status.FILE_OK && ff1.getStatus() != Status.FILE_SIGNATURE_MISMATCH || ff2.getStatus() != Status.FILE_OK
        && ff2.getStatus() != Status.FILE_SIGNATURE_MISMATCH) {
      return 0.0f;
    }
    if (ff1.getLength() == 0L && ff2.getLength() == 0L) {
      return 100.0f;
    }
    if (fi instanceof FdmfFingerprint) {
      final FdmfFingerprint pf = (FdmfFingerprint)fi;
      // try to read the files
      final byte[] a = getFingerprint();
      final byte[] b = pf.getFingerprint();
      if ((ff1.getStatus() == Status.FILE_OK || ff1.getStatus() == Status.FILE_SIGNATURE_MISMATCH)
          && (ff2.getStatus() == Status.FILE_OK || ff2.getStatus() == Status.FILE_SIGNATURE_MISMATCH)) {
        if (a == null) {
          log.error("File [" + ff1.toString() + "] read Ok, but fingerprint is null!");
          ff1.setStatus(Status.FILE_CORRUPT);
          return 0.0f;
        }
        if (b == null) {
          log.error("File [" + ff2.toString() + "] read Ok, but fingerprint is null!");
          ff2.setStatus(Status.FILE_CORRUPT);
          return 0.0f;
        }
        final float c = (float)compare(a, b);
        return c;
      }
    }
    return 0.0f;
  }

  /**
   * @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#postRead()
   */
  @Override
  protected void postRead() {
    // calc fingerprint
    fftInputArray = null;
    if (chunkPos > 0) {
      // cut the buffers to a power of 2
      final int len = (int)Math.pow(2.0, ((int)(Math.log(chunkPos) / Math.log(2.0))));
      Complex[] eb = new Complex[len];
      Complex[] rb = new Complex[len];
      Complex[] tb = new Complex[len];
      System.arraycopy(energyBuffer, 0, eb, 0, len);
      System.arraycopy(ratioBuffer, 0, rb, 0, len);
      System.arraycopy(twistBuffer, 0, tb, 0, len);
      // release memory
      energyBuffer = null;
      ratioBuffer = null;
      twistBuffer = null;
      final double[] wt = config.makeWindow(len);
      // do fft
      eb = FFT.fft2(FFT.window(eb, wt));
      rb = FFT.fft2(FFT.window(rb, wt));
      tb = FFT.fft2(FFT.window(tb, wt));
      fingerprint = new byte[(eb.length + rb.length + tb.length >> 3)];
      final byte[] l1 = quantize(eb);
      System.arraycopy(l1, 0, fingerprint, 0, l1.length);
      final byte[] l2 = quantize(rb);
      System.arraycopy(l2, 0, fingerprint, l1.length, l2.length);
      final byte[] l3 = quantize(tb);
      System.arraycopy(l3, 0, fingerprint, l1.length + l2.length, l3.length);
    }
  }

  /**
   * @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#preRead()
   */
  @Override
  protected void preRead() {
    started = true;
    fftPos = 0;
    fftInputArray = new Complex[CHUNK_SAMPLES];
    chunkPos = 0;
    energyBuffer = new Complex[MAX_CHUNKS];
    ratioBuffer = new Complex[MAX_CHUNKS];
    twistBuffer = new Complex[MAX_CHUNKS];
  }

  /**
   * @see net.za.grasser.duplicate.fingerprint.AbstractFingerprint#update(byte[], int)
   */
  @Override
  protected void update(final byte[] pBuffer, final int pLength) {
    int baPos = 0;
    // find start of sound (ignore silence)
    if (started) {
      int i = 0;
      for (; i < pLength; i++) {
        if (pBuffer[i] != 0) {
          baPos = i;
          break;
        }
      }
      if (i == pLength) {
        return;
      }
    }
    started = false;
    Point ret = convertAudioToComplex(pBuffer, fftInputArray, baPos, pLength, fftPos);
    while (baPos < pLength && chunkPos < MAX_CHUNKS) {
      fftPos = ret.x;
      baPos = ret.y;
      if (fftPos == CHUNK_SAMPLES) {
        chunkMetrics(calculateBandEnergies(FFT.fft2(FFT.window(fftInputArray, config.getWindow()))));
        chunkPos++;
        // don't forget the last couple of bytes
        if (baPos < pLength) {
          ret = convertAudioToComplex(pBuffer, fftInputArray, baPos, pLength, 0);
        }
      }
    }
  }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -