/* * ================================================= * Copyright 2011 tagtraum industries incorporated * All rights reserved. * ================================================= */ package com.tagtraum.jipes.audio; import com.tagtraum.jipes.AbstractSignalProcessor; import javax.sound.sampled.AudioFormat; import java.util.Arrays; /** * Adds zero samples (zero stuffing) in order to upsample the data. Note that proper upsampling (i.e. interpolating) * has to be followed by the application of a suitable low pass filter (see {@link com.tagtraum.jipes.math.Filters} * and {@link com.tagtraum.jipes.universal.Mapping}) to avoid aliasing. * * @author <a href="mailto:[email protected]">Hendrik Schreiber</a> */ public class Upsample extends AbstractSignalProcessor<AudioBuffer, AudioBuffer> { private int factor = 2; private float[] output; private RealAudioBuffer realAudioBuffer; /** * Creates an upsample processor with the given upsampling factor. * * @param factor number of added zeros plus one (upsampling factor) */ public Upsample(final int factor) { this.factor = factor; } /** * Creates an upsample processor that adds a zero for each sample. */ public Upsample() { this(2); } /** * @see #setFactor(int) * @return upsampling factor */ public int getFactor() { return factor; } /** * Add zeroes for every sample so that the sample rate is <code>x</code>-times higher * than before. * * So if you specify 2, 1 zero will be added for every sample, effectively doubling the sample rate. * If you specify 3, the number of samples (and thus also the samplerate) triples - for each * sample two zeros are added. * * @param factor upsampling factor */ public void setFactor(final int factor) { if (factor < 1) throw new IllegalArgumentException("Upsampling factor must be greater than 0: " + factor); this.factor = factor; } private AudioFormat getProcessedAudioFormat(final AudioBuffer buffer) { final AudioFormat sourceAudioFormat = buffer.getAudioFormat(); return new AudioFormat(sourceAudioFormat.getSampleRate() * factor, sourceAudioFormat.getSampleSizeInBits(), sourceAudioFormat.getChannels(), AudioFormat.Encoding.PCM_SIGNED.equals(sourceAudioFormat.getEncoding()), sourceAudioFormat.isBigEndian()); } @Override protected AudioBuffer processNext(final AudioBuffer buffer) { final float[] floats = buffer.getData(); final int outputLength = floats.length * factor; if (output == null || outputLength != output.length) { output = new float[outputLength]; } final int channels = buffer.getAudioFormat().getChannels(); Arrays.fill(output, 0f); for (int i=0; i<floats.length; i+=channels) { try { System.arraycopy(floats, i, output, i*factor, channels); } catch (Exception e) { e.printStackTrace(); } } if (realAudioBuffer == null) { realAudioBuffer = new RealAudioBuffer(buffer.getFrameNumber() * factor, output, getProcessedAudioFormat(buffer)); } else { realAudioBuffer.reuse(buffer.getFrameNumber() * factor, output, realAudioBuffer.getAudioFormat()); } return realAudioBuffer; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Upsample that = (Upsample) o; if (factor != that.factor) return false; return true; } @Override public int hashCode() { return factor; } @Override public String toString() { return "Upsample{" + "factor=" + factor + '}'; } }