001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030/**
031 *
032 */
033package org.openimaj.audio.processor;
034
035import org.openimaj.audio.AudioStream;
036import org.openimaj.audio.SampleChunk;
037
038/**
039 * An interface for objects that are able to process audio sample data. Due to
040 * the fact that audio processors provide processed audio, they are also able to
041 * implement the {@link AudioStream} interface thereby making processors
042 * chainable.
043 *
044 * @author David Dupplaw (dpd@ecs.soton.ac.uk)
045 * @created 8 Jun 2011
046 *
047 */
048public abstract class AudioProcessor extends AudioStream
049{
050        /** The audio stream to process in a chain */
051        private AudioStream stream = null;
052
053        /**
054         * A default constructor for processing sample chunks or files in an ad-hoc
055         * manner.
056         */
057        public AudioProcessor()
058        {
059        }
060
061        /**
062         * Construct a new processor based on the given stream. This processor can
063         * then be used as a stream itself in a chain.
064         *
065         * @param a
066         *            The audio stream to process.
067         */
068        public AudioProcessor(final AudioStream a)
069        {
070                this.stream = a;
071                if (a != null)
072                        this.format = a.getFormat().clone();
073        }
074
075        /**
076         * Function to process a whole audio stream. If the process returns null, it
077         * will stop the processing of the audio stream. Note that the output of the
078         * audio stream processing is not stored (it may be a live stream and so
079         * would be too large to store), so the caller must interact with the audio
080         * processor themselves to retrieve any useful information from the
081         * processing.
082         *
083         * @param a
084         *            The audio stream to process.
085         * @throws Exception
086         *             If the processing failed
087         */
088        public void process(final AudioStream a) throws Exception
089        {
090                this.stream = a;
091                while (this.nextSampleChunk() != null)
092                        ;
093                this.processingComplete(a);
094        }
095
096        /**
097         * Function that takes a sample chunk and processes the chunk. It should
098         * also return a sample chunk containing the processed data. If wished, the
099         * chunk may be side-affected and the input chunk returned. It should not be
100         * assumed that the input chunk will be side-affected, but it must be noted
101         * that it is possible that it could be. This process function may also
102         * return null. If null is returned it means that the rest of the audio
103         * stream is not required to be processed by this processing function.
104         * Whether the rest of the sample chunks are copied or ignored is up to the
105         * caller.
106         *
107         * @param sample
108         *            The sample chunk to process.
109         * @return A sample chunk containing processed data.
110         * @throws Exception
111         *             If the processing could not take place
112         */
113        public abstract SampleChunk process(SampleChunk sample) throws Exception;
114
115        /**
116         * Called when the processing of a given audio stream has been completed.
117         * This can be used to alter the audio stream's properties.
118         *
119         * @param a
120         *            The audio stream that has finished processing.
121         */
122        public void processingComplete(final AudioStream a)
123        {
124                // Default is no implementation. Override this if necessary.
125        }
126
127        /**
128         * {@inheritDoc}
129         *
130         * @see org.openimaj.audio.AudioStream#nextSampleChunk()
131         */
132        @Override
133        public SampleChunk nextSampleChunk()
134        {
135                try
136                {
137                        final SampleChunk s = this.stream.nextSampleChunk();
138                        return (s != null ? this.process(s) : null);
139                } catch (final Exception e)
140                {
141                        e.printStackTrace();
142                        return null;
143                }
144        }
145
146        /**
147         * Get the underlying stream. Will return null for non-chained audio
148         * processors.
149         *
150         * @return The underlying stream on chained processors.
151         */
152        public AudioStream getUnderlyingStream()
153        {
154                return this.stream;
155        }
156
157        /**
158         * Sets the underlying stream, allowing it to be changed.
159         *
160         * @param stream
161         *            The stream
162         */
163        public void setUnderlyingStream(final AudioStream stream)
164        {
165                this.stream = stream;
166                if (stream != null)
167                        this.format = stream.getFormat().clone();
168        }
169
170        /**
171         * {@inheritDoc}
172         *
173         * The implementation in this class just calls reset on the wrapped stream
174         * (if it's set).
175         *
176         * @see org.openimaj.audio.AudioStream#reset()
177         */
178        @Override
179        public void reset()
180        {
181                if (this.stream != null) {
182                        stream.reset();
183                }
184        }
185
186        /**
187         * {@inheritDoc}
188         *
189         * @see org.openimaj.audio.AudioStream#getLength()
190         */
191        @Override
192        public long getLength()
193        {
194                return this.stream.getLength();
195        }
196}