package org.jcodec.audio; import org.jcodec.audio.Audio.DummyFilter; import java.lang.IllegalArgumentException; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Audio filter graph * * Represents a combination of filters as on 'uber' filter * * @author The JCodec project * */ public class FilterGraph implements AudioFilter { public static Factory addLevel(AudioFilter first) { return new Factory(first); } public static class Factory { private List<FilterSocket> sockets; protected Factory(AudioFilter firstFilter) { this.sockets = new ArrayList<FilterSocket>(); if (firstFilter.getDelay() != 0) { // Removing first filter delay using filter socket zero stuffing // features sockets.add(FilterSocket.createFilterSocket(new DummyFilter(firstFilter.getNInputs()))); addLevel(firstFilter); } else sockets.add(FilterSocket.createFilterSocket(firstFilter)); } //@formatter:off /** * Adds filters to the next level in the graph * * The filters are added to from the left to the right, i.e. * <pre> * L0 * L1 L1 L1 L1 L1 L1 * L2 L2 --> * </pre> * As a consequence if the filters in this level contain less inputs then * there are outputs in a previous level the graph will throw exception * because the configuration it's misconfigured. * * @param filters * @return */ //@formatter:on public Factory addLevel(AudioFilter... arguments) { FilterSocket socket = FilterSocket.createFilterSocket(arguments); socket.allocateBuffers(4096); sockets.add(socket); return this; } /** * Adds n of this filter as the next level in a graph * * @param filter * @param n * @return */ public Factory addLevels(AudioFilter filter, int n) { AudioFilter[] filters = new AudioFilter[n]; Arrays.fill(filters, filter); return addLevel(filters); } /** * Adds a level to the graph and tries to fill it with as many of the * current filter as will be needed to take all the outputs of the * previous level * * @param filter * @return */ public Factory addLevelSpan(AudioFilter filter) { int prevLevelOuts = sockets.get(sockets.size() - 1).getTotalOutputs(); if ((prevLevelOuts % filter.getNInputs()) != 0) throw new IllegalArgumentException("Can't fill " + prevLevelOuts + " with multiple of " + filter.getNInputs()); return addLevels(filter, prevLevelOuts / filter.getNInputs()); } public FilterGraph create() { return new FilterGraph(sockets.toArray(new FilterSocket[0])); } } private FilterSocket[] sockets; private FilterGraph(FilterSocket[] sockets) { this.sockets = sockets; } @Override public void filter(FloatBuffer[] ins, long[] pos, FloatBuffer[] outs) { sockets[0].setBuffers(ins, pos); for (int i = 0; i < sockets.length; i++) { FloatBuffer[] curOut = i < sockets.length - 1 ? sockets[i + 1].getBuffers() : outs; sockets[i].filter(curOut); if (i > 0) { sockets[i].rotate(); } if (i < sockets.length - 1) { for (FloatBuffer b : curOut) b.flip(); } } } @Override public int getDelay() { return sockets[0].getFilters()[0].getDelay(); } @Override public int getNInputs() { return sockets[0].getTotalInputs(); } @Override public int getNOutputs() { return sockets[sockets.length - 1].getTotalOutputs(); } }