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}