All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openimaj.audio.SampleChunk Maven / Gradle / Ivy

Go to download

Core definitions of audio streams and samples/chunks. Also contains interfaces for processors for these basic types.

There is a newer version: 1.3.10
Show newest version
/**
 * Copyright (c) 2011, The University of Southampton and the individual contributors.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *   * 	Redistributions of source code must retain the above copyright notice,
 * 	this list of conditions and the following disclaimer.
 *
 *   *	Redistributions in binary form must reproduce the above copyright notice,
 * 	this list of conditions and the following disclaimer in the documentation
 * 	and/or other materials provided with the distribution.
 *
 *   *	Neither the name of the University of Southampton nor the names of its
 * 	contributors may be used to endorse or promote products derived from this
 * 	software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 *
 */
package org.openimaj.audio;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.openimaj.audio.samples.SampleBuffer;
import org.openimaj.audio.samples.SampleBufferFactory;
import org.openimaj.audio.timecode.AudioTimecode;

/**
 * Represents a chunk of an audio file and stores the raw audio data. The data
 * is unnormalised - that is, it is stored in this class in its original format
 * in the form of a byte array. This is for speed during audio playback.
 *
 * If you require normalised data (data as an integer array for example) use the
 * method {@link #getSamplesAsByteBuffer()} and use the {@link ByteBuffer}'s
 * methods asXXXBuffer (e.g. ByteBuffer#asShortBuffer) to get the samples in a
 * normalised form.
 *
 * @author David Dupplaw ([email protected])
 * @created 8 Jun 2011
 *
 */
public class SampleChunk extends Audio
{
	/** The samples in the chunk */
	private byte[] samples = new byte[1];

	/** The timecode of the start of the sample chunk */
	private AudioTimecode startTimecode = new AudioTimecode(0);

	/**
	 * Create a new SampleChunk buffer with the given audio format, but do not
	 * initialise the samples.
	 *
	 * @param af
	 *            The audio format of the samples
	 */
	public SampleChunk(final AudioFormat af)
	{
		this(new byte[1], af);
	}

	/**
	 * Create a new sample chunk using the given samples and the given audio
	 * format.
	 *
	 * @param samples
	 *            The samples to initialise with
	 * @param af
	 *            The audio format of the samples
	 */
	public SampleChunk(final byte[] samples, final AudioFormat af)
	{
		this.setSamples(samples);
		super.format = af;
	}

	/**
	 * Create a new sample chunk using the given samples and the given audio
	 * format.
	 *
	 * @param samples
	 *            The samples to initialise with
	 * @param af
	 *            The audio format of the samples
	 * @param tc
	 *            The audio timecode of these samples
	 */
	public SampleChunk(final byte[] samples, final AudioFormat af, final AudioTimecode tc)
	{
		this.setSamples(samples);
		this.startTimecode = tc;
		super.format = af;
	}

	/**
	 * Set the samples in this sample chunk.
	 *
	 * @param samples
	 *            the samples in this sample chunk.
	 */
	public void setSamples(final byte[] samples)
	{
		synchronized (this.samples)
		{
			this.samples = samples;
		}
	}

	/**
	 * Get the samples in this sample chunk
	 *
	 * @return the samples in this sample chunk
	 */
	public byte[] getSamples()
	{
		return this.samples;
	}

	/**
	 * Returns the number of samples in this sample chunk. If there are 128
	 * stereo samples, this method will return 256. That is, it does not
	 * normalise for the number of channels. However, it does normalise for the
	 * size of each sample. So if this is a 16-bit buffer of 256 bytes length,
	 * this method will return 128.
	 *
	 * @return the number of samples in this sample chunk.
	 */
	public int getNumberOfSamples()
	{
		return this.samples.length / (this.format.getNBits() / 8);
	}

	/**
	 * Returns a {@link ByteBuffer} that can be used to create views of the
	 * samples in the object. For example, to get short integers, you can get
	 * {@link #getSamplesAsByteBuffer()}.asShortBuffer()
	 *
	 * @return A {@link ByteBuffer}
	 */
	public ByteBuffer getSamplesAsByteBuffer()
	{
		if (this.samples == null)
			return null;

		ByteOrder bo = null;

		if (this.format.isBigEndian())
			bo = ByteOrder.BIG_ENDIAN;
		else
			bo = ByteOrder.LITTLE_ENDIAN;

		return ByteBuffer.wrap(this.samples).order(bo);
	}

