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