1 /* 2 * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.media.sound; 26 27 import java.io.BufferedInputStream; 28 import java.io.File; 29 import java.io.FileInputStream; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.net.URL; 33 import java.util.HashMap; 34 import java.util.Map; 35 36 import javax.sound.sampled.AudioFileFormat; 37 import javax.sound.sampled.AudioFormat; 38 import javax.sound.sampled.AudioInputStream; 39 import javax.sound.sampled.AudioSystem; 40 import javax.sound.sampled.UnsupportedAudioFileException; 41 import javax.sound.sampled.AudioFormat.Encoding; 42 import javax.sound.sampled.spi.AudioFileReader; 43 44 /** 45 * WAVE file reader for files using format WAVE_FORMAT_EXTENSIBLE (0xFFFE). 46 * 47 * @author Karl Helgason 48 */ 49 public final class WaveExtensibleFileReader extends AudioFileReader { 50 51 static private class GUID { 52 long i1; 53 54 int s1; 55 56 int s2; 57 58 int x1; 59 60 int x2; 61 62 int x3; 63 64 int x4; 65 66 int x5; 67 68 int x6; 69 70 int x7; 71 72 int x8; 73 74 private GUID() { 75 } 76 77 GUID(long i1, int s1, int s2, int x1, int x2, int x3, int x4, 78 int x5, int x6, int x7, int x8) { 79 this.i1 = i1; 80 this.s1 = s1; 81 this.s2 = s2; 82 this.x1 = x1; 83 this.x2 = x2; 84 this.x3 = x3; 85 this.x4 = x4; 86 this.x5 = x5; 87 this.x6 = x6; 88 this.x7 = x7; 89 this.x8 = x8; 90 } 91 92 public static GUID read(RIFFReader riff) throws IOException { 93 GUID d = new GUID(); 94 d.i1 = riff.readUnsignedInt(); 95 d.s1 = riff.readUnsignedShort(); 96 d.s2 = riff.readUnsignedShort(); 97 d.x1 = riff.readUnsignedByte(); 98 d.x2 = riff.readUnsignedByte(); 99 d.x3 = riff.readUnsignedByte(); 100 d.x4 = riff.readUnsignedByte(); 101 d.x5 = riff.readUnsignedByte(); 102 d.x6 = riff.readUnsignedByte(); 103 d.x7 = riff.readUnsignedByte(); 104 d.x8 = riff.readUnsignedByte(); 105 return d; 106 } 107 108 public int hashCode() { 109 return (int) i1; 110 } 111 112 public boolean equals(Object obj) { 113 if (!(obj instanceof GUID)) 114 return false; 115 GUID t = (GUID) obj; 116 if (i1 != t.i1) 117 return false; 118 if (s1 != t.s1) 119 return false; 120 if (s2 != t.s2) 121 return false; 122 if (x1 != t.x1) 123 return false; 124 if (x2 != t.x2) 125 return false; 126 if (x3 != t.x3) 127 return false; 128 if (x4 != t.x4) 129 return false; 130 if (x5 != t.x5) 131 return false; 144 "BL", 145 "BR", // 5.1 146 "FLC", "FLR", "BC", "SL", "SR", "TC", "TFL", "TFC", "TFR", "TBL", 147 "TBC", "TBR" }; 148 149 private static final String[] allchannelnames = { "w1", "w2", "w3", "w4", "w5", 150 "w6", "w7", "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", 151 "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", "w24", 152 "w25", "w26", "w27", "w28", "w29", "w30", "w31", "w32", "w33", 153 "w34", "w35", "w36", "w37", "w38", "w39", "w40", "w41", "w42", 154 "w43", "w44", "w45", "w46", "w47", "w48", "w49", "w50", "w51", 155 "w52", "w53", "w54", "w55", "w56", "w57", "w58", "w59", "w60", 156 "w61", "w62", "w63", "w64" }; 157 158 private static final GUID SUBTYPE_PCM = new GUID(0x00000001, 0x0000, 0x0010, 159 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); 160 161 private static final GUID SUBTYPE_IEEE_FLOAT = new GUID(0x00000003, 0x0000, 162 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); 163 164 private String decodeChannelMask(long channelmask) { 165 StringBuilder sb = new StringBuilder(); 166 long m = 1; 167 for (int i = 0; i < allchannelnames.length; i++) { 168 if ((channelmask & m) != 0L) { 169 if (i < channelnames.length) { 170 sb.append(channelnames[i]).append(' '); 171 } else { 172 sb.append(allchannelnames[i]).append(' '); 173 } 174 } 175 m *= 2L; 176 } 177 if (sb.length() == 0) 178 return null; 179 return sb.substring(0, sb.length() - 1); 180 181 } 182 183 public AudioFileFormat getAudioFileFormat(InputStream stream) 184 throws UnsupportedAudioFileException, IOException { 185 186 stream.mark(200); 187 AudioFileFormat format; 188 try { 189 format = internal_getAudioFileFormat(stream); 190 } finally { 191 stream.reset(); 192 } 193 return format; 194 } 195 196 private AudioFileFormat internal_getAudioFileFormat(InputStream stream) 197 throws UnsupportedAudioFileException, IOException { 198 199 RIFFReader riffiterator = new RIFFReader(stream); 200 if (!riffiterator.getFormat().equals("RIFF")) 201 throw new UnsupportedAudioFileException(); 202 if (!riffiterator.getType().equals("WAVE")) 203 throw new UnsupportedAudioFileException(); 204 205 boolean fmt_found = false; 206 boolean data_found = false; 207 208 int channels = 1; 209 long samplerate = 1; 210 // long framerate = 1; 211 int framesize = 1; 212 int bits = 1; 213 int validBitsPerSample = 1; 214 long channelMask = 0; 215 GUID subFormat = null; 216 227 channels = chunk.readUnsignedShort(); 228 samplerate = chunk.readUnsignedInt(); 229 /* framerate = */chunk.readUnsignedInt(); 230 framesize = chunk.readUnsignedShort(); 231 bits = chunk.readUnsignedShort(); 232 int cbSize = chunk.readUnsignedShort(); 233 if (cbSize != 22) 234 throw new UnsupportedAudioFileException(); 235 validBitsPerSample = chunk.readUnsignedShort(); 236 if (validBitsPerSample > bits) 237 throw new UnsupportedAudioFileException(); 238 channelMask = chunk.readUnsignedInt(); 239 subFormat = GUID.read(chunk); 240 241 } 242 if (chunk.getFormat().equals("data")) { 243 data_found = true; 244 break; 245 } 246 } 247 248 if (!fmt_found) 249 throw new UnsupportedAudioFileException(); 250 if (!data_found) 251 throw new UnsupportedAudioFileException(); 252 253 Map<String, Object> p = new HashMap<String, Object>(); 254 String s_channelmask = decodeChannelMask(channelMask); 255 if (s_channelmask != null) 256 p.put("channelOrder", s_channelmask); 257 if (channelMask != 0) 258 p.put("channelMask", channelMask); 259 // validBitsPerSample is only informational for PCM data, 260 // data is still encode according to SampleSizeInBits. 261 p.put("validBitsPerSample", validBitsPerSample); 262 263 AudioFormat audioformat = null; 264 if (subFormat.equals(SUBTYPE_PCM)) { 265 if (bits == 8) { 266 audioformat = new AudioFormat(Encoding.PCM_UNSIGNED, 267 samplerate, bits, channels, framesize, samplerate, 268 false, p); 269 } else { 270 audioformat = new AudioFormat(Encoding.PCM_SIGNED, samplerate, 271 bits, channels, framesize, samplerate, false, p); 272 } 273 } else if (subFormat.equals(SUBTYPE_IEEE_FLOAT)) { 274 audioformat = new AudioFormat(Encoding.PCM_FLOAT, 275 samplerate, bits, channels, framesize, samplerate, false, p); 276 } else 277 throw new UnsupportedAudioFileException(); 278 279 AudioFileFormat fileformat = new AudioFileFormat( 280 AudioFileFormat.Type.WAVE, audioformat, 281 AudioSystem.NOT_SPECIFIED); 282 return fileformat; 283 } 284 285 public AudioInputStream getAudioInputStream(InputStream stream) 286 throws UnsupportedAudioFileException, IOException { 287 288 AudioFileFormat format = getAudioFileFormat(stream); 289 RIFFReader riffiterator = new RIFFReader(stream); 290 if (!riffiterator.getFormat().equals("RIFF")) 291 throw new UnsupportedAudioFileException(); 292 if (!riffiterator.getType().equals("WAVE")) 293 throw new UnsupportedAudioFileException(); 294 while (riffiterator.hasNextChunk()) { 295 RIFFReader chunk = riffiterator.nextChunk(); 296 if (chunk.getFormat().equals("data")) { 297 return new AudioInputStream(chunk, format.getFormat(), chunk 298 .getSize()); 299 } 300 } 301 throw new UnsupportedAudioFileException(); 302 } 303 304 public AudioFileFormat getAudioFileFormat(URL url) 305 throws UnsupportedAudioFileException, IOException { 306 InputStream stream = url.openStream(); 307 AudioFileFormat format; 308 try { 309 format = getAudioFileFormat(new BufferedInputStream(stream)); 310 } finally { 311 stream.close(); 312 } 313 return format; 314 } 315 316 public AudioFileFormat getAudioFileFormat(File file) 317 throws UnsupportedAudioFileException, IOException { 318 InputStream stream = new FileInputStream(file); 319 AudioFileFormat format; 320 try { 321 format = getAudioFileFormat(new BufferedInputStream(stream)); 322 } finally { 323 stream.close(); 324 } 325 return format; 326 } 327 328 public AudioInputStream getAudioInputStream(URL url) 329 throws UnsupportedAudioFileException, IOException { 330 return getAudioInputStream(new BufferedInputStream(url.openStream())); 331 } 332 333 public AudioInputStream getAudioInputStream(File file) 334 throws UnsupportedAudioFileException, IOException { 335 return getAudioInputStream(new BufferedInputStream(new FileInputStream( 336 file))); 337 } 338 339 } | 1 /* 2 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.media.sound; 27 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.util.HashMap; 31 import java.util.Map; 32 33 import javax.sound.sampled.AudioFileFormat; 34 import javax.sound.sampled.AudioFormat; 35 import javax.sound.sampled.AudioFormat.Encoding; 36 import javax.sound.sampled.AudioInputStream; 37 import javax.sound.sampled.AudioSystem; 38 import javax.sound.sampled.UnsupportedAudioFileException; 39 40 /** 41 * WAVE file reader for files using format WAVE_FORMAT_EXTENSIBLE (0xFFFE). 42 * 43 * @author Karl Helgason 44 */ 45 public final class WaveExtensibleFileReader extends SunFileReader { 46 47 private static class GUID { 48 private long i1; 49 private int s1; 50 private int s2; 51 private int x1; 52 private int x2; 53 private int x3; 54 private int x4; 55 private int x5; 56 private int x6; 57 private int x7; 58 private int x8; 59 private GUID() { 60 } 61 62 GUID(long i1, int s1, int s2, int x1, int x2, int x3, int x4, 63 int x5, int x6, int x7, int x8) { 64 this.i1 = i1; 65 this.s1 = s1; 66 this.s2 = s2; 67 this.x1 = x1; 68 this.x2 = x2; 69 this.x3 = x3; 70 this.x4 = x4; 71 this.x5 = x5; 72 this.x6 = x6; 73 this.x7 = x7; 74 this.x8 = x8; 75 } 76 77 public static GUID read(RIFFReader riff) throws IOException { 78 GUID d = new GUID(); 79 d.i1 = riff.readUnsignedInt(); 80 d.s1 = riff.readUnsignedShort(); 81 d.s2 = riff.readUnsignedShort(); 82 d.x1 = riff.readUnsignedByte(); 83 d.x2 = riff.readUnsignedByte(); 84 d.x3 = riff.readUnsignedByte(); 85 d.x4 = riff.readUnsignedByte(); 86 d.x5 = riff.readUnsignedByte(); 87 d.x6 = riff.readUnsignedByte(); 88 d.x7 = riff.readUnsignedByte(); 89 d.x8 = riff.readUnsignedByte(); 90 return d; 91 } 92 93 @Override 94 public int hashCode() { 95 return (int) i1; 96 } 97 98 @Override 99 public boolean equals(Object obj) { 100 if (!(obj instanceof GUID)) 101 return false; 102 GUID t = (GUID) obj; 103 if (i1 != t.i1) 104 return false; 105 if (s1 != t.s1) 106 return false; 107 if (s2 != t.s2) 108 return false; 109 if (x1 != t.x1) 110 return false; 111 if (x2 != t.x2) 112 return false; 113 if (x3 != t.x3) 114 return false; 115 if (x4 != t.x4) 116 return false; 117 if (x5 != t.x5) 118 return false; 131 "BL", 132 "BR", // 5.1 133 "FLC", "FLR", "BC", "SL", "SR", "TC", "TFL", "TFC", "TFR", "TBL", 134 "TBC", "TBR" }; 135 136 private static final String[] allchannelnames = { "w1", "w2", "w3", "w4", "w5", 137 "w6", "w7", "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", 138 "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", "w24", 139 "w25", "w26", "w27", "w28", "w29", "w30", "w31", "w32", "w33", 140 "w34", "w35", "w36", "w37", "w38", "w39", "w40", "w41", "w42", 141 "w43", "w44", "w45", "w46", "w47", "w48", "w49", "w50", "w51", 142 "w52", "w53", "w54", "w55", "w56", "w57", "w58", "w59", "w60", 143 "w61", "w62", "w63", "w64" }; 144 145 private static final GUID SUBTYPE_PCM = new GUID(0x00000001, 0x0000, 0x0010, 146 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); 147 148 private static final GUID SUBTYPE_IEEE_FLOAT = new GUID(0x00000003, 0x0000, 149 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); 150 151 private static String decodeChannelMask(long channelmask) { 152 StringBuilder sb = new StringBuilder(); 153 long m = 1; 154 for (int i = 0; i < allchannelnames.length; i++) { 155 if ((channelmask & m) != 0L) { 156 if (i < channelnames.length) { 157 sb.append(channelnames[i]).append(' '); 158 } else { 159 sb.append(allchannelnames[i]).append(' '); 160 } 161 } 162 m *= 2L; 163 } 164 if (sb.length() == 0) 165 return null; 166 return sb.substring(0, sb.length() - 1); 167 168 } 169 170 @Override 171 AudioFileFormat getAudioFileFormatImpl(final InputStream stream) 172 throws UnsupportedAudioFileException, IOException { 173 174 RIFFReader riffiterator = new RIFFReader(stream); 175 if (!riffiterator.getFormat().equals("RIFF")) 176 throw new UnsupportedAudioFileException(); 177 if (!riffiterator.getType().equals("WAVE")) 178 throw new UnsupportedAudioFileException(); 179 180 boolean fmt_found = false; 181 boolean data_found = false; 182 183 int channels = 1; 184 long samplerate = 1; 185 // long framerate = 1; 186 int framesize = 1; 187 int bits = 1; 188 int validBitsPerSample = 1; 189 long channelMask = 0; 190 GUID subFormat = null; 191 202 channels = chunk.readUnsignedShort(); 203 samplerate = chunk.readUnsignedInt(); 204 /* framerate = */chunk.readUnsignedInt(); 205 framesize = chunk.readUnsignedShort(); 206 bits = chunk.readUnsignedShort(); 207 int cbSize = chunk.readUnsignedShort(); 208 if (cbSize != 22) 209 throw new UnsupportedAudioFileException(); 210 validBitsPerSample = chunk.readUnsignedShort(); 211 if (validBitsPerSample > bits) 212 throw new UnsupportedAudioFileException(); 213 channelMask = chunk.readUnsignedInt(); 214 subFormat = GUID.read(chunk); 215 216 } 217 if (chunk.getFormat().equals("data")) { 218 data_found = true; 219 break; 220 } 221 } 222 if (!fmt_found || !data_found) { 223 throw new UnsupportedAudioFileException(); 224 } 225 Map<String, Object> p = new HashMap<String, Object>(); 226 String s_channelmask = decodeChannelMask(channelMask); 227 if (s_channelmask != null) 228 p.put("channelOrder", s_channelmask); 229 if (channelMask != 0) 230 p.put("channelMask", channelMask); 231 // validBitsPerSample is only informational for PCM data, 232 // data is still encode according to SampleSizeInBits. 233 p.put("validBitsPerSample", validBitsPerSample); 234 235 AudioFormat audioformat = null; 236 if (subFormat.equals(SUBTYPE_PCM)) { 237 if (bits == 8) { 238 audioformat = new AudioFormat(Encoding.PCM_UNSIGNED, 239 samplerate, bits, channels, framesize, samplerate, 240 false, p); 241 } else { 242 audioformat = new AudioFormat(Encoding.PCM_SIGNED, samplerate, 243 bits, channels, framesize, samplerate, false, p); 244 } 245 } else if (subFormat.equals(SUBTYPE_IEEE_FLOAT)) { 246 audioformat = new AudioFormat(Encoding.PCM_FLOAT, 247 samplerate, bits, channels, framesize, samplerate, false, p); 248 } else { 249 throw new UnsupportedAudioFileException(); 250 } 251 return new AudioFileFormat(AudioFileFormat.Type.WAVE, audioformat, 252 AudioSystem.NOT_SPECIFIED); 253 } 254 255 @Override 256 public AudioInputStream getAudioInputStream(final InputStream stream) 257 throws UnsupportedAudioFileException, IOException { 258 259 AudioFileFormat format = getAudioFileFormat(stream); 260 // we've got everything, the stream is supported and it is at the 261 // beginning of the header, so find the data chunk again and return an 262 // AudioInputStream 263 RIFFReader riffiterator = new RIFFReader(stream); 264 while (riffiterator.hasNextChunk()) { 265 RIFFReader chunk = riffiterator.nextChunk(); 266 if (chunk.getFormat().equals("data")) { 267 return new AudioInputStream(chunk, format.getFormat(), chunk 268 .getSize()); 269 } 270 } 271 throw new UnsupportedAudioFileException(); 272 } 273 } |