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.samples;
034
035import java.util.Iterator;
036
037import org.apache.commons.lang.NotImplementedException;
038import org.openimaj.audio.AudioFormat;
039import org.openimaj.audio.SampleChunk;
040import org.openimaj.audio.timecode.AudioTimecode;
041
042/**
043 * A {@link SampleBuffer} for 8 bit sample chunks.
044 *
045 * @author David Dupplaw (dpd@ecs.soton.ac.uk)
046 * @created 23rd November 2011
047 */
048public class SampleBuffer8Bit implements SampleBuffer, Iterator<Float> {
049        /** Scalar to convert integer to byte */
050        private final static int SAMPLE_SCALAR = Integer.MAX_VALUE / Byte.MAX_VALUE;
051
052        /** The byte buffer */
053        private byte[] byteBuffer = null;
054
055        /** The audio format of the samples */
056        private AudioFormat format = null;
057
058        /** The iterator counter for iterating over the samples */
059        private int iteratorCount;
060
061        /** The timecode of this sample buffer */
062        private AudioTimecode timecode;
063
064        /**
065         * Create a new 8-bit sample buffer using the given samples and the given
066         * audio format.
067         *
068         * @param samples
069         *            The samples to buffer.
070         * @param af
071         *            The audio format.
072         */
073        public SampleBuffer8Bit(final SampleChunk samples, final AudioFormat af) {
074                this.format = af;
075                if (this.format == null || this.format.getNBits() != 8)
076                        throw new IllegalArgumentException("Number of bits " +
077                                        "must be 8 if you're instantiating an 8 bit " +
078                                        "sample buffer. However " +
079                                        (this.format == null ? "format object was null."
080                                                        : "number of bits in format was " + this.format.getNBits()));
081
082                this.byteBuffer = samples.getSamples();
083                this.timecode = samples.getStartTimecode();
084        }
085
086        /**
087         * Create a new sample buffer with the given format and the given number of
088         * samples. It does not scale for the number of channels in the audio
089         * format, so you must pre-multiply the number of samples by the number of
090         * channels if you are only counting samples per channel.
091         *
092         * @param af
093         *            The {@link AudioFormat} of the samples
094         * @param nSamples
095         *            The number of samples
096         */
097        public SampleBuffer8Bit(final AudioFormat af, final int nSamples) {
098                this.format = af.clone();
099                if (this.format == null || this.format.getNBits() != 8)
100                        throw new IllegalArgumentException("Number of bits " +
101                                        "must be 8 if you're instantiating an 8 bit " +
102                                        "sample buffer. However " +
103                                        (this.format == null ? "format object was null."
104                                                        : "number of bits in format was " + this.format.getNBits()));
105
106                this.byteBuffer = new byte[nSamples];
107        }
108
109        /**
110         * {@inheritDoc}
111         * 
112         * @see org.openimaj.audio.samples.SampleBuffer#getSampleChunk()
113         */
114        @Override
115        public SampleChunk getSampleChunk() {
116                final SampleChunk sc = new SampleChunk(this.byteBuffer, this.format);
117                sc.setStartTimecode(this.timecode);
118                return sc;
119        }
120
121        /**
122         * {@inheritDoc}
123         *
124         * Note that because we cannot use native methods for copying parts of an
125         * array, we must use Java methods so this will be considerably slower than
126         * {@link #getSampleChunk()}.
127         *
128         * @see org.openimaj.audio.samples.SampleBuffer#getSampleChunk(int)
129         */
130        @Override
131        public SampleChunk getSampleChunk(final int channel) {
132                if (channel > this.format.getNumChannels())
133                        throw new IllegalArgumentException("Cannot generate sample chunk " +
134                                        "for channel " + channel + " as sample only has " +
135                                        this.format.getNumChannels() + " channels.");
136
137                if (channel == 0 && this.format.getNumChannels() == 1)
138                        return this.getSampleChunk();
139
140                final byte[] newSamples = new byte[this.size()];
141                for (int i = 0; i < this.size(); i++)
142                        newSamples[i] = this.byteBuffer[i * this.format.getNumChannels() + channel];
143
144                final AudioFormat af = this.format.clone();
145                af.setNumChannels(1);
146                return new SampleChunk(newSamples, af);
147        }
148
149        /**
150         * {@inheritDoc}
151         * 
152         * @see org.openimaj.audio.samples.SampleBuffer#get(int)
153         */
154        @Override
155        public float get(final int index) {
156                // Convert the byte to an integer
157                return this.byteBuffer[index] * SampleBuffer8Bit.SAMPLE_SCALAR;
158        }
159
160        /**
161         * {@inheritDoc}
162         * 
163         * @see org.openimaj.audio.samples.SampleBuffer#getUnscaled(int)
164         */
165        @Override
166        public float getUnscaled(final int index) {
167                return this.byteBuffer[index];
168        }
169
170        /**
171         * {@inheritDoc}
172         * 
173         * @see org.openimaj.audio.samples.SampleBuffer#set(int, float)
174         */
175        @Override
176        public void set(final int index, float sample) {
177                if (sample > Byte.MAX_VALUE)
178                        sample = Byte.MAX_VALUE;
179                if (sample < Byte.MIN_VALUE)
180                        sample = Byte.MIN_VALUE;
181
182                this.byteBuffer[index] = (byte) (sample / SampleBuffer8Bit.SAMPLE_SCALAR);
183        }
184
185        /**
186         * {@inheritDoc}
187         * 
188         * @see org.openimaj.audio.samples.SampleBuffer#size()
189         */
190        @Override
191        public int size() {
192                return this.byteBuffer.length;
193        }
194
195        /**
196         * {@inheritDoc}
197         * 
198         * @see org.openimaj.audio.samples.SampleBuffer#getFormat()
199         */
200        @Override
201        public AudioFormat getFormat() {
202                return this.format;
203        }
204
205        /**
206         * {@inheritDoc}
207         * 
208         * @see org.openimaj.audio.samples.SampleBuffer#setFormat(org.openimaj.audio.AudioFormat)
209         */
210        @Override
211        public void setFormat(final AudioFormat af) {
212                this.format = af;
213        }
214
215        /**
216         * {@inheritDoc}
217         * 
218         * @see org.openimaj.audio.samples.SampleBuffer#asDoubleArray()
219         */
220        @Override
221        public double[] asDoubleArray() {
222                final double[] d = new double[this.size()];
223                for (int i = 0; i < this.size(); i++)
224                        d[i] = this.get(i);
225                return d;
226        }
227
228        /**
229         * {@inheritDoc}
230         * 
231         * @see org.openimaj.audio.samples.SampleBuffer#asDoubleChannelArray()
232         */
233        @Override
234        public double[][] asDoubleChannelArray() {
235                final int nc = this.format.getNumChannels();
236                final double[][] s = new double[nc][this.size() / nc];
237                for (int c = 0; c < nc; c++)
238                        for (int sa = 0; sa < this.size() / nc; sa++)
239                                s[c][sa] = this.get(sa * nc + c);
240                return s;
241        }
242
243        /**
244         * {@inheritDoc}
245         * 
246         * @see org.openimaj.audio.samples.SampleBuffer#asDoubleArray()
247         */
248        @Override
249        public float[] asFloatArray() {
250                final float[] d = new float[this.size()];
251                for (int i = 0; i < this.size(); i++)
252                        d[i] = this.get(i);
253                return d;
254        }
255
256        /**
257         * {@inheritDoc}
258         * 
259         * @see org.openimaj.audio.samples.SampleBuffer#asDoubleChannelArray()
260         */
261        @Override
262        public float[][] asFloatChannelArray() {
263                final int nc = this.format.getNumChannels();
264                final float[][] s = new float[nc][this.size() / nc];
265                for (int c = 0; c < nc; c++)
266                        for (int sa = 0; sa < this.size() / nc; sa++)
267                                s[c][sa] = this.get(sa * nc + c);
268                return s;
269        }
270
271        /**
272         * {@inheritDoc}
273         * 
274         * @see java.lang.Iterable#iterator()
275         */
276        @Override
277        public Iterator<Float> iterator() {
278                this.iteratorCount = 0;
279                return this;
280        }
281
282        /**
283         * {@inheritDoc}
284         * 
285         * @see java.util.Iterator#hasNext()
286         */
287        @Override
288        public boolean hasNext() {
289                return this.iteratorCount < this.size();
290        }
291
292        /**
293         * {@inheritDoc}
294         * 
295         * @see java.util.Iterator#next()
296         */
297        @Override
298        public Float next() {
299                final float f = this.get(this.iteratorCount);
300                this.iteratorCount++;
301                return f;
302        }
303
304        /**
305         * {@inheritDoc}
306         * 
307         * @see java.util.Iterator#remove()
308         */
309        @Override
310        public void remove() {
311                throw new NotImplementedException("Cannot remove from 16bit sample buffer");
312        }
313
314        @Override
315        public AudioTimecode getStartTimecode() {
316                return this.timecode;
317        }
318}