Mercurial > hg > release > icedtea6-1.2
view overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java @ 873:a1832ea486e9
Import Gervill fixes from CVS and add tests.
2008-05-23 Mark Wielaard <mark@klomp.org>
* overlays/openjdk/jdk/src/share/classes/com/sun/media/sound:
Import Gervill fixes from CVS. See CHANGES.txt.
Added: Software Mixing Mixer.
Fix: AudioFloatFormatConverter.getTargetFormats()
* overlays/openjdk/jdk/test/com/sun/media/sound: Imported all tests.
author | Mark Wielaard <mark@klomp.org> |
---|---|
date | Sun, 25 May 2008 21:21:11 +0200 |
parents | 30c7450d05dc |
children |
line wrap: on
line source
/* * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.sun.media.sound; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioFormat.Encoding; import javax.sound.sampled.spi.FormatConversionProvider; /** * This class is used to convert between 8,16,24,32 bit signed/unsigned * big/litle endian fixed/floating stereo/mono/multi-channel audio streams * and perform sample-rate conversion if needed. * * @version %I%, %E% * @author Karl Helgason */ public class AudioFloatFormatConverter extends FormatConversionProvider { private class AudioFloatFormatConverterInputStream extends InputStream { private AudioFloatConverter converter; private AudioFloatInputStream stream; private float[] readfloatbuffer; private int fsize = 0; public AudioFloatFormatConverterInputStream(AudioFormat targetFormat, AudioFloatInputStream stream) { this.stream = stream; converter = AudioFloatConverter.getConverter(targetFormat); fsize = ((targetFormat.getSampleSizeInBits()+7) / 8); } public int read() throws IOException { byte[] b = new byte[1]; int ret = read(b); if(ret < 0) return ret; return b[0] & 0xFF; } public int read(byte[] b, int off, int len) throws IOException { int flen = len / fsize; if(readfloatbuffer == null || readfloatbuffer.length < flen) readfloatbuffer = new float[flen]; int ret = stream.read(readfloatbuffer, 0, flen); if(ret < 0) return ret; converter.toByteArray(readfloatbuffer, 0, ret, b, off); return ret*fsize; } public int available() throws IOException { int ret = stream.available(); if(ret < 0) return ret; return ret * fsize; } public void close() throws IOException { stream.close(); } public synchronized void mark(int readlimit) { stream.mark(readlimit*fsize); } public boolean markSupported() { return stream.markSupported(); } public synchronized void reset() throws IOException { stream.reset(); } public long skip(long n) throws IOException { long ret = stream.skip(n/fsize); if(ret < 0) return ret; return ret*fsize; } } private class AudioFloatInputStreamChannelMixer extends AudioFloatInputStream { private int targetChannels; private int sourceChannels; private AudioFloatInputStream ais; private AudioFormat targetFormat; private float[] conversion_buffer; public AudioFloatInputStreamChannelMixer(AudioFloatInputStream ais, int targetChannels) { this.sourceChannels = ais.getFormat().getChannels(); this.targetChannels = targetChannels; this.ais = ais; AudioFormat format = ais.getFormat(); targetFormat = new AudioFormat(format.getEncoding(), format.getSampleRate(), format.getSampleSizeInBits(), targetChannels, (format.getFrameSize()/sourceChannels)*targetChannels, format.getFrameRate(), format.isBigEndian()); } public int available() throws IOException { return (ais.available()/sourceChannels)*targetChannels; } public void close() throws IOException { ais.close(); } public AudioFormat getFormat() { return targetFormat; } public long getFrameLength() { return ais.getFrameLength(); } public void mark(int readlimit) { ais.mark((readlimit/targetChannels)*sourceChannels); } public boolean markSupported() { return ais.markSupported(); } public int read(float[] b, int off, int len) throws IOException { int len2 = (len / targetChannels)*sourceChannels; if(conversion_buffer == null || conversion_buffer.length < len2) conversion_buffer = new float[len2]; int ret = ais.read(conversion_buffer, 0, len2); if(ret < 0) return ret; if(sourceChannels == 1) { int cs = targetChannels; for (int c = 0; c < targetChannels; c++) { for (int i = 0, ix = off+c; i < len2; i++, ix+=cs) { b[ix] = conversion_buffer[i];; } } } else if(targetChannels == 1) { int cs = sourceChannels; for (int i = 0, ix = off; i < len2; i+=cs, ix++) { b[ix] = conversion_buffer[i]; } for (int c = 1; c < sourceChannels; c++) { for (int i = c, ix = off; i < len2; i+=cs, ix++) { b[ix] += conversion_buffer[i];; } } float vol = 1f / ((float)sourceChannels); for (int i = 0, ix = off; i < len2; i+=cs, ix++) { b[ix] *= vol; } } else { int minChannels = Math.min(sourceChannels, targetChannels); int off_len = off + len; int ct = targetChannels; int cs = sourceChannels; for (int c = 0; c < minChannels; c++) { for (int i = off+c, ix = c; i < off_len; i+=ct, ix+=cs) { b[i] = conversion_buffer[ix]; } } for (int c = minChannels; c < targetChannels; c++) { for (int i = off+c; i < off_len; i+=ct) { b[i] = 0; } } } return (ret / sourceChannels)*targetChannels; } public void reset() throws IOException { ais.reset(); } public long skip(long len) throws IOException { long ret = ais.skip((len / targetChannels)*sourceChannels); if(ret < 0) return ret; return (ret / sourceChannels)*targetChannels; } } private class AudioFloatInputStreamResampler extends AudioFloatInputStream { private AudioFloatInputStream ais; private AudioFormat targetFormat; private float[] skipbuffer; private SoftAbstractResampler resampler; private float[] pitch = new float[1]; private float[] ibuffer2; private float[][] ibuffer; private float ibuffer_index = 0; private int ibuffer_len = 0; private int nrofchannels = 0; private float[][] cbuffer; private int buffer_len = 512; private int pad; private int pad2; private float[] ix = new float[1]; private int[] ox = new int[1]; private float[][] mark_ibuffer = null; private float mark_ibuffer_index = 0; private int mark_ibuffer_len = 0; public AudioFloatInputStreamResampler(AudioFloatInputStream ais, AudioFormat format) { this.ais = ais; AudioFormat sourceFormat = ais.getFormat(); targetFormat = new AudioFormat(sourceFormat.getEncoding(), format.getSampleRate(), sourceFormat.getSampleSizeInBits(), sourceFormat.getChannels(), sourceFormat.getFrameSize(), format.getSampleRate(), sourceFormat.isBigEndian()); nrofchannels = targetFormat.getChannels(); Object interpolation = format.getProperty("interpolation"); if(interpolation != null && (interpolation instanceof String)) { String resamplerType = (String)interpolation; if(resamplerType.equalsIgnoreCase("point")) this.resampler = new SoftPointResampler(); if(resamplerType.equalsIgnoreCase("linear")) this.resampler = new SoftLinearResampler2(); if(resamplerType.equalsIgnoreCase("linear1")) this.resampler = new SoftLinearResampler(); if(resamplerType.equalsIgnoreCase("linear2")) this.resampler = new SoftLinearResampler2(); if(resamplerType.equalsIgnoreCase("cubic")) this.resampler = new SoftCubicResampler(); if(resamplerType.equalsIgnoreCase("lanczos")) this.resampler = new SoftLanczosResampler(); if(resamplerType.equalsIgnoreCase("sinc")) this.resampler = new SoftSincResampler(); } if(resampler == null) resampler = new SoftLinearResampler2(); // new SoftLinearResampler2(); pitch[0] = sourceFormat.getSampleRate() / format.getSampleRate(); pad = resampler.getPadding(); pad2 = pad*2; ibuffer = new float[nrofchannels][buffer_len + pad2]; ibuffer2 = new float[nrofchannels*buffer_len]; ibuffer_index = buffer_len + pad; ibuffer_len = buffer_len; } public int available() throws IOException { return 0; } public void close() throws IOException { ais.close(); } public AudioFormat getFormat() { return targetFormat; } public long getFrameLength() { return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength(); } public void mark(int readlimit) { ais.mark((int)(readlimit*pitch[0])); mark_ibuffer_index = ibuffer_index; mark_ibuffer_len = ibuffer_len; if(mark_ibuffer == null) { mark_ibuffer = new float[ibuffer.length][ibuffer[0].length]; } for (int c = 0; c < ibuffer.length; c++) { float[] from = ibuffer[c] ; float[] to = mark_ibuffer[c] ; for (int i = 0; i < to.length; i++) { to[i] = from[i]; } } } public boolean markSupported() { return ais.markSupported(); } private void readNextBuffer() throws IOException { if(ibuffer_len == -1) return; for (int c = 0; c < nrofchannels; c++) { float[] buff = ibuffer[c] ; int buffer_len_pad = ibuffer_len+pad2; for (int i = ibuffer_len, ix = 0; i < buffer_len_pad; i++, ix++) { buff[ix] = buff[i]; } } ibuffer_index -= (ibuffer_len); ibuffer_len = ais.read(ibuffer2); if(ibuffer_len >= 0) { while(ibuffer_len < ibuffer2.length) { int ret = ais.read(ibuffer2, ibuffer_len, ibuffer2.length - ibuffer_len); if(ret == -1) break; ibuffer_len += ret; } Arrays.fill(ibuffer2, ibuffer_len, ibuffer2.length, 0); ibuffer_len /= nrofchannels; } else { Arrays.fill(ibuffer2, 0, ibuffer2.length, 0); } int ibuffer2_len = ibuffer2.length; for (int c = 0; c < nrofchannels; c++) { float[] buff = ibuffer[c] ; for (int i = c, ix = pad2; i < ibuffer2_len; i+=nrofchannels, ix++) { buff[ix] = ibuffer2[i]; } } } public int read(float[] b, int off, int len) throws IOException { if(cbuffer == null || cbuffer[0].length < len/nrofchannels) { cbuffer = new float[nrofchannels][len / nrofchannels]; } if(ibuffer_len == -1) return -1; if(len < 0) return 0; int remain = len/nrofchannels; int destPos = 0; int in_end = ibuffer_len; while(remain > 0) { if(ibuffer_len >= 0) { if(ibuffer_index >= (ibuffer_len+pad)) readNextBuffer(); in_end = ibuffer_len+pad; } if(ibuffer_len < 0) { in_end = pad2; if(ibuffer_index >= in_end) break; } if(ibuffer_index < 0) break; int preDestPos = destPos; for (int c = 0; c < nrofchannels; c++) { ix[0] = ibuffer_index; ox[0] = destPos; float[] buff = ibuffer[c] ; resampler.interpolate(buff, ix, in_end, pitch, 0, cbuffer[c], ox, len / nrofchannels); } ibuffer_index = ix[0]; destPos = ox[0]; remain -= destPos - preDestPos; } for (int c = 0; c < nrofchannels; c++) { int ix = 0; float[] buff = cbuffer[c] ; for (int i = c; i < b.length; i+=nrofchannels) { b[i] = buff[ix++]; } } return len - remain*nrofchannels; } public void reset() throws IOException { ais.reset(); if(mark_ibuffer == null) return; ibuffer_index = mark_ibuffer_index; ibuffer_len = mark_ibuffer_len; for (int c = 0; c < ibuffer.length; c++) { float[] from = mark_ibuffer[c] ; float[] to = ibuffer[c] ; for (int i = 0; i < to.length; i++) { to[i] = from[i]; } } } public long skip(long len) throws IOException { if(len > 0) return 0; if(skipbuffer == null) skipbuffer = new float[1024*targetFormat.getFrameSize()]; float[] l_skipbuffer = skipbuffer; long remain = len; while(remain > 0) { int ret = read(l_skipbuffer, 0, (int)Math.min(remain, skipbuffer.length)); if(ret < 0) { if(remain == len) return ret; break; } remain -= ret; } return len - remain; } } private Encoding[] formats = {Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED, AudioFloatConverter.PCM_FLOAT}; public AudioInputStream getAudioInputStream(Encoding targetEncoding, AudioInputStream sourceStream) { if(sourceStream.getFormat().getEncoding().equals(targetEncoding)) return sourceStream; AudioFormat format = sourceStream.getFormat(); int channels = format.getChannels(); Encoding encoding = targetEncoding; float samplerate = format.getSampleRate(); int bits = format.getSampleSizeInBits(); boolean bigendian = format.isBigEndian(); if(targetEncoding.equals(AudioFloatConverter.PCM_FLOAT)) bits = 32; AudioFormat targetFormat = new AudioFormat(encoding, samplerate, bits, channels, channels*bits/8, samplerate, bigendian); return getAudioInputStream(targetFormat, sourceStream); } public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream) { if(!isConversionSupported(targetFormat, sourceStream.getFormat())) throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetFormat.toString()); return getAudioInputStream(targetFormat, AudioFloatInputStream.getInputStream(sourceStream)); } public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioFloatInputStream sourceStream) { if(!isConversionSupported(targetFormat, sourceStream.getFormat())) throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetFormat.toString()); if(targetFormat.getChannels() != sourceStream.getFormat().getChannels()) sourceStream = new AudioFloatInputStreamChannelMixer(sourceStream, targetFormat.getChannels()); if(Math.abs(targetFormat.getSampleRate() - sourceStream.getFormat().getSampleRate()) > 0.000001) sourceStream = new AudioFloatInputStreamResampler(sourceStream, targetFormat); return new AudioInputStream( new AudioFloatFormatConverterInputStream(targetFormat, sourceStream), targetFormat, sourceStream.getFrameLength()); } public Encoding[] getSourceEncodings() { return formats; } public Encoding[] getTargetEncodings() { return formats; } public Encoding[] getTargetEncodings(AudioFormat sourceFormat) { if(AudioFloatConverter.getConverter(sourceFormat) == null) return new Encoding[0]; return formats; } public AudioFormat[] getTargetFormats(Encoding targetEncoding, AudioFormat sourceFormat) { if(AudioFloatConverter.getConverter(sourceFormat) == null) return new AudioFormat[0]; int channels = sourceFormat.getChannels(); ArrayList<AudioFormat> formats = new ArrayList<AudioFormat>(); if(targetEncoding.equals(Encoding.PCM_SIGNED)) formats.add(new AudioFormat(Encoding.PCM_SIGNED, AudioSystem.NOT_SPECIFIED, 8, channels, channels, AudioSystem.NOT_SPECIFIED, false)); if(targetEncoding.equals(Encoding.PCM_UNSIGNED)) formats.add(new AudioFormat(Encoding.PCM_UNSIGNED, AudioSystem.NOT_SPECIFIED, 8, channels, channels, AudioSystem.NOT_SPECIFIED, false)); for (int bits = 16; bits < 32; bits+=8) { if(targetEncoding.equals(Encoding.PCM_SIGNED)) { formats.add(new AudioFormat(Encoding.PCM_SIGNED, AudioSystem.NOT_SPECIFIED, bits, channels, channels*bits/8, AudioSystem.NOT_SPECIFIED, false)); formats.add(new AudioFormat(Encoding.PCM_SIGNED, AudioSystem.NOT_SPECIFIED, bits, channels, channels*bits/8, AudioSystem.NOT_SPECIFIED, true)); } if(targetEncoding.equals(Encoding.PCM_UNSIGNED)) { formats.add(new AudioFormat(Encoding.PCM_UNSIGNED, AudioSystem.NOT_SPECIFIED, bits, channels, channels*bits/8, AudioSystem.NOT_SPECIFIED, true)); formats.add(new AudioFormat(Encoding.PCM_UNSIGNED, AudioSystem.NOT_SPECIFIED, bits, channels, channels*bits/8, AudioSystem.NOT_SPECIFIED, false)); } } if(targetEncoding.equals(AudioFloatConverter.PCM_FLOAT)) { formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 32, channels, channels*4, AudioSystem.NOT_SPECIFIED, false)); formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 32, channels, channels*4, AudioSystem.NOT_SPECIFIED, true)); formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 64, channels, channels*8, AudioSystem.NOT_SPECIFIED, false)); formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 64, channels, channels*8, AudioSystem.NOT_SPECIFIED, true)); } return formats.toArray(new AudioFormat[formats.size()]); } public boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) { if(AudioFloatConverter.getConverter(sourceFormat) == null) return false; if(AudioFloatConverter.getConverter(targetFormat) == null) return false; if(sourceFormat.getChannels() <= 0) return false; if(targetFormat.getChannels() <= 0) return false; return true; } public boolean isConversionSupported(Encoding targetEncoding, AudioFormat sourceFormat) { if(AudioFloatConverter.getConverter(sourceFormat) == null) return false; for (int i = 0; i < formats.length; i++) { if(targetEncoding.equals(formats[i])) return true; } return false; } }