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.conversion; 034 035import org.openimaj.audio.AudioFormat; 036import org.openimaj.audio.AudioStream; 037import org.openimaj.audio.SampleChunk; 038import org.openimaj.audio.processor.AudioProcessor; 039import org.openimaj.audio.samples.SampleBuffer; 040import org.openimaj.audio.samples.SampleBufferFactory; 041 042/** 043 * An audio processor that converts the bit-depth of an audio stream. 044 * The algorithm used to provide the conversion is enumerated within 045 * the {@link BitDepthConversionAlgorithm} enum, a public inner class 046 * within this class. The class supports chainable and direct processing, 047 * like all audio processors should. 048 * <p> 049 * To use the class on an audio stream, use something like the following: 050 * <p> 051 * <code><pre> 052 * BitDepthConverter bdc = new BitDepthConverter( 053 * audioStream, 054 * BitDepthConversionAlgorithm.NEAREST, 055 * new AudioFormat( 8, 44.1, 1 ) ); 056 * </pre></code> 057 * <p> 058 * The constructors take an output format which must match the audio format 059 * of the incoming stream in all respects other than the bit-depth. If the 060 * input and output formats differ, an {@link IllegalArgumentException} will 061 * be thrown. If the input and output formats are identical in every respect, 062 * the processor does nothing. 063 * <p> 064 * For the NEAREST algorithm, the conversion of the bit-depth is 065 * mainly provided through the {@link SampleBuffer} class. 066 * 067 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 068 * @created 18 Jun 2012 069 */ 070public class BitDepthConverter extends AudioProcessor 071{ 072 /** 073 * An enumerator of the different bit-depth conversion algorithms 074 * available. 075 * 076 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 077 * @created 18 Jun 2012 078 */ 079 public enum BitDepthConversionAlgorithm 080 { 081 /** 082 * Performs a basic nearest value rounding bit-depth conversion. It 083 * does this by utilising the sample buffer conversion routines. 084 */ 085 NEAREST 086 { 087 @Override 088 public SampleChunk process( final SampleChunk s, final AudioFormat output ) 089 { 090 final SampleBuffer sbin = s.getSampleBuffer(); 091 final SampleBuffer sbout = SampleBufferFactory.createSampleBuffer( 092 output, sbin.size() ); 093 sbout.setFormat( output ); 094 095 // The sample buffer will do the conversion 096 for( int i = 0; i < sbin.size(); i++ ) 097 sbout.set( i, sbin.get(i) ); 098 099 return sbout.getSampleChunk(); 100 } 101 }; 102 103 /** 104 * Process a sample chunk and output a sample chunk in the given 105 * output format. 106 * 107 * @param s The input sample chunk 108 * @param output The output format 109 * @return A resampled sample chunk. 110 */ 111 public abstract SampleChunk process( SampleChunk s, AudioFormat output ); 112 } 113 114 /** 115 * Bit depth conversion defaults to 116 * {@link BitDepthConversionAlgorithm#NEAREST} 117 */ 118 private BitDepthConversionAlgorithm bitDepthConverter = 119 BitDepthConversionAlgorithm.NEAREST; 120 121 /** The output format to which sample chunks will be converted */ 122 private AudioFormat outputFormat = null; 123 124 /** 125 * Default constructor that takes the input conversion 126 * @param converter The converter to use 127 * @param outputFormat The output format to convert to 128 */ 129 public BitDepthConverter( final BitDepthConversionAlgorithm converter, 130 final AudioFormat outputFormat ) 131 { 132 this.bitDepthConverter = converter; 133 this.outputFormat = outputFormat; 134 this.setFormat( outputFormat ); 135 } 136 137 /** 138 * Chainable constructor. 139 * 140 * @param as The audio stream to process 141 * @param converter The converter to use 142 * @param outputFormat The output format to convert to 143 */ 144 public BitDepthConverter( final AudioStream as, final BitDepthConversionAlgorithm converter, 145 final AudioFormat outputFormat ) 146 { 147 super( as ); 148 this.bitDepthConverter = converter; 149 this.outputFormat = outputFormat; 150 this.setFormat( outputFormat ); 151 } 152 153 /** 154 * {@inheritDoc} 155 * @see org.openimaj.audio.processor.AudioProcessor#process(org.openimaj.audio.SampleChunk) 156 */ 157 @Override 158 public SampleChunk process( final SampleChunk sample ) throws Exception 159 { 160 if( sample.getFormat().getSampleRateKHz() != this.outputFormat.getSampleRateKHz() ) 161 throw new IllegalArgumentException( "The sample rate of the " + 162 "output format is not the same as the sample chunk. Use a " + 163 "sample rate converter first before using the bit depth" + 164 "converter." ); 165 166 if( sample.getFormat().getNumChannels() != this.outputFormat.getNumChannels() ) 167 throw new IllegalArgumentException( "The number of channels in the " + 168 "output format is not the same as the sample chunk. Use a " + 169 "channel converter first before using the bit-depth " + 170 "converter." ); 171 172 if( sample.getFormat().getNBits() == this.outputFormat.getNBits() ) 173 return sample; 174 175 final SampleChunk sc = this.bitDepthConverter.process( sample, this.outputFormat ); 176 sc.setStartTimecode( sample.getStartTimecode() ); 177 return sc; 178 } 179}