From: David ‘Bombe’ Roden Date: Tue, 4 Jun 2013 18:36:51 +0000 (+0200) Subject: Pull all interfaces into a single interface: Filter. X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=633a841142f978235ed9f745b6ba16c278963e62;p=sonitus.git Pull all interfaces into a single interface: Filter. --- diff --git a/src/main/java/net/pterodactylus/sonitus/data/AbstractControlledComponent.java b/src/main/java/net/pterodactylus/sonitus/data/AbstractControlledComponent.java deleted file mode 100644 index fe338c1..0000000 --- a/src/main/java/net/pterodactylus/sonitus/data/AbstractControlledComponent.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Sonitus - AbstractControlledComponent.java - Copyright © 2013 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sonitus.data; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import com.google.common.collect.Lists; - -/** - * Abstract {@link ControlledComponent} implementation that takes care of - * managing {@link MetadataListener}s. - * - * @author David ‘Bombe’ Roden - */ -public abstract class AbstractControlledComponent implements ControlledComponent { - - /** The name of this filter. */ - private final String name; - - /** The list of metadata listeners. */ - private final List metadataListeners = Lists.newCopyOnWriteArrayList(); - - /** The current metadata. */ - private final AtomicReference metadata = new AtomicReference(); - - /** - * Creates a new abstract controlled component. - * - * @param name - * The name of the component - */ - protected AbstractControlledComponent(String name) { - this.name = name; - } - - // - // LISTENER MANAGEMENT - // - - @Override - public void addMetadataListener(MetadataListener metadataListener) { - metadataListeners.add(metadataListener); - } - - @Override - public void removeMetadataListener(MetadataListener metadataListener) { - metadataListeners.remove(metadataListener); - } - - // - // CONTROLLEDCOMPONENT METHODS - // - - @Override - public String name() { - return name; - } - - @Override - public Metadata metadata() { - return metadata.get(); - } - - @Override - public void metadataUpdated(Metadata metadata) { - if (metadata.equals(this.metadata.get())) { - return; - } - this.metadata.set(metadata); - fireMetadataUpdated(metadata); - } - - // - // EVENT METHODS - // - - /** - * Notifies all registered metadata listeners that the metadata has changed. - * - * @param metadata - * The new metadata - */ - protected void fireMetadataUpdated(Metadata metadata) { - for (MetadataListener metadataListener : metadataListeners) { - metadataListener.metadataUpdated(this, metadata); - } - } - -} diff --git a/src/main/java/net/pterodactylus/sonitus/data/AbstractFilter.java b/src/main/java/net/pterodactylus/sonitus/data/AbstractFilter.java new file mode 100644 index 0000000..6fa6511 --- /dev/null +++ b/src/main/java/net/pterodactylus/sonitus/data/AbstractFilter.java @@ -0,0 +1,188 @@ +/* + * Sonitus - AbstractFilter.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sonitus.data; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import com.google.common.collect.Lists; +import com.google.common.io.Closeables; + +/** + * Abstract {@link Filter} implementation that takes care of managing {@link + * MetadataListener}s and pipes its input to its output. + * + * @author David ‘Bombe’ Roden + */ +public abstract class AbstractFilter implements Filter { + + /** The name of this filter. */ + private final String name; + + /** The list of metadata listeners. */ + private final List metadataListeners = Lists.newCopyOnWriteArrayList(); + + /** The current metadata. */ + private final AtomicReference metadata = new AtomicReference(); + + /** The input stream from which to read. */ + private InputStream inputStream; + + /** The output stream to which to write. */ + private OutputStream outputStream; + + /** + * Creates a new abstract filter. + * + * @param name + * The name of the filter + */ + protected AbstractFilter(String name) { + this.name = name; + } + + // + // LISTENER MANAGEMENT + // + + @Override + public void addMetadataListener(MetadataListener metadataListener) { + metadataListeners.add(metadataListener); + } + + @Override + public void removeMetadataListener(MetadataListener metadataListener) { + metadataListeners.remove(metadataListener); + } + + // + // FILTER METHODS + // + + @Override + public String name() { + return name; + } + + @Override + public List> controllers() { + return Collections.emptyList(); + } + + @Override + public Metadata metadata() { + return metadata.get(); + } + + @Override + public void metadataUpdated(Metadata metadata) { + if (metadata.equals(this.metadata.get())) { + return; + } + this.metadata.set(metadata); + fireMetadataUpdated(metadata); + } + + @Override + public void open(Metadata metadata) throws IOException { + metadataUpdated(metadata); + inputStream = createInputStream(); + outputStream = createOutputStream(); + } + + @Override + public void close() { + try { + Closeables.close(outputStream, true); + Closeables.close(inputStream, true); + } catch (IOException e) { + /* won’t throw. */ + } + } + + @Override + public void process(byte[] buffer) throws IOException { + outputStream.write(buffer); + outputStream.flush(); + } + + @Override + public byte[] get(int bufferSize) throws IOException { + byte[] buffer = new byte[bufferSize]; + int read = inputStream.read(buffer); + if (read == -1) { + throw new EOFException(); + } + return Arrays.copyOf(buffer, read); + } + + // + // EVENT METHODS + // + + /** + * Notifies all registered metadata listeners that the metadata has changed. + * + * @param metadata + * The new metadata + */ + protected void fireMetadataUpdated(Metadata metadata) { + for (MetadataListener metadataListener : metadataListeners) { + metadataListener.metadataUpdated(this, metadata); + } + } + + // + // SUBCLASS METHODS + // + + /** + * Creates the input stream from which {@link net.pterodactylus.sonitus.data.Pipeline} + * will read the audio data. If you override this, you have to override {@link + * #createOutputStream()}, too! + * + * @return The input stream to read from + * @throws IOException + * if an I/O error occurs + */ + protected InputStream createInputStream() throws IOException { + return new PipedInputStream(); + } + + /** + * Creates the output stream to which {@link net.pterodactylus.sonitus.data.Pipeline} + * will write the audio data. If you override this, you have to override {@link + * #createInputStream()}, too! + * + * @return The output stream to write to + * @throws IOException + * if an I/O error occurs + */ + protected OutputStream createOutputStream() throws IOException { + return new PipedOutputStream((PipedInputStream) inputStream); + } + +} diff --git a/src/main/java/net/pterodactylus/sonitus/data/ControlledComponent.java b/src/main/java/net/pterodactylus/sonitus/data/ControlledComponent.java deleted file mode 100644 index d3fde8e..0000000 --- a/src/main/java/net/pterodactylus/sonitus/data/ControlledComponent.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Sonitus - Controlled.java - Copyright © 2013 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sonitus.data; - -import java.util.List; - -/** - * Interface for components that can be controlled externally in some way. - * - * @author David ‘Bombe’ Roden - */ -public interface ControlledComponent { - - /** - * Adds the given listener to the list of registered listeners. - * - * @param metadataListener - * The metadata listener to add - */ - void addMetadataListener(MetadataListener metadataListener); - - /** - * Removes the given listener from the list of registered listeners. - * - * @param metadataListener - * The metadata listener to remove - */ - void removeMetadataListener(MetadataListener metadataListener); - - /** - * Returns the name of this controlled component. - * - * @return The name of this controlled component - */ - public String name(); - - /** - * Returns the current metadata of this component. - * - * @return The current metadata of this component - */ - public Metadata metadata(); - - /** - * Returns the controllers offered by this component. - * - * @return The controllers of this component - */ - public List> controllers(); - - /** - * Notifies the sink that the metadata of the audio stream has changed. This - * method should return as fast as possible, i.e. every heavy lifting should be - * done from another thread. - * - * @param metadata - * The new metadata - */ - void metadataUpdated(Metadata metadata); - -} diff --git a/src/main/java/net/pterodactylus/sonitus/data/Filter.java b/src/main/java/net/pterodactylus/sonitus/data/Filter.java index c251cfe..ec8fb94 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/Filter.java +++ b/src/main/java/net/pterodactylus/sonitus/data/Filter.java @@ -17,12 +17,98 @@ package net.pterodactylus.sonitus.data; +import java.io.IOException; +import java.util.List; + /** - * A filter is both a {@link Source} and a {@link Sink}. It is used to process + * A filter is both a source and a sink for audio data. It is used to process * the audio date in whatever way seems appropriate. * * @author David ‘Bombe’ Roden */ -public interface Filter extends ControlledComponent, Source, Sink { +public interface Filter { + + /** + * Adds the given listener to the list of registered listeners. + * + * @param metadataListener + * The metadata listener to add + */ + void addMetadataListener(MetadataListener metadataListener); + + /** + * Removes the given listener from the list of registered listeners. + * + * @param metadataListener + * The metadata listener to remove + */ + void removeMetadataListener(MetadataListener metadataListener); + + /** + * Returns the name of this filter. + * + * @return The name of this filter + */ + String name(); + + /** + * Returns the controllers offered by this filter. + * + * @return The controllers of this filter + */ + List> controllers(); + + /** + * Returns the metadata of the audio stream. + * + * @return The metadata of the audio stream + */ + Metadata metadata(); + + /** + * Notifies the sink that the metadata of the audio stream has changed. This + * method should return as fast as possible, i.e. every heavy lifting should be + * done from another thread. + * + * @param metadata + * The new metadata + */ + void metadataUpdated(Metadata metadata); + + /** + * Retrieves data from the audio stream. + * + * @param bufferSize + * The maximum amount of bytes to retrieve from the audio stream + * @return A buffer filled with up to {@code bufferSize} bytes of data; the + * returned buffer may contain less data than requested but will not + * contain excess elements + * @throws IOException + * if an I/O error occurs + */ + byte[] get(int bufferSize) throws IOException; + + /** + * Opens this sink using the format parameters of the given metadata. + * + * @param metadata + * The metadata of the stream + * @throws IOException + * if an I/O error occurs + */ + void open(Metadata metadata) throws IOException; + + /** Closes this sink. */ + void close(); + + /** + * Processes the given buffer of data. + * + * @param buffer + * The data to process + * @throws IOException + * if an I/O error occurs + */ + void process(byte[] buffer) throws IOException; } diff --git a/src/main/java/net/pterodactylus/sonitus/data/MetadataListener.java b/src/main/java/net/pterodactylus/sonitus/data/MetadataListener.java index 01efe78..6b88ddf 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/MetadataListener.java +++ b/src/main/java/net/pterodactylus/sonitus/data/MetadataListener.java @@ -25,13 +25,13 @@ package net.pterodactylus.sonitus.data; public interface MetadataListener { /** - * Notifies a listener when the metadata of the given component was updated. + * Notifies a listener when the metadata of the given filter was updated. * - * @param component - * The component whose metadata was updated + * @param filter + * The filter whose metadata was updated * @param metadata * The new metadata */ - void metadataUpdated(ControlledComponent component, Metadata metadata); + void metadataUpdated(Filter filter, Metadata metadata); } diff --git a/src/main/java/net/pterodactylus/sonitus/data/Pipeline.java b/src/main/java/net/pterodactylus/sonitus/data/Pipeline.java index ab6500e..5bbcd63 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/Pipeline.java +++ b/src/main/java/net/pterodactylus/sonitus/data/Pipeline.java @@ -36,26 +36,27 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.util.concurrent.MoreExecutors; /** - * A pipeline is responsible for streaming audio data from a {@link Source} to - * an arbitrary number of connected {@link Filter}s and {@link Sink}s. + * A pipeline is responsible for streaming audio data from a {@link Filter} to + * an arbitrary number of connected {@link Filter}s. * * @author David ‘Bombe’ Roden */ -public class Pipeline implements Iterable { +public class Pipeline implements Iterable { /** The logger. */ private static final Logger logger = Logger.getLogger(Pipeline.class.getName()); /** The source of the audio stream. */ - private final Source source; + private final Filter source; - /** The sinks for each source. */ - private final Multimap sinks; + /** The filters for each source. */ + private final ListMultimap filters; /** All started connections. */ private final List connections = Lists.newArrayList(); @@ -65,23 +66,21 @@ public class Pipeline implements Iterable { * * @param source * The source of the audio stream - * @param sinks - * The sinks for each source + * @param filters + * The filters for each source */ - private Pipeline(Source source, Multimap sinks) { + private Pipeline(Filter source, Multimap filters) { this.source = Preconditions.checkNotNull(source, "source must not be null"); - this.sinks = Preconditions.checkNotNull(sinks, "sinks must not be null"); - for (ControlledComponent component : Lists.reverse(components())) { - logger.finest(String.format("Adding Listener to %s.", component.name())); - component.addMetadataListener(new MetadataListener() { + this.filters = ArrayListMultimap.create(Preconditions.checkNotNull(filters, "filters must not be null")); + for (Filter filter : Lists.reverse(filters())) { + logger.finest(String.format("Adding Listener to %s.", filter.name())); + filter.addMetadataListener(new MetadataListener() { + @Override - public void metadataUpdated(ControlledComponent component, Metadata metadata) { - if (!(component instanceof Source)) { - return; - } - for (ControlledComponent controlledComponent : sinks((Source) component)) { - logger.fine(String.format("Updating Metadata from %s to %s as %s.", component.name(), controlledComponent.name(), metadata)); - controlledComponent.metadataUpdated(metadata); + public void metadataUpdated(Filter filter, Metadata metadata) { + for (Filter sinks : filters(filter)) { + logger.fine(String.format("Updating Metadata from %s to %s as %s.", filter.name(), sinks.name(), metadata)); + sinks.metadataUpdated(metadata); } } }); @@ -97,38 +96,37 @@ public class Pipeline implements Iterable { * * @return This pipeline’s source */ - public Source source() { + public Filter source() { return source; } /** - * Returns all {@link Sink}s (or {@link Filter}s, really) that are connected to - * the given source. + * Returns all {@link Filter}s that are connected to the given filter. * - * @param source - * The source to get the sinks for - * @return The sinks connected to the given source, or an empty list if the - * source does not exist in this pipeline + * @param filter + * The filter to get the connected filters for + * @return The filters connected to the given filter, or an empty list if the + * filter does not exist in this pipeline, or is not connected to any filters */ - public Collection sinks(Source source) { - return sinks.get(source); + public List filters(Filter filter) { + return filters.get(filter); } /** - * Returns the traffic counters of the given controlled component. + * Returns the traffic counters of the given filter. * - * @param controlledComponent - * The controlled component to get the traffic counters for - * @return The traffic counters for the given controlled component + * @param filter + * The filter to get the traffic counters for + * @return The traffic counters for the given filter */ - public TrafficCounter trafficCounter(ControlledComponent controlledComponent) { + public TrafficCounter trafficCounter(Filter filter) { long input = -1; long output = -1; for (Connection connection : connections) { /* the connection where the source matches knows the output. */ - if (connection.source.equals(controlledComponent)) { + if (connection.source.equals(filter)) { output = connection.counter(); - } else if (connection.sinks.contains(controlledComponent)) { + } else if (connection.sinks.contains(filter)) { input = connection.counter(); } } @@ -143,7 +141,7 @@ public class Pipeline implements Iterable { * Starts the pipeline. * * @throws IOException - * if any of the sinks can not be opened + * if any of the filters can not be opened * @throws IllegalStateException * if the pipeline is already running */ @@ -151,26 +149,24 @@ public class Pipeline implements Iterable { if (!connections.isEmpty()) { throw new IllegalStateException("Pipeline is already running!"); } - List sources = Lists.newArrayList(); - sources.add(source); + List filters = Lists.newArrayList(); + filters.add(source); /* collect all source->sink pairs. */ - while (!sources.isEmpty()) { - Source source = sources.remove(0); - Collection sinks = this.sinks.get(source); - connections.add(new Connection(source, sinks)); - for (Sink sink : sinks) { + while (!filters.isEmpty()) { + Filter filter = filters.remove(0); + Collection sinks = this.filters.get(filter); + connections.add(new Connection(filter, sinks)); + for (Filter sink : sinks) { logger.info(String.format("Opening %s with %s...", sink.name(), source.metadata())); - sink.open(source.metadata()); - if (sink instanceof Filter) { - sources.add((Source) sink); - } + sink.open(filter.metadata()); + filters.add(sink); } } for (Connection connection : connections) { - String threadName = String.format("%s → %s.", connection.source.name(), FluentIterable.from(connection.sinks).transform(new Function() { + String threadName = String.format("%s → %s.", connection.source.name(), FluentIterable.from(connection.sinks).transform(new Function() { @Override - public String apply(Sink sink) { + public String apply(Filter sink) { return sink.name(); } })); @@ -194,8 +190,8 @@ public class Pipeline implements Iterable { // @Override - public Iterator iterator() { - return components().iterator(); + public Iterator iterator() { + return filters().iterator(); } // @@ -203,26 +199,24 @@ public class Pipeline implements Iterable { // /** - * Returns all components of this pipeline, listed breadth-first, starting with + * Returns all filters of this pipeline, listed breadth-first, starting with * the source. * - * @return All components of this pipeline + * @return All filters of this pipeline */ - public List components() { - ImmutableList.Builder components = ImmutableList.builder(); - List currentComponents = Lists.newArrayList(); - components.add(source); - currentComponents.add(source); - while (!currentComponents.isEmpty()) { - Collection sinks = this.sinks((Source) currentComponents.remove(0)); - for (Sink sink : sinks) { - components.add(sink); - if (sink instanceof Source) { - currentComponents.add(sink); - } + public List filters() { + ImmutableList.Builder filters = ImmutableList.builder(); + List remainingFilters = Lists.newArrayList(); + filters.add(source); + remainingFilters.add(source); + while (!remainingFilters.isEmpty()) { + Collection sinks = this.filters(remainingFilters.remove(0)); + for (Filter sink : sinks) { + filters.add(sink); + remainingFilters.add(sink); } } - return components.build(); + return filters.build(); } // @@ -236,7 +230,7 @@ public class Pipeline implements Iterable { * The source at which to start * @return A builder for a new pipeline */ - public static Builder builder(Source source) { + public static Builder builder(Filter source) { return new Builder(source); } @@ -248,13 +242,13 @@ public class Pipeline implements Iterable { public static class Builder { /** The source of the pipeline. */ - private final Source source; + private final Filter source; - /** The sinks to which each source streams. */ - private Multimap nextSinks = ArrayListMultimap.create(); + /** The filters to which each source streams. */ + private Multimap nextSinks = ArrayListMultimap.create(); /** The last added source. */ - private Source lastSource; + private Filter lastSource; /** * Creates a new builder. @@ -262,31 +256,27 @@ public class Pipeline implements Iterable { * @param source * The source that starts the pipeline */ - private Builder(Source source) { + private Builder(Filter source) { this.source = source; lastSource = source; } /** - * Adds a {@link Sink} (or {@link Filter} as a recipient for the last added - * {@link Source}. + * Adds a {@link Filter} as a recipient for the last added source. * * @param sink * The sink to add * @return This builder - * @throws IllegalStateException - * if the last added {@link Sink} was not also a {@link Source} */ - public Builder to(Sink sink) { - Preconditions.checkState(lastSource != null, "last added Sink was not a Source"); + public Builder to(Filter sink) { nextSinks.put(lastSource, sink); - lastSource = (sink instanceof Filter) ? (Source) sink : null; + lastSource = sink; return this; } /** * Locates the given source and sets it as the last added node so that the - * next invocation of {@link #to(Sink)} can “fork” the pipeline. + * next invocation of {@link #to(Filter)} can “fork” the pipeline. * * @param source * The source to locate @@ -294,7 +284,7 @@ public class Pipeline implements Iterable { * @throws IllegalStateException * if the given source was not previously added as a sink */ - public Builder find(Source source) { + public Builder find(Filter source) { Preconditions.checkState(nextSinks.containsValue(source)); lastSource = source; return this; @@ -312,8 +302,8 @@ public class Pipeline implements Iterable { } /** - * A connection is responsible for streaming audio from one {@link Source} to - * an arbitrary number of {@link Sink}s it is connected to. A connection is + * A connection is responsible for streaming audio from one {@link Filter} to + * an arbitrary number of {@link Filter}s it is connected to. A connection is * started by creating a {@link Thread} wrapping it and starting said thread. * * @author David ‘Bombe’ Roden @@ -321,10 +311,10 @@ public class Pipeline implements Iterable { public class Connection implements Runnable { /** The source. */ - private final Source source; + private final Filter source; - /** The sinks. */ - private final Collection sinks; + /** The filters. */ + private final Collection sinks; /** Whether the feeder was stopped. */ private final AtomicBoolean stopped = new AtomicBoolean(false); @@ -344,9 +334,9 @@ public class Pipeline implements Iterable { * @param source * The source of the stream * @param sinks - * The sinks to which to stream + * The filters to which to stream */ - public Connection(Source source, Collection sinks) { + public Connection(Filter source, Collection sinks) { this.source = source; this.sinks = sinks; if (sinks.size() < 2) { @@ -406,10 +396,10 @@ public class Pipeline implements Iterable { } catch (IOException ioe1) { throw new IOException(String.format("I/O error while reading from %s.", source.name()), ioe1); } - List> futures = executorService.invokeAll(FluentIterable.from(sinks).transform(new Function>() { + List> futures = executorService.invokeAll(FluentIterable.from(sinks).transform(new Function>() { @Override - public Callable apply(final Sink sink) { + public Callable apply(final Filter sink) { return new Callable() { @Override @@ -485,7 +475,7 @@ public class Pipeline implements Iterable { * Returns the number of input bytes. * * @return The number of input bytes, or {@link Optional#absent()} if the - * component can not receive input + * filter did not receive input */ public Optional input() { return (input == -1) ? Optional.absent() : Optional.of(input); @@ -495,7 +485,7 @@ public class Pipeline implements Iterable { * Returns the number of output bytes. * * @return The number of output bytes, or {@link Optional#absent()} if the - * component can not send output + * filter did not send output */ public Optional output() { return (output == -1) ? Optional.absent() : Optional.of(output); diff --git a/src/main/java/net/pterodactylus/sonitus/data/Sink.java b/src/main/java/net/pterodactylus/sonitus/data/Sink.java deleted file mode 100644 index 937a4e5..0000000 --- a/src/main/java/net/pterodactylus/sonitus/data/Sink.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.pterodactylus.sonitus.data; - -import java.io.IOException; - -/** - * A sink is a destination for audio data. It can be played on speakers, it can - * be written to a file, or it can be sent to a remote streaming server. - * - * @author David ‘Bombe’ Roden - */ -public interface Sink extends ControlledComponent { - - /** - * Opens this sink using the format parameters of the given metadata. - * - * @param metadata - * The metadata of the stream - * @throws IOException - * if an I/O error occurs - */ - void open(Metadata metadata) throws IOException; - - /** Closes this sink. */ - void close(); - - /** - * Processes the given buffer of data. - * - * @param buffer - * The data to process - * @throws IOException - * if an I/O error occurs - */ - void process(byte[] buffer) throws IOException; - -} diff --git a/src/main/java/net/pterodactylus/sonitus/data/Source.java b/src/main/java/net/pterodactylus/sonitus/data/Source.java deleted file mode 100644 index b12ec20..0000000 --- a/src/main/java/net/pterodactylus/sonitus/data/Source.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.pterodactylus.sonitus.data; - -import java.io.IOException; - -/** - * A source produces an audio stream and accompanying metadata. - * - * @author David ‘Bombe’ Roden - */ -public interface Source extends ControlledComponent { - - /** - * Returns the metadata of the audio stream. - * - * @return The metadata of the audio stream - */ - Metadata metadata(); - - /** - * Retrieves data from the audio stream. - * - * @param bufferSize - * The maximum amount of bytes to retrieve from the audio stream - * @return A buffer filled with up to {@code bufferSize} bytes of data; the - * returned buffer may contain less data than requested but will not - * contain excess elements - * @throws IOException - * if an I/O error occurs - */ - byte[] get(int bufferSize) throws IOException; - -} diff --git a/src/main/java/net/pterodactylus/sonitus/data/event/SourceFinishedEvent.java b/src/main/java/net/pterodactylus/sonitus/data/event/SourceFinishedEvent.java deleted file mode 100644 index 6161a16..0000000 --- a/src/main/java/net/pterodactylus/sonitus/data/event/SourceFinishedEvent.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Sonitus - SourceFinishedEvent.java - Copyright © 2013 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sonitus.data.event; - -import net.pterodactylus.sonitus.data.Source; - -/** - * Event that is sent to an {@link com.google.common.eventbus.EventBus} when - * a {@link Source} is no longer in use. - * - * @author David ‘Bombe’ Roden - */ -public class SourceFinishedEvent { - - /** The source that is no longer in use. */ - private final Source source; - - /** - * Creates a new source finished event. - * - * @param source - * The source that is no longer in use - */ - public SourceFinishedEvent(Source source) { - this.source = source; - } - - /** - * Returns the source that is no longer in use - * - * @return The source that is no longer in use - */ - public Source source() { - return source; - } - -} diff --git a/src/main/java/net/pterodactylus/sonitus/data/filter/AudioProcessingFilter.java b/src/main/java/net/pterodactylus/sonitus/data/filter/AudioProcessingFilter.java index 7312f7f..933109e 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/filter/AudioProcessingFilter.java +++ b/src/main/java/net/pterodactylus/sonitus/data/filter/AudioProcessingFilter.java @@ -20,6 +20,7 @@ package net.pterodactylus.sonitus.data.filter; import java.io.IOException; import java.io.OutputStream; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.io.ProcessingOutputStream; @@ -28,7 +29,7 @@ import net.pterodactylus.sonitus.io.ProcessingOutputStream; * * @author David ‘Bombe’ Roden */ -public abstract class AudioProcessingFilter extends BasicFilter { +public abstract class AudioProcessingFilter extends AbstractFilter implements Filter { /** * Creates a new audio processing filter with the given name. @@ -41,7 +42,7 @@ public abstract class AudioProcessingFilter extends BasicFilter { } // - // BASICFILTER METHODS + // FILTER METHODS // @Override diff --git a/src/main/java/net/pterodactylus/sonitus/data/filter/BasicFilter.java b/src/main/java/net/pterodactylus/sonitus/data/filter/BasicFilter.java deleted file mode 100644 index 0f248d6..0000000 --- a/src/main/java/net/pterodactylus/sonitus/data/filter/BasicFilter.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Sonitus - AbstractFilter.java - Copyright © 2013 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sonitus.data.filter; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import net.pterodactylus.sonitus.data.AbstractControlledComponent; -import net.pterodactylus.sonitus.data.Controller; -import net.pterodactylus.sonitus.data.Filter; -import net.pterodactylus.sonitus.data.Metadata; - -import com.google.common.io.Closeables; - -/** - * Basic {@link Filter} implementation that pipes its input to its output. - * - * @author David ‘Bombe’ Roden - */ -public class BasicFilter extends AbstractControlledComponent implements Filter { - - /** The input stream from which to read. */ - private InputStream inputStream; - - /** The output stream to which to write. */ - private OutputStream outputStream; - - /** - * Creates a new dummy filter with the given name. - * - * @param name - * The name of the filter - */ - public BasicFilter(String name) { - super(name); - } - - // - // CONTROLLED METHODS - // - - @Override - public List> controllers() { - return Collections.emptyList(); - } - - // - // FILTER METHODS - // - - @Override - public void open(Metadata metadata) throws IOException { - metadataUpdated(metadata); - inputStream = createInputStream(); - outputStream = createOutputStream(); - } - - @Override - public void close() { - try { - Closeables.close(outputStream, true); - Closeables.close(inputStream, true); - } catch (IOException e) { - /* won’t throw. */ - } - } - - @Override - public void process(byte[] buffer) throws IOException { - outputStream.write(buffer); - outputStream.flush(); - } - - @Override - public byte[] get(int bufferSize) throws IOException { - byte[] buffer = new byte[bufferSize]; - int read = inputStream.read(buffer); - if (read == -1) { - throw new EOFException(); - } - return Arrays.copyOf(buffer, read); - } - - // - // SUBCLASS METHODS - // - - /** - * Creates the input stream from which {@link net.pterodactylus.sonitus.data.Pipeline} - * will read the audio data. If you override this, you have to override {@link - * #createOutputStream()}, too! - * - * @return The input stream to read from - * @throws IOException - * if an I/O error occurs - */ - protected InputStream createInputStream() throws IOException { - return new PipedInputStream(); - } - - /** - * Creates the output stream to which {@link net.pterodactylus.sonitus.data.Pipeline} - * will write the audio data. If you override this, you have to override {@link - * #createInputStream()}, too! - * - * @return The output stream to write to - * @throws IOException - * if an I/O error occurs - */ - protected OutputStream createOutputStream() throws IOException { - return new PipedOutputStream((PipedInputStream) inputStream); - } - -} diff --git a/src/main/java/net/pterodactylus/sonitus/data/filter/ExternalFilter.java b/src/main/java/net/pterodactylus/sonitus/data/filter/ExternalFilter.java index 73338b5..a482cf6 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/filter/ExternalFilter.java +++ b/src/main/java/net/pterodactylus/sonitus/data/filter/ExternalFilter.java @@ -22,6 +22,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.logging.Logger; +import net.pterodactylus.sonitus.data.AbstractFilter; +import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Metadata; import net.pterodactylus.sonitus.io.InputStreamDrainer; @@ -34,7 +36,7 @@ import com.google.common.collect.Iterables; * * @author David ‘Bombe’ Roden */ -public abstract class ExternalFilter extends BasicFilter { +public abstract class ExternalFilter extends AbstractFilter implements Filter { /** The logger. */ private final Logger logger = Logger.getLogger(getClass().getName()); @@ -69,10 +71,6 @@ public abstract class ExternalFilter extends BasicFilter { process.destroy(); } - // - // BASICFILTER METHODS - // - @Override protected InputStream createInputStream() throws IOException { return process.getInputStream(); diff --git a/src/main/java/net/pterodactylus/sonitus/data/filter/PredicateFilter.java b/src/main/java/net/pterodactylus/sonitus/data/filter/PredicateFilter.java index 8968596..20840a8 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/filter/PredicateFilter.java +++ b/src/main/java/net/pterodactylus/sonitus/data/filter/PredicateFilter.java @@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Metadata; @@ -33,7 +34,7 @@ import com.google.common.base.Predicate; * * @author David ‘Bombe’ Roden */ -public class PredicateFilter extends BasicFilter { +public class PredicateFilter extends AbstractFilter implements Filter { /** The predicate. */ private final Predicate metadataPredicate; diff --git a/src/main/java/net/pterodactylus/sonitus/data/filter/RateLimitingFilter.java b/src/main/java/net/pterodactylus/sonitus/data/filter/RateLimitingFilter.java index 881deeb..04ed2d1 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/filter/RateLimitingFilter.java +++ b/src/main/java/net/pterodactylus/sonitus/data/filter/RateLimitingFilter.java @@ -20,6 +20,8 @@ package net.pterodactylus.sonitus.data.filter; import java.io.IOException; import java.util.logging.Logger; +import net.pterodactylus.sonitus.data.AbstractFilter; +import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Metadata; /** @@ -29,7 +31,7 @@ import net.pterodactylus.sonitus.data.Metadata; * * @author David ‘Bombe’ Roden */ -public class RateLimitingFilter extends BasicFilter { +public class RateLimitingFilter extends AbstractFilter implements Filter { /** The logger. */ private static final Logger logger = Logger.getLogger(RateLimitingFilter.class.getName()); diff --git a/src/main/java/net/pterodactylus/sonitus/data/filter/StereoSeparationFilter.java b/src/main/java/net/pterodactylus/sonitus/data/filter/StereoSeparationFilter.java index 3993e11..6eef02c 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/filter/StereoSeparationFilter.java +++ b/src/main/java/net/pterodactylus/sonitus/data/filter/StereoSeparationFilter.java @@ -42,7 +42,7 @@ public class StereoSeparationFilter extends AudioProcessingFilter { } // - // CONTROLLED METHODS + // FILTER METHODS // @Override @@ -50,10 +50,6 @@ public class StereoSeparationFilter extends AudioProcessingFilter { return Arrays.>asList(separationKnob); } - // - // AUDIOPROCESSINGFILTER METHODS - // - @Override protected int[] processSamples(int[] samples) { if (samples.length == 1) { diff --git a/src/main/java/net/pterodactylus/sonitus/data/filter/TimeCounterFilter.java b/src/main/java/net/pterodactylus/sonitus/data/filter/TimeCounterFilter.java index cfa93af..ce08a61 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/filter/TimeCounterFilter.java +++ b/src/main/java/net/pterodactylus/sonitus/data/filter/TimeCounterFilter.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Metadata; @@ -31,7 +32,7 @@ import net.pterodactylus.sonitus.data.Metadata; * * @author David ‘Bombe’ Roden */ -public class TimeCounterFilter extends BasicFilter { +public class TimeCounterFilter extends AbstractFilter implements Filter { /** The byte counter. */ private final AtomicLong counter = new AtomicLong(); @@ -95,7 +96,7 @@ public class TimeCounterFilter extends BasicFilter { } // - // BASICFILTER METHODS + // FILTER METHODS // @Override diff --git a/src/main/java/net/pterodactylus/sonitus/data/filter/VolumeFilter.java b/src/main/java/net/pterodactylus/sonitus/data/filter/VolumeFilter.java index 02570db..01d325f 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/filter/VolumeFilter.java +++ b/src/main/java/net/pterodactylus/sonitus/data/filter/VolumeFilter.java @@ -47,7 +47,7 @@ public class VolumeFilter extends AudioProcessingFilter { } // - // CONTROLLED METHODS + // FILTER METHODS // @Override @@ -55,10 +55,6 @@ public class VolumeFilter extends AudioProcessingFilter { return Arrays.>asList(volumeFader, muteSwitch); } - // - // AUDIOPROCESSINGFILTER METHODS - // - @Override protected int[] processSamples(int[] samples) { int[] processedSamples = new int[samples.length]; diff --git a/src/main/java/net/pterodactylus/sonitus/data/sink/AudioSink.java b/src/main/java/net/pterodactylus/sonitus/data/sink/AudioSink.java index 07499ad..6cace62 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/sink/AudioSink.java +++ b/src/main/java/net/pterodactylus/sonitus/data/sink/AudioSink.java @@ -34,11 +34,10 @@ import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; -import net.pterodactylus.sonitus.data.AbstractControlledComponent; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.Controller; +import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Metadata; -import net.pterodactylus.sonitus.data.Sink; -import net.pterodactylus.sonitus.data.Source; import net.pterodactylus.sonitus.data.controller.Fader; import net.pterodactylus.sonitus.data.controller.Switch; import net.pterodactylus.sonitus.io.IntegralWriteOutputStream; @@ -46,12 +45,12 @@ import net.pterodactylus.sonitus.io.IntegralWriteOutputStream; import com.google.common.base.Preconditions; /** - * {@link Sink} implementation that uses the JDK’s {@link AudioSystem} to play - * all {@link Source}s. + * {@link Filter} implementation that uses the JDK’s {@link AudioSystem} to play + * all the audio signal. * * @author David ‘Bombe’ Roden */ -public class AudioSink extends AbstractControlledComponent implements Sink { +public class AudioSink extends AbstractFilter { /** The logger. */ private static final Logger logger = Logger.getLogger(AudioSink.class.getName()); @@ -135,7 +134,7 @@ public class AudioSink extends AbstractControlledComponent implements Sink { } // - // CONTROLLED METHODS + // FILTER METHODS // @Override @@ -143,13 +142,10 @@ public class AudioSink extends AbstractControlledComponent implements Sink { return Arrays.>asList(volumeFader, muteSwitch); } - // - // SINK METHODS - // - @Override public void open(Metadata metadata) throws IOException { Preconditions.checkArgument(metadata.encoding().equalsIgnoreCase("PCM"), "source must be PCM-encoded"); + super.open(metadata); AudioFormat audioFormat = new AudioFormat(metadata.frequency(), 16, metadata.channels(), true, false); try { sourceDataLine = AudioSystem.getSourceDataLine(audioFormat); @@ -178,6 +174,7 @@ public class AudioSink extends AbstractControlledComponent implements Sink { @Override public void process(byte[] buffer) throws IOException { sourceDataLineOutputStream.write(buffer); + super.process(buffer); logger.finest(String.format("AudioSink: Wrote %d Bytes.", buffer.length)); } diff --git a/src/main/java/net/pterodactylus/sonitus/data/sink/FileSink.java b/src/main/java/net/pterodactylus/sonitus/data/sink/FileSink.java index be934f3..5011321 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/sink/FileSink.java +++ b/src/main/java/net/pterodactylus/sonitus/data/sink/FileSink.java @@ -19,14 +19,10 @@ package net.pterodactylus.sonitus.data.sink; import java.io.FileOutputStream; import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.logging.Logger; -import net.pterodactylus.sonitus.data.AbstractControlledComponent; -import net.pterodactylus.sonitus.data.Controller; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.Metadata; -import net.pterodactylus.sonitus.data.Sink; /** * {@link net.pterodactylus.sonitus.data.Sink} that writes all received data @@ -34,7 +30,7 @@ import net.pterodactylus.sonitus.data.Sink; * * @author David ‘Bombe’ Roden */ -public class FileSink extends AbstractControlledComponent implements Sink { +public class FileSink extends AbstractFilter { /** The logger. */ private static final Logger logger = Logger.getLogger(FileSink.class.getName()); @@ -57,16 +53,7 @@ public class FileSink extends AbstractControlledComponent implements Sink { } // - // CONTROLLED METHODS - // - - @Override - public List> controllers() { - return Collections.emptyList(); - } - - // - // SINK METHODS + // FILTER METHODS // @Override diff --git a/src/main/java/net/pterodactylus/sonitus/data/sink/Icecast2Sink.java b/src/main/java/net/pterodactylus/sonitus/data/sink/Icecast2Sink.java index 8e1c154..f98fba5 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/sink/Icecast2Sink.java +++ b/src/main/java/net/pterodactylus/sonitus/data/sink/Icecast2Sink.java @@ -28,10 +28,9 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import net.pterodactylus.sonitus.data.AbstractControlledComponent; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.Controller; import net.pterodactylus.sonitus.data.Metadata; -import net.pterodactylus.sonitus.data.Sink; import net.pterodactylus.sonitus.io.InputStreamDrainer; import com.google.common.io.BaseEncoding; @@ -43,7 +42,7 @@ import com.google.common.io.Closeables; * * @author David ‘Bombe’ Roden */ -public class Icecast2Sink extends AbstractControlledComponent implements Sink { +public class Icecast2Sink extends AbstractFilter { /** The logger. */ private static final Logger logger = Logger.getLogger(Icecast2Sink.class.getName()); @@ -111,7 +110,7 @@ public class Icecast2Sink extends AbstractControlledComponent implements Sink { } // - // CONTROLLED METHODS + // FILTER METHODS // @Override @@ -119,10 +118,6 @@ public class Icecast2Sink extends AbstractControlledComponent implements Sink { return Collections.emptyList(); } - // - // SINK METHODS - // - @Override public void open(Metadata metadata) throws IOException { logger.info(String.format("Connecting to %s:%d...", server, port)); diff --git a/src/main/java/net/pterodactylus/sonitus/data/source/FileSource.java b/src/main/java/net/pterodactylus/sonitus/data/source/FileSource.java index 9aff8d4..7251f65 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/source/FileSource.java +++ b/src/main/java/net/pterodactylus/sonitus/data/source/FileSource.java @@ -27,21 +27,21 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import net.pterodactylus.sonitus.data.AbstractControlledComponent; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.Controller; +import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Metadata; -import net.pterodactylus.sonitus.data.Source; import net.pterodactylus.sonitus.io.IdentifyingInputStream; import com.google.common.base.Optional; /** - * A {@link net.pterodactylus.sonitus.data.Source} that is read from the local - * file system. + * A {@link Filter} that reads a file from the local file system and does not + * expect any input. * * @author David ‘Bombe’ Roden */ -public class FileSource extends AbstractControlledComponent implements Source { +public class FileSource extends AbstractFilter { /** The path of the file. */ private final String path; @@ -73,7 +73,7 @@ public class FileSource extends AbstractControlledComponent implements Source { } // - // CONTROLLED METHODS + // FILTER METHODS // @Override @@ -81,10 +81,6 @@ public class FileSource extends AbstractControlledComponent implements Source { return Collections.emptyList(); } - // - // SOURCE METHODS - // - @Override public byte[] get(int bufferSize) throws IOException { byte[] buffer = new byte[bufferSize]; diff --git a/src/main/java/net/pterodactylus/sonitus/data/source/MultiSource.java b/src/main/java/net/pterodactylus/sonitus/data/source/MultiSource.java index 80c3f56..b8afd82 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/source/MultiSource.java +++ b/src/main/java/net/pterodactylus/sonitus/data/source/MultiSource.java @@ -27,21 +27,20 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; import javax.swing.event.EventListenerList; -import net.pterodactylus.sonitus.data.AbstractControlledComponent; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.Controller; +import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Metadata; -import net.pterodactylus.sonitus.data.Source; import com.google.inject.Inject; /** - * {@link Source} implementation that simply forwards another source and - * supports changing the source without letting the {@link - * net.pterodactylus.sonitus.data.Sink} know. + * {@link Filter} implementation that simply forwards data from another filter + * and supports changing the source without letting downstream filters know. * * @author David ‘Bombe’ Roden */ -public class MultiSource extends AbstractControlledComponent implements Source { +public class MultiSource extends AbstractFilter { /** The logger. */ private static final Logger logger = Logger.getLogger(MultiSource.class.getName()); @@ -50,7 +49,7 @@ public class MultiSource extends AbstractControlledComponent implements Source { private final EventListenerList sourceFinishedListeners = new EventListenerList(); /** The current source. */ - private final AtomicReference source = new AtomicReference(); + private final AtomicReference source = new AtomicReference(); /** Whether the source was changed. */ private boolean sourceChanged; @@ -95,10 +94,10 @@ public class MultiSource extends AbstractControlledComponent implements Source { * @param source * The new source to use */ - public void setSource(Source source) { + public void setSource(Filter source) { checkNotNull(source, "source must not be null"); - Source oldSource = this.source.getAndSet(source); + Filter oldSource = this.source.getAndSet(source); if (!source.equals(oldSource)) { synchronized (this.source) { sourceChanged = true; @@ -115,7 +114,7 @@ public class MultiSource extends AbstractControlledComponent implements Source { /** * Notifies all registered listeners that the current source finished playing - * and that a new source should be {@link #setSource(Source) set}. + * and that a new source should be {@link #setSource(Filter) set}. * * @see SourceFinishedListener */ @@ -126,7 +125,7 @@ public class MultiSource extends AbstractControlledComponent implements Source { } // - // CONTROLLED METHODS + // FILTER METHODS // @Override @@ -144,10 +143,6 @@ public class MultiSource extends AbstractControlledComponent implements Source { return super.metadata(); } - // - // SOURCE METHODS - // - @Override public byte[] get(int bufferSize) throws EOFException, IOException { while (true) { @@ -167,7 +162,7 @@ public class MultiSource extends AbstractControlledComponent implements Source { // PRIVATE METHODS // - /** Waits for a new source to be {@link #setSource(Source) set}. */ + /** Waits for a new source to be {@link #setSource(Filter) set}. */ private void waitForNewSource() { fireSourceFinished(); synchronized (source) { diff --git a/src/main/java/net/pterodactylus/sonitus/data/source/SourceFinishedListener.java b/src/main/java/net/pterodactylus/sonitus/data/source/SourceFinishedListener.java index 35ad426..63c1eee 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/source/SourceFinishedListener.java +++ b/src/main/java/net/pterodactylus/sonitus/data/source/SourceFinishedListener.java @@ -19,8 +19,6 @@ package net.pterodactylus.sonitus.data.source; import java.util.EventListener; -import net.pterodactylus.sonitus.data.Source; - /** * Interface for {@link MultiSource} notifications if a source is finished * playing. diff --git a/src/main/java/net/pterodactylus/sonitus/data/source/StreamSource.java b/src/main/java/net/pterodactylus/sonitus/data/source/StreamSource.java index dc6e4d0..a66fb7a 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/source/StreamSource.java +++ b/src/main/java/net/pterodactylus/sonitus/data/source/StreamSource.java @@ -27,12 +27,11 @@ import java.util.List; import java.util.Map; import java.util.logging.Logger; -import net.pterodactylus.sonitus.data.AbstractControlledComponent; +import net.pterodactylus.sonitus.data.AbstractFilter; import net.pterodactylus.sonitus.data.ContentMetadata; import net.pterodactylus.sonitus.data.Controller; import net.pterodactylus.sonitus.data.FormatMetadata; import net.pterodactylus.sonitus.data.Metadata; -import net.pterodactylus.sonitus.data.Source; import net.pterodactylus.sonitus.io.MetadataStream; import com.google.common.base.Optional; @@ -47,7 +46,7 @@ import com.google.common.primitives.Ints; * * @author David ‘Bombe’ Roden */ -public class StreamSource extends AbstractControlledComponent implements Source { +public class StreamSource extends AbstractFilter { /** The logger. */ private static final Logger logger = Logger.getLogger(StreamSource.class.getName()); @@ -127,7 +126,7 @@ public class StreamSource extends AbstractControlledComponent implements Source } // - // CONTROLLED METHODS + // FILTER METHODS // @Override @@ -140,10 +139,6 @@ public class StreamSource extends AbstractControlledComponent implements Source return Collections.emptyList(); } - // - // SOURCE METHODS - // - @Override public Metadata metadata() { Optional streamMetadata = metadataStream.getContentMetadata(); diff --git a/src/main/java/net/pterodactylus/sonitus/gui/ComponentInfoPanel.java b/src/main/java/net/pterodactylus/sonitus/gui/ComponentInfoPanel.java deleted file mode 100644 index 752ceef..0000000 --- a/src/main/java/net/pterodactylus/sonitus/gui/ComponentInfoPanel.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Sonitus - ComponentInfoPanel.java - Copyright © 2013 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sonitus.gui; - -import java.awt.Dimension; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.pterodactylus.sonitus.data.ControlledComponent; -import net.pterodactylus.sonitus.data.Controller; -import net.pterodactylus.sonitus.data.FormatMetadata; -import net.pterodactylus.sonitus.data.controller.Fader; -import net.pterodactylus.sonitus.data.controller.Knob; -import net.pterodactylus.sonitus.data.controller.Switch; - -import com.google.common.base.Optional; - -/** - * Panel that shows information about a {@link ControlledComponent}. - * - * @author David ‘Bombe’ Roden - */ -public class ComponentInfoPanel extends JPanel { - - /** The name of the component. */ - private final JLabel headerLabel = new JLabel(); - - /** The number of received input bytes. */ - private final JLabel inputLabel = new JLabel(); - - /** The number of sent output bytes. */ - private final JLabel outputLabel = new JLabel(); - - /** The current format metadata. */ - private final JLabel formatLabel = new JLabel(); - - /** - * Creates a new component info panel. - * - * @param controlledComponent - * The component to display - */ - public ComponentInfoPanel(ControlledComponent controlledComponent) { - super(new GridBagLayout()); - - setPreferredSize(new Dimension(300, 0)); - createPanel(controlledComponent); - } - - // - // ACTIONS - // - - /** - * Sets the number of received input bytes. - * - * @param input - * The number of received input bytes - * @return This panel - */ - public ComponentInfoPanel input(Optional input) { - if (input.isPresent()) { - inputLabel.setText(format(input.get())); - } else { - inputLabel.setText(""); - } - return this; - } - - /** - * Sets the number of sent output bytes. - * - * @param output - * The number of sent output input bytes - * @return This panel - */ - public ComponentInfoPanel output(Optional output) { - if (output.isPresent()) { - outputLabel.setText(format(output.get())); - } else { - outputLabel.setText(""); - } - return this; - } - - /** - * Sets the current format metadata. - * - * @param metadata - * The format metadata - * @return This panel - */ - public ComponentInfoPanel format(Optional metadata) { - if (metadata.isPresent()) { - formatLabel.setText(metadata.get().toString()); - } else { - formatLabel.setText(""); - } - return this; - } - - // - // PRIVATE METHODS - // - - /** - * Creates the panel for the given controlled component. - * - * @param controlledComponent - * The controlled component - */ - private void createPanel(ControlledComponent controlledComponent) { - setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); - - headerLabel.setText(controlledComponent.name()); - headerLabel.setFont(headerLabel.getFont().deriveFont(Font.BOLD)); - - int line = 0; - add(headerLabel, new GridBagConstraints(0, line++, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - add(new JLabel("Input"), new GridBagConstraints(0, line, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(18, 0, 0, 0), 0, 0)); - add(inputLabel, new GridBagConstraints(1, line++, 1, 1, 1.0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(18, 6, 0, 0), 0, 0)); - add(new JLabel("Output"), new GridBagConstraints(0, line, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0)); - add(outputLabel, new GridBagConstraints(1, line++, 1, 1, 1.0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - add(new JLabel("Format"), new GridBagConstraints(0, line, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0)); - add(formatLabel, new GridBagConstraints(1, line++, 1, 1, 1.0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - /* add the controllers. */ - for (Controller controller : controlledComponent.controllers()) { - add(new JLabel(controller.name()), new GridBagConstraints(0, line, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(6, 0, 0, 6), 0, 0)); - if (controller instanceof Fader) { - add(new FaderPanel((Fader) controller), new GridBagConstraints(1, line++, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0)); - } else if (controller instanceof Switch) { - add(new SwitchPanel((Switch) controller), new GridBagConstraints(1, line++, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0)); - } else if (controller instanceof Knob) { - add(new KnobPanel((Knob) controller), new GridBagConstraints(1, line++, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0)); - } - } - - add(Box.createVerticalGlue(), new GridBagConstraints(1, line++, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(6, 6, 0, 0), 0, 0)); - } - - /** - * Formats the number using SI prefixes so that a maximum of 3 digits are - * shown. - * - * @param number - * The number to format - * @return The formatted number - */ - private static String format(long number) { - String[] prefixes = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; - double shortenedNumber = number; - for (String prefix : prefixes) { - if (shortenedNumber < 1000) { - return String.format("%.1f %sB", shortenedNumber, prefix); - } - shortenedNumber /= 1024; - } - return String.format("%.1e B", (double) number); - } - -} diff --git a/src/main/java/net/pterodactylus/sonitus/gui/FilterInfoPanel.java b/src/main/java/net/pterodactylus/sonitus/gui/FilterInfoPanel.java new file mode 100644 index 0000000..b26afe9 --- /dev/null +++ b/src/main/java/net/pterodactylus/sonitus/gui/FilterInfoPanel.java @@ -0,0 +1,183 @@ +/* + * Sonitus - FilterInfoPanel.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sonitus.gui; + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import net.pterodactylus.sonitus.data.Controller; +import net.pterodactylus.sonitus.data.Filter; +import net.pterodactylus.sonitus.data.FormatMetadata; +import net.pterodactylus.sonitus.data.controller.Fader; +import net.pterodactylus.sonitus.data.controller.Knob; +import net.pterodactylus.sonitus.data.controller.Switch; + +import com.google.common.base.Optional; + +/** + * Panel that shows information about a {@link Filter}. + * + * @author David ‘Bombe’ Roden + */ +public class FilterInfoPanel extends JPanel { + + /** The name of the filter. */ + private final JLabel headerLabel = new JLabel(); + + /** The number of received input bytes. */ + private final JLabel inputLabel = new JLabel(); + + /** The number of sent output bytes. */ + private final JLabel outputLabel = new JLabel(); + + /** The current format metadata. */ + private final JLabel formatLabel = new JLabel(); + + /** + * Creates a new filter info panel. + * + * @param filter + * The filter to display + */ + public FilterInfoPanel(Filter filter) { + super(new GridBagLayout()); + + setPreferredSize(new Dimension(300, 0)); + createPanel(filter); + } + + // + // ACTIONS + // + + /** + * Sets the number of received input bytes. + * + * @param input + * The number of received input bytes + * @return This panel + */ + public FilterInfoPanel input(Optional input) { + if (input.isPresent()) { + inputLabel.setText(format(input.get())); + } else { + inputLabel.setText(""); + } + return this; + } + + /** + * Sets the number of sent output bytes. + * + * @param output + * The number of sent output input bytes + * @return This panel + */ + public FilterInfoPanel output(Optional output) { + if (output.isPresent()) { + outputLabel.setText(format(output.get())); + } else { + outputLabel.setText(""); + } + return this; + } + + /** + * Sets the current format metadata. + * + * @param metadata + * The format metadata + * @return This panel + */ + public FilterInfoPanel format(Optional metadata) { + if (metadata.isPresent()) { + formatLabel.setText(metadata.get().toString()); + } else { + formatLabel.setText(""); + } + return this; + } + + // + // PRIVATE METHODS + // + + /** + * Creates the panel for the given filter. + * + * @param filter + * The filter to create a panel for + */ + private void createPanel(Filter filter) { + setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); + + headerLabel.setText(filter.name()); + headerLabel.setFont(headerLabel.getFont().deriveFont(Font.BOLD)); + + int line = 0; + add(headerLabel, new GridBagConstraints(0, line++, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + add(new JLabel("Input"), new GridBagConstraints(0, line, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(18, 0, 0, 0), 0, 0)); + add(inputLabel, new GridBagConstraints(1, line++, 1, 1, 1.0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(18, 6, 0, 0), 0, 0)); + add(new JLabel("Output"), new GridBagConstraints(0, line, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0)); + add(outputLabel, new GridBagConstraints(1, line++, 1, 1, 1.0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + add(new JLabel("Format"), new GridBagConstraints(0, line, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0)); + add(formatLabel, new GridBagConstraints(1, line++, 1, 1, 1.0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + /* add the controllers. */ + for (Controller controller : filter.controllers()) { + add(new JLabel(controller.name()), new GridBagConstraints(0, line, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(6, 0, 0, 6), 0, 0)); + if (controller instanceof Fader) { + add(new FaderPanel((Fader) controller), new GridBagConstraints(1, line++, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0)); + } else if (controller instanceof Switch) { + add(new SwitchPanel((Switch) controller), new GridBagConstraints(1, line++, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0)); + } else if (controller instanceof Knob) { + add(new KnobPanel((Knob) controller), new GridBagConstraints(1, line++, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0)); + } + } + + add(Box.createVerticalGlue(), new GridBagConstraints(1, line++, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(6, 6, 0, 0), 0, 0)); + } + + /** + * Formats the number using SI prefixes so that a maximum of 3 digits are + * shown. + * + * @param number + * The number to format + * @return The formatted number + */ + private static String format(long number) { + String[] prefixes = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; + double shortenedNumber = number; + for (String prefix : prefixes) { + if (shortenedNumber < 1000) { + return String.format("%.1f %sB", shortenedNumber, prefix); + } + shortenedNumber /= 1024; + } + return String.format("%.1e B", (double) number); + } + +} diff --git a/src/main/java/net/pterodactylus/sonitus/gui/MainWindow.java b/src/main/java/net/pterodactylus/sonitus/gui/MainWindow.java index bb69578..85a49fb 100644 --- a/src/main/java/net/pterodactylus/sonitus/gui/MainWindow.java +++ b/src/main/java/net/pterodactylus/sonitus/gui/MainWindow.java @@ -30,9 +30,9 @@ import javax.swing.JTabbedPane; import javax.swing.Timer; import javax.swing.WindowConstants; -import net.pterodactylus.sonitus.data.ControlledComponent; +import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Pipeline; -import net.pterodactylus.sonitus.gui.PipelinePanel.ComponentSelectionListener; +import net.pterodactylus.sonitus.gui.PipelinePanel.FilterSelectionListener; import net.pterodactylus.sonitus.main.Version; import com.google.common.base.Optional; @@ -48,7 +48,7 @@ public class MainWindow extends JFrame { /** The pipeline to display. */ private final Pipeline pipeline; - /** The tabbed pane displaying all controlled components. */ + /** The tabbed pane displaying all pipelines. */ private final JTabbedPane tabbedPane = new JTabbedPane(); /** The info panel card layout. */ @@ -57,8 +57,8 @@ public class MainWindow extends JFrame { /** The info panel. */ private final JPanel infoPanel = new JPanel(infoPanelCardLayout); - /** The mapping from controlled components to info panels. */ - private final Map controlledInfoPanels = Maps.newHashMap(); + /** The mapping from filters to info panels. */ + private final Map filterInfoPanels = Maps.newHashMap(); /** * Creates a new main window. @@ -72,11 +72,11 @@ public class MainWindow extends JFrame { tabbedPane.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); final JPanel pipelineInfoPanel = new JPanel(new BorderLayout(12, 12)); PipelinePanel pipelinePanel = new PipelinePanel(pipeline); - pipelinePanel.addComponentHoverListener(new ComponentSelectionListener() { + pipelinePanel.addFilterSelectionListener(new FilterSelectionListener() { @Override - public void componentSelected(ControlledComponent controlledComponent) { - infoPanelCardLayout.show(infoPanel, controlledComponent.name()); + public void filterSelected(Filter filter) { + infoPanelCardLayout.show(infoPanel, filter.name()); } }); pipelineInfoPanel.add(pipelinePanel, BorderLayout.CENTER); @@ -85,11 +85,11 @@ public class MainWindow extends JFrame { getContentPane().add(tabbedPane, BorderLayout.CENTER); setSize(new Dimension(800, 450)); - /* create info panels for all components. */ - for (ControlledComponent controlledComponent : pipeline) { - ComponentInfoPanel componentInfoPanel = new ComponentInfoPanel(controlledComponent); - infoPanel.add(componentInfoPanel, controlledComponent.name()); - controlledInfoPanels.put(controlledComponent, componentInfoPanel); + /* create info panels for all filters. */ + for (Filter fliter : pipeline) { + FilterInfoPanel filterInfoPanel = new FilterInfoPanel(fliter); + infoPanel.add(filterInfoPanel, fliter.name()); + filterInfoPanels.put(fliter, filterInfoPanel); } Timer timer = new Timer(250, new ActionListener() { @@ -97,11 +97,11 @@ public class MainWindow extends JFrame { @Override public void actionPerformed(ActionEvent actionEvent) { /* update all info panels. */ - for (ControlledComponent controlled : MainWindow.this.pipeline) { - ComponentInfoPanel componentInfoPanel = controlledInfoPanels.get(controlled); - componentInfoPanel.input(MainWindow.this.pipeline.trafficCounter(controlled).input()); - componentInfoPanel.output(MainWindow.this.pipeline.trafficCounter(controlled).output()); - componentInfoPanel.format(Optional.of(controlled.metadata().format())); + for (Filter filter : MainWindow.this.pipeline) { + FilterInfoPanel filterInfoPanel = filterInfoPanels.get(filter); + filterInfoPanel.input(MainWindow.this.pipeline.trafficCounter(filter).input()); + filterInfoPanel.output(MainWindow.this.pipeline.trafficCounter(filter).output()); + filterInfoPanel.format(Optional.of(filter.metadata().format())); } } }); diff --git a/src/main/java/net/pterodactylus/sonitus/gui/PipelinePanel.java b/src/main/java/net/pterodactylus/sonitus/gui/PipelinePanel.java index d47003d..93189ef 100644 --- a/src/main/java/net/pterodactylus/sonitus/gui/PipelinePanel.java +++ b/src/main/java/net/pterodactylus/sonitus/gui/PipelinePanel.java @@ -38,15 +38,13 @@ import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.event.EventListenerList; -import net.pterodactylus.sonitus.data.ControlledComponent; +import net.pterodactylus.sonitus.data.Filter; import net.pterodactylus.sonitus.data.Metadata; import net.pterodactylus.sonitus.data.MetadataListener; import net.pterodactylus.sonitus.data.Pipeline; -import net.pterodactylus.sonitus.data.Sink; -import net.pterodactylus.sonitus.data.Source; /** - * {@link JPanel} that displays all components of a {@link Pipeline}. + * {@link JPanel} that displays all filters of a {@link Pipeline}. * * @author David ‘Bombe’ Roden */ @@ -58,11 +56,11 @@ public class PipelinePanel extends JPanel { /** The pipeline being displayed. */ private final Pipeline pipeline; - /** The component hover listeners. */ - private final EventListenerList componentSelectionListeners = new EventListenerList(); + /** The filter selection listeners. */ + private final EventListenerList filterSelectionListeners = new EventListenerList(); - /** The currently selected component. */ - private JComponent selectedComponent; + /** The currently selected filter. */ + private JComponent selectedFilter; /** * Creates a new pipeline panel displaying the given pipeline. @@ -81,13 +79,13 @@ public class PipelinePanel extends JPanel { // /** - * Adds the given component selection listener to this panel. + * Adds the given filter selection listener to this panel. * - * @param componentSelectionListener - * The component selection listener to add + * @param filterSelectionListener + * The filter selection listener to add */ - public void addComponentHoverListener(ComponentSelectionListener componentSelectionListener) { - componentSelectionListeners.add(ComponentSelectionListener.class, componentSelectionListener); + public void addFilterSelectionListener(FilterSelectionListener filterSelectionListener) { + filterSelectionListeners.add(FilterSelectionListener.class, filterSelectionListener); } // @@ -99,16 +97,11 @@ public class PipelinePanel extends JPanel { /* clear everything. */ removeAll(); - /* count all sinks. */ + /* count all filters. */ int sinkCount = 0; - for (ControlledComponent component : pipeline.components()) { - if (!(component instanceof Source)) { - logger.finest(String.format("%s is not a Source, skipping.", component.name())); - sinkCount++; - continue; - } - Collection sinks = pipeline.sinks((Source) component); - logger.finest(String.format("%s has %d sinks: %s", component.name(), sinks.size(), sinks)); + for (Filter filter : pipeline.filters()) { + Collection sinks = pipeline.filters(filter); + logger.finest(String.format("%s has %d filters: %s", filter.name(), sinks.size(), sinks)); if (sinks.isEmpty()) { sinkCount++; } @@ -120,120 +113,119 @@ public class PipelinePanel extends JPanel { gridCellCount *= n; } - /* paint all components recursively. */ - addControlled(pipeline.source(), 0, 0, gridCellCount, null); + /* paint all filters recursively. */ + addFilter(pipeline.source(), 0, 0, gridCellCount, null); } /** - * Displays the given component. + * Displays the given filter. * - * @param controlledComponent - * The component to add this panel. + * @param filter + * The filter to add this panel. * @param level - * The level at which to show the component (the source is level {@code 0}) + * The level at which to show the filter (the source is level {@code 0}) * @param position - * The position at which to display the component + * The position at which to display the filter * @param width - * The width of the component in grid cells + * The width of the filter in grid cells */ - private void addControlled(final ControlledComponent controlledComponent, int level, int position, int width, ControlledComponent parentComponent) { - /* create a GUI component that displays the component. */ - final JPanel componentPanel = createComponentPanel(controlledComponent, parentComponent); - componentPanel.addMouseListener(new MouseAdapter() { + private void addFilter(final Filter filter, int level, int position, int width, Filter parentFilter) { + /* create a GUI component that displays the filter. */ + final JPanel filterPanel = createFilterPanel(filter, parentFilter); + filterPanel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { for (Component component : getComponents()) { component.setBackground(UIManager.getColor("Panel.background")); } - for (ComponentSelectionListener componentSelectionListener : componentSelectionListeners.getListeners(ComponentSelectionListener.class)) { - componentPanel.setBackground(Color.LIGHT_GRAY); - componentSelectionListener.componentSelected(controlledComponent); + for (FilterSelectionListener filterSelectionListener : filterSelectionListeners.getListeners(FilterSelectionListener.class)) { + filterPanel.setBackground(Color.LIGHT_GRAY); + filterSelectionListener.filterSelected(filter); } - selectedComponent = componentPanel; + selectedFilter = filterPanel; } @Override public void mouseEntered(MouseEvent mouseEvent) { - if (componentPanel != selectedComponent) { - componentPanel.setBackground(Color.white); + if (filterPanel != selectedFilter) { + filterPanel.setBackground(Color.white); } } @Override public void mouseExited(MouseEvent mouseEvent) { - if (componentPanel != selectedComponent) { - componentPanel.setBackground(UIManager.getColor("Panel.background")); + if (filterPanel != selectedFilter) { + filterPanel.setBackground(UIManager.getColor("Panel.background")); } } }); - /* show component. */ - add(componentPanel, new GridBagConstraints(position, level, width, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + /* show filter. */ + add(filterPanel, new GridBagConstraints(position, level, width, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - /* if the component does not have connected sinks, exit here. */ - if (!(controlledComponent instanceof Source)) { + /* if the filter does not have connected filters, exit here. */ + Collection sinks = pipeline.filters(filter); + if (sinks.isEmpty()) { add(new JPanel(), new GridBagConstraints(position, 999, width, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); return; } - /* iterate over the component’s sinks. */ - Collection sinks = pipeline.sinks((Source) controlledComponent); + /* iterate over the filter’s connected filters. */ if (!sinks.isEmpty()) { int sinkWidth = width / sinks.size(); int sinkIndex = 0; - for (Sink connectedSink : sinks) { - /* distribute all sinks evenly below this source. */ - addControlled(connectedSink, level + 1, position + sinkIndex * sinkWidth, sinkWidth, controlledComponent); + for (Filter connectedSink : sinks) { + /* distribute all filters evenly below this source. */ + addFilter(connectedSink, level + 1, position + sinkIndex * sinkWidth, sinkWidth, filter); sinkIndex++; } } } /** - * Creates a panel displaying a single component. + * Creates a panel displaying a single filter. * - * @param controlledComponent - * The component to display + * @param filter + * The filter to display * @return The created panel */ - private static JPanel createComponentPanel(final ControlledComponent controlledComponent, final ControlledComponent parentComponent) { - JPanel componentPanel = new JPanel(new BorderLayout(12, 12)); - componentPanel.setBorder(createCompoundBorder(createEtchedBorder(), createEmptyBorder(0, 4, 0, 3))); - componentPanel.add(new JLabel(controlledComponent.name()), BorderLayout.WEST); - final JLabel titleLabel = new JLabel(controlledComponent.metadata().fullTitle()); + private static JPanel createFilterPanel(final Filter filter, final Filter parentFilter) { + JPanel filterPanel = new JPanel(new BorderLayout(12, 12)); + filterPanel.setBorder(createCompoundBorder(createEtchedBorder(), createEmptyBorder(0, 4, 0, 3))); + filterPanel.add(new JLabel(filter.name()), BorderLayout.WEST); + final JLabel titleLabel = new JLabel(filter.metadata().fullTitle()); titleLabel.setFont(titleLabel.getFont().deriveFont(titleLabel.getFont().getSize2D() * 0.8f)); - componentPanel.add(titleLabel, BorderLayout.EAST); - if (parentComponent != null) { - titleLabel.setVisible(!parentComponent.metadata().fullTitle().equals(controlledComponent.metadata().fullTitle())); + filterPanel.add(titleLabel, BorderLayout.EAST); + if (parentFilter != null) { + titleLabel.setVisible(!parentFilter.metadata().fullTitle().equals(filter.metadata().fullTitle())); } - controlledComponent.addMetadataListener(new MetadataListener() { + filter.addMetadataListener(new MetadataListener() { @Override - public void metadataUpdated(ControlledComponent component, Metadata metadata) { + public void metadataUpdated(Filter filter, Metadata metadata) { titleLabel.setText(metadata.fullTitle()); - titleLabel.setVisible((parentComponent == null) || !parentComponent.metadata().fullTitle().equals(metadata.fullTitle())); + titleLabel.setVisible((parentFilter == null) || !parentFilter.metadata().fullTitle().equals(metadata.fullTitle())); } }); - return componentPanel; + return filterPanel; } /** * Interface for objects that want to be notified if the user moves the mouse - * cursor over a controlled component. + * cursor over a filter. * * @author David ‘Bombe’ Roden */ - public static interface ComponentSelectionListener extends EventListener { + public static interface FilterSelectionListener extends EventListener { /** - * Notifies the listener that the mouse is now over the given controlled - * component. + * Notifies the listener that the mouse is now over the given filter. * - * @param controlledComponent - * The controlled component now under the mouse + * @param filter + * The filter now under the mouse */ - void componentSelected(ControlledComponent controlledComponent); + void filterSelected(Filter filter); } diff --git a/src/main/java/net/pterodactylus/sonitus/gui/SwitchPanel.java b/src/main/java/net/pterodactylus/sonitus/gui/SwitchPanel.java index e3350a7..dba2f3c 100644 --- a/src/main/java/net/pterodactylus/sonitus/gui/SwitchPanel.java +++ b/src/main/java/net/pterodactylus/sonitus/gui/SwitchPanel.java @@ -22,9 +22,7 @@ import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.BorderFactory; import javax.swing.JCheckBox; -import javax.swing.JLabel; import javax.swing.JPanel; import net.pterodactylus.sonitus.data.controller.Switch; @@ -39,8 +37,8 @@ public class SwitchPanel extends JPanel { /** * Creates a new fader panel. * - * @param fader - * The fader being controlled + * @param switchController + * The switch being controlled */ public SwitchPanel(final Switch switchController) { super(new GridBagLayout());