	/**
	 * Returns an appropriate sample buffer for this sample chunk. If an
	 * appropriate sample buffer cannot be found, null will be returned. This
	 * will wrap the samples array from the underlying chunk, so you should only
	 * side-affect the samples if you're sure they will not be reused.
	 *
	 * @return An appropriate {@link SampleBuffer}
	 */
	public SampleBuffer getSampleBuffer()
	{
		return SampleBufferFactory.createSampleBuffer(this, this.format);
	}

	/**
	 * Set the timecode at the start of this audio chunk.
	 *
	 * @param startTimecode
	 *            the timecode at the start of the chunk.
	 */
	public void setStartTimecode(final AudioTimecode startTimecode)
	{
		this.startTimecode = startTimecode;
	}

	/**
	 * Get the timecode at the start of this audio chunk.
	 *
	 * @return the timecode at the start of the chunk.
	 */
	public AudioTimecode getStartTimecode()
	{
		return this.startTimecode;
	}

	/**
	 * Return a slice of data from the sample array. The indices are based on
	 * the samples in the byte array, not the bytes themselves.
	 * 

* The assumption is that samples are whole numbers of bytes. So, if the * sample size was 16-bits, then passing in 2 for the start index would * actually index the byte at index 4 in the underlying sample array. The * order of the bytes is unchanged. * * @param start * The start index of the sample. * @param length * The length of the samples get. * @return The sample slice as a new {@link SampleChunk} */ public SampleChunk getSampleSlice(final int start, final int length) { final int nBytesPerSample = this.format.getNBits() / 8; final int startSampleByteIndex = start * nBytesPerSample; final byte[] newSamples = new byte[length * nBytesPerSample]; synchronized (this.samples) { System.arraycopy(this.samples, startSampleByteIndex, newSamples, 0, length * nBytesPerSample); } final SampleChunk s = new SampleChunk(this.format); s.setSamples(newSamples); // Set the timestamp to the start of this new slice final double samplesPerChannelPerMillisec = this.format.getSampleRateKHz(); s.setStartTimecode(new AudioTimecode( this.getStartTimecode().getTimecodeInMilliseconds() + (long) (start / samplesPerChannelPerMillisec))); return s; } /** * Prepends the given samples to the front of this sample chunk. It is * expected that the given samples are in the same format as this sample * chunk; if they are not an exception is thrown. Side-affects this sample * chunk and will return a reference to this sample chunk. * * @param sample * the samples to add * @return This sample chunk with the bytes prepended */ public SampleChunk prepend(final SampleChunk sample) { // Check the sample formats are the same if (!sample.getFormat().equals(this.format)) throw new IllegalArgumentException("Sample types are not equivalent"); // Get the samples from the given chunk final byte[] x1 = sample.getSamplesAsByteBuffer().array(); // Create an array for the concatenated pair final byte[] newSamples = new byte[this.samples.length + x1.length]; // Loop through adding the new samples System.arraycopy(x1, 0, newSamples, 0, x1.length); synchronized (this.samples) { System.arraycopy(this.samples, 0, newSamples, x1.length, this.samples.length); } // Update this object this.samples = newSamples; this.setStartTimecode(sample.getStartTimecode().clone()); return this; } /** * Appends the given samples to the end of this sample chunk. It is expected * that the given samples are in the same format as this sample chunk; if * they are not an exception is thrown. Side-affects this sample chunk and * will return a reference to this sample chunk. * * @param sample * the samples to add * @return This sample chunk with the bytes appended */ public SampleChunk append(final SampleChunk sample) { // Check the sample formats are the same if (!sample.getFormat().equals(this.format)) throw new IllegalArgumentException("Sample types are not equivalent"); // Get the samples from the given chunk final byte[] x1 = sample.getSamplesAsByteBuffer().array(); // Create an array for the concatenated pair final byte[] newSamples = new byte[this.samples.length + x1.length]; synchronized (this.samples) { System.arraycopy(this.samples, 0, newSamples, 0, this.samples.length); } System.arraycopy(x1, 0, newSamples, this.samples.length, x1.length); // Update this object this.samples = newSamples; return this; } /** * {@inheritDoc} */ @Override public SampleChunk clone() { return new SampleChunk(this.samples.clone(), this.format.clone(), this.startTimecode == null ? new AudioTimecode( 0) : this.startTimecode.clone()); } /** * Pads the sample chunk to the given size with zeros. * * @param requiredSampleSetSize * The required sample size */ public void pad(final int requiredSampleSetSize) { final byte[] samples = new byte[requiredSampleSetSize * (this.format.getNBits() / 8)]; System.arraycopy(this.samples, 0, samples, 0, this.samples.length); this.samples = samples; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy