1 /* 2 * Copyright (c) 2007, 2018, 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.File; 29 import java.io.FileNotFoundException; 30 import java.io.IOException; 31 import java.io.OutputStream; 32 import java.io.RandomAccessFile; 33 34 /** 35 * Resource Interchange File Format (RIFF) stream encoder. 36 * 37 * @author Karl Helgason 38 */ 39 public final class RIFFWriter extends OutputStream { 40 41 private interface RandomAccessWriter { 42 seek(long chunksizepointer)43 void seek(long chunksizepointer) throws IOException; 44 getPointer()45 long getPointer() throws IOException; 46 close()47 void close() throws IOException; 48 write(int b)49 void write(int b) throws IOException; 50 write(byte[] b, int off, int len)51 void write(byte[] b, int off, int len) throws IOException; 52 write(byte[] bytes)53 void write(byte[] bytes) throws IOException; 54 length()55 long length() throws IOException; 56 setLength(long i)57 void setLength(long i) throws IOException; 58 } 59 60 private static class RandomAccessFileWriter implements RandomAccessWriter { 61 62 RandomAccessFile raf; 63 RandomAccessFileWriter(File file)64 RandomAccessFileWriter(File file) throws FileNotFoundException { 65 this.raf = new RandomAccessFile(file, "rw"); 66 } 67 RandomAccessFileWriter(String name)68 RandomAccessFileWriter(String name) throws FileNotFoundException { 69 this.raf = new RandomAccessFile(name, "rw"); 70 } 71 72 @Override seek(long chunksizepointer)73 public void seek(long chunksizepointer) throws IOException { 74 raf.seek(chunksizepointer); 75 } 76 77 @Override getPointer()78 public long getPointer() throws IOException { 79 return raf.getFilePointer(); 80 } 81 82 @Override close()83 public void close() throws IOException { 84 raf.close(); 85 } 86 87 @Override write(int b)88 public void write(int b) throws IOException { 89 raf.write(b); 90 } 91 92 @Override write(byte[] b, int off, int len)93 public void write(byte[] b, int off, int len) throws IOException { 94 raf.write(b, off, len); 95 } 96 97 @Override write(byte[] bytes)98 public void write(byte[] bytes) throws IOException { 99 raf.write(bytes); 100 } 101 102 @Override length()103 public long length() throws IOException { 104 return raf.length(); 105 } 106 107 @Override setLength(long i)108 public void setLength(long i) throws IOException { 109 raf.setLength(i); 110 } 111 } 112 113 private static class RandomAccessByteWriter implements RandomAccessWriter { 114 115 byte[] buff = new byte[32]; 116 int length = 0; 117 int pos = 0; 118 byte[] s; 119 final OutputStream stream; 120 RandomAccessByteWriter(OutputStream stream)121 RandomAccessByteWriter(OutputStream stream) { 122 this.stream = stream; 123 } 124 125 @Override seek(long chunksizepointer)126 public void seek(long chunksizepointer) throws IOException { 127 pos = (int) chunksizepointer; 128 } 129 130 @Override getPointer()131 public long getPointer() throws IOException { 132 return pos; 133 } 134 135 @Override close()136 public void close() throws IOException { 137 stream.write(buff, 0, length); 138 stream.close(); 139 } 140 141 @Override write(int b)142 public void write(int b) throws IOException { 143 if (s == null) 144 s = new byte[1]; 145 s[0] = (byte)b; 146 write(s, 0, 1); 147 } 148 149 @Override write(byte[] b, int off, int len)150 public void write(byte[] b, int off, int len) throws IOException { 151 int newsize = pos + len; 152 if (newsize > length) 153 setLength(newsize); 154 int end = off + len; 155 for (int i = off; i < end; i++) { 156 buff[pos++] = b[i]; 157 } 158 } 159 160 @Override write(byte[] bytes)161 public void write(byte[] bytes) throws IOException { 162 write(bytes, 0, bytes.length); 163 } 164 165 @Override length()166 public long length() throws IOException { 167 return length; 168 } 169 170 @Override setLength(long i)171 public void setLength(long i) throws IOException { 172 length = (int) i; 173 if (length > buff.length) { 174 int newlen = Math.max(buff.length << 1, length); 175 byte[] newbuff = new byte[newlen]; 176 System.arraycopy(buff, 0, newbuff, 0, buff.length); 177 buff = newbuff; 178 } 179 } 180 } 181 private int chunktype = 0; // 0=RIFF, 1=LIST; 2=CHUNK 182 private RandomAccessWriter raf; 183 private final long chunksizepointer; 184 private final long startpointer; 185 private RIFFWriter childchunk = null; 186 private boolean open = true; 187 private boolean writeoverride = false; 188 RIFFWriter(String name, String format)189 public RIFFWriter(String name, String format) throws IOException { 190 this(new RandomAccessFileWriter(name), format, 0); 191 } 192 RIFFWriter(File file, String format)193 public RIFFWriter(File file, String format) throws IOException { 194 this(new RandomAccessFileWriter(file), format, 0); 195 } 196 RIFFWriter(OutputStream stream, String format)197 public RIFFWriter(OutputStream stream, String format) throws IOException { 198 this(new RandomAccessByteWriter(stream), format, 0); 199 } 200 RIFFWriter(RandomAccessWriter raf, String format, int chunktype)201 private RIFFWriter(RandomAccessWriter raf, String format, int chunktype) 202 throws IOException { 203 if (chunktype == 0) 204 if (raf.length() != 0) 205 raf.setLength(0); 206 this.raf = raf; 207 if (raf.getPointer() % 2 != 0) 208 raf.write(0); 209 210 if (chunktype == 0) 211 raf.write("RIFF".getBytes("ascii")); 212 else if (chunktype == 1) 213 raf.write("LIST".getBytes("ascii")); 214 else 215 raf.write((format + " ").substring(0, 4).getBytes("ascii")); 216 217 chunksizepointer = raf.getPointer(); 218 this.chunktype = 2; 219 writeUnsignedInt(0); 220 this.chunktype = chunktype; 221 startpointer = raf.getPointer(); 222 if (chunktype != 2) 223 raf.write((format + " ").substring(0, 4).getBytes("ascii")); 224 225 } 226 seek(long pos)227 public void seek(long pos) throws IOException { 228 raf.seek(pos); 229 } 230 getFilePointer()231 public long getFilePointer() throws IOException { 232 return raf.getPointer(); 233 } 234 setWriteOverride(boolean writeoverride)235 public void setWriteOverride(boolean writeoverride) { 236 this.writeoverride = writeoverride; 237 } 238 getWriteOverride()239 public boolean getWriteOverride() { 240 return writeoverride; 241 } 242 243 @Override close()244 public void close() throws IOException { 245 if (!open) 246 return; 247 if (childchunk != null) { 248 childchunk.close(); 249 childchunk = null; 250 } 251 252 int bakchunktype = chunktype; 253 long fpointer = raf.getPointer(); 254 raf.seek(chunksizepointer); 255 chunktype = 2; 256 writeUnsignedInt(fpointer - startpointer); 257 258 if (bakchunktype == 0) 259 raf.close(); 260 else 261 raf.seek(fpointer); 262 open = false; 263 raf = null; 264 } 265 266 @Override write(int b)267 public void write(int b) throws IOException { 268 if (!writeoverride) { 269 if (chunktype != 2) { 270 throw new IllegalArgumentException( 271 "Only chunks can write bytes!"); 272 } 273 if (childchunk != null) { 274 childchunk.close(); 275 childchunk = null; 276 } 277 } 278 raf.write(b); 279 } 280 281 @Override write(byte[] b, int off, int len)282 public void write(byte[] b, int off, int len) throws IOException { 283 if (!writeoverride) { 284 if (chunktype != 2) { 285 throw new IllegalArgumentException( 286 "Only chunks can write bytes!"); 287 } 288 if (childchunk != null) { 289 childchunk.close(); 290 childchunk = null; 291 } 292 } 293 raf.write(b, off, len); 294 } 295 writeList(String format)296 public RIFFWriter writeList(String format) throws IOException { 297 if (chunktype == 2) { 298 throw new IllegalArgumentException( 299 "Only LIST and RIFF can write lists!"); 300 } 301 if (childchunk != null) { 302 childchunk.close(); 303 childchunk = null; 304 } 305 childchunk = new RIFFWriter(this.raf, format, 1); 306 return childchunk; 307 } 308 writeChunk(String format)309 public RIFFWriter writeChunk(String format) throws IOException { 310 if (chunktype == 2) { 311 throw new IllegalArgumentException( 312 "Only LIST and RIFF can write chunks!"); 313 } 314 if (childchunk != null) { 315 childchunk.close(); 316 childchunk = null; 317 } 318 childchunk = new RIFFWriter(this.raf, format, 2); 319 return childchunk; 320 } 321 322 // Write ASCII chars to stream writeString(String string)323 public void writeString(String string) throws IOException { 324 byte[] buff = string.getBytes(); 325 write(buff); 326 } 327 328 // Write ASCII chars to stream writeString(String string, int len)329 public void writeString(String string, int len) throws IOException { 330 byte[] buff = string.getBytes(); 331 if (buff.length > len) 332 write(buff, 0, len); 333 else { 334 write(buff); 335 for (int i = buff.length; i < len; i++) 336 write(0); 337 } 338 } 339 340 // Write 8 bit signed integer to stream writeByte(int b)341 public void writeByte(int b) throws IOException { 342 write(b); 343 } 344 345 // Write 16 bit signed integer to stream writeShort(short b)346 public void writeShort(short b) throws IOException { 347 write((b >>> 0) & 0xFF); 348 write((b >>> 8) & 0xFF); 349 } 350 351 // Write 32 bit signed integer to stream writeInt(int b)352 public void writeInt(int b) throws IOException { 353 write((b >>> 0) & 0xFF); 354 write((b >>> 8) & 0xFF); 355 write((b >>> 16) & 0xFF); 356 write((b >>> 24) & 0xFF); 357 } 358 359 // Write 64 bit signed integer to stream writeLong(long b)360 public void writeLong(long b) throws IOException { 361 write((int) (b >>> 0) & 0xFF); 362 write((int) (b >>> 8) & 0xFF); 363 write((int) (b >>> 16) & 0xFF); 364 write((int) (b >>> 24) & 0xFF); 365 write((int) (b >>> 32) & 0xFF); 366 write((int) (b >>> 40) & 0xFF); 367 write((int) (b >>> 48) & 0xFF); 368 write((int) (b >>> 56) & 0xFF); 369 } 370 371 // Write 8 bit unsigned integer to stream writeUnsignedByte(int b)372 public void writeUnsignedByte(int b) throws IOException { 373 writeByte((byte) b); 374 } 375 376 // Write 16 bit unsigned integer to stream writeUnsignedShort(int b)377 public void writeUnsignedShort(int b) throws IOException { 378 writeShort((short) b); 379 } 380 381 // Write 32 bit unsigned integer to stream writeUnsignedInt(long b)382 public void writeUnsignedInt(long b) throws IOException { 383 writeInt((int) b); 384 } 385 } 386