/* * FloatFile.java * (FScape) * * Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved. * * This software is published under the GNU General Public License v3+ * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de */ package de.sciss.fscape.io; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; public class FloatFile extends GenericFile { // -------- private variables -------- private byte buf[]; private int bufSize; // = buf.length private int bufOffset = 0; private int bufLength = 0; // tatsaechlicher Inhalt! private boolean flushed = true; private long bufPhysical = 0L; // FilePointer of Buffer-Start // -------- public methods -------- /** * Datei, die Float-Daten enthaelt bzw. enthalten soll, oeffnen * * @param f entsprechende Datei * @param mode MODE_INPUT zum Lesen, MODE_OUTPUT zum Schreiben */ public FloatFile( File f, int mode ) throws IOException { super( f, (mode & ~MODE_TYPEMASK) | MODE_FLOAT ); bufSize = 131072; // getDefaultBufSize() & ~3; buf = new byte[ bufSize ]; if( (mode & MODE_FILEMASK) == MODE_INPUT ) { // "read/write header" if( (this.mode & MODE_TYPEMASK) != MODE_FLOAT ) { throw new UnsupportedEncodingException( ERR_ILLEGALFILE ); } } else { writeInt( FLOAT_MAGIC ); } seekFloat( 0 ); } public FloatFile( String fname, int mode ) throws IOException { this( new File( fname ), mode ); } /** * Springt zu einem bestimmten Float * (nachfolgende readFloats() und writeFloats() sind betroffen) */ public void seekFloat( int offset ) throws IOException { long physical = (offset + 1) << 2; if( !flushed ) { flush(); } else { if( (bufPhysical <= physical) && (bufPhysical + (long) bufLength > physical) ) { bufOffset = (int) (physical - bufPhysical); // minimize loading physical = bufPhysical + (long) bufLength; } else { bufOffset = 0; bufLength = 0; } } seek( physical ); } public void flush() throws IOException { if( !flushed ) { write( buf, 0, bufLength ); // flush flushed = true; bufOffset = 0; bufLength = 0; bufPhysical = getFilePointer(); } } public long getSize() throws IOException { flush(); return( (length() >> 2) - 1 ); } /** * Liest Floats aus der Datei ein * auch bei Output-Files erlaubt * * @return Zahl der gelesenen Floats * (wenn kleiner als length, wurde der Rest automatisch mit Nullen gefuellt!) */ public int readFloats( float[] data, int offset, int length ) throws IOException { int dataEnd = offset + length; int i, num; flush(); loop: while( offset < dataEnd ) { if( bufOffset >= bufLength ) { bufPhysical = getFilePointer(); num = (int) Math.min( (long) bufSize, this.length() - bufPhysical ) & ~3; readFully( buf, 0, num ); bufOffset = 0; bufLength = num; } num = Math.min( dataEnd - offset, (bufLength - bufOffset) >> 2 ); if( num <= 0 ) break loop; for( i = 0; i < num; i++ ) { data[ offset++ ] = Float.intBitsToFloat( ((int) buf[ bufOffset++ ] << 24) | (((int) buf[ bufOffset++ ] & 0xFF) << 16) | (((int) buf[ bufOffset++ ] & 0xFF) << 8) | ((int) buf[ bufOffset++ ] & 0xFF) ); } } // main loop num = length - dataEnd + offset; if( (num == 0) && (length > 0) ) throw new EOFException(); while( offset < dataEnd ) { data[ offset++ ] = 0.0f; // zero padding } return num; } // full array read public int readFloats( float[] data ) throws IOException { return readFloats( data, 0, data.length ); } /** * Liest Ints aus der Datei ein * auch bei Output-Files erlaubt * * @return Zahl der gelesenen Ints * (wenn kleiner als length, wurde der Rest automatisch mit Nullen gefuellt!) */ public int readInts( int[] data, int offset, int length ) throws IOException { int dataEnd = offset + length; int i, num; flush(); loop: while( offset < dataEnd ) { if( bufOffset >= bufLength ) { bufPhysical = getFilePointer(); num = (int) Math.min( (long) bufSize, this.length() - bufPhysical ) & ~3; readFully( buf, 0, num ); bufOffset = 0; bufLength = num; } num = Math.min( dataEnd - offset, (bufLength - bufOffset) >> 2 ); if( num <= 0 ) break loop; for( i = 0; i < num; i++ ) { data[ offset++ ] = ((int) buf[ bufOffset++ ] << 24) | (((int) buf[ bufOffset++ ] & 0xFF) << 16) | (((int) buf[ bufOffset++ ] & 0xFF) << 8) | ((int) buf[ bufOffset++ ] & 0xFF); } } // main loop num = length - dataEnd + offset; if( (num == 0) && (length > 0) ) throw new EOFException(); while( offset < dataEnd ) { data[ offset++ ] = 0; // zero padding } return num; } // full array read public int readInts( int[] data ) throws IOException { return readInts( data, 0, data.length ); } /** * Read an unbuffered Float * Switching between this and readFloats()/writeFloats() MUST be * intercepted by a seekFloat() to ensure that the buffer holds * no artifacts!!! */ public float readUnbufferedFloat() throws IOException { return readFloat(); } /** * Schreibt Floats in die Datei */ public void writeFloats( float[] data, int offset, int length ) throws IOException { int dataEnd = offset + length; int i, j, num; if( flushed ) { if( bufOffset != bufLength ) { // falls readFloats() noch Restpuffer erzeugt hat seek( bufPhysical + (long) bufOffset ); } bufOffset = 0; bufLength = 0; bufPhysical = getFilePointer(); } while( offset < dataEnd ) { if( bufLength >= bufSize ) { flush(); } flushed = false; num = Math.min( dataEnd - offset, (bufSize - bufLength) >> 2 ); for( i = 0; i < num; i++ ) { j = Float.floatToIntBits( data[ offset++ ]); buf[ bufLength++ ] = (byte) (j >> 24); buf[ bufLength++ ] = (byte) (j >> 16); buf[ bufLength++ ] = (byte) (j >> 8); buf[ bufLength++ ] = (byte) j; } } // main loop bufOffset = bufLength; } // full array write public void writeFloats( float[] data ) throws IOException { writeFloats( data, 0, data.length ); } /** * Schreibt Ints in die Datei */ public void writeInts( int[] data, int offset, int length ) throws IOException { int dataEnd = offset + length; int i, j, num; if( flushed ) { if( bufOffset != bufLength ) { // falls readInts() noch Restpuffer erzeugt hat seek( bufPhysical + (long) bufOffset ); } bufOffset = 0; bufLength = 0; bufPhysical = getFilePointer(); } while( offset < dataEnd ) { if( bufLength >= bufSize ) { flush(); } flushed = false; num = Math.min( dataEnd - offset, (bufSize - bufLength) >> 2 ); for( i = 0; i < num; i++ ) { j = data[ offset++ ]; buf[ bufLength++ ] = (byte) (j >> 24); buf[ bufLength++ ] = (byte) (j >> 16); buf[ bufLength++ ] = (byte) (j >> 8); buf[ bufLength++ ] = (byte) j; } } // main loop bufOffset = bufLength; } // full array write public void writeInts( int[] data ) throws IOException { writeInts( data, 0, data.length ); } /** * Datei schliessen */ public void close() throws IOException { int bufLenTmp = bufLength; this.bufLength = 0; byte bufTmp[] = buf; buf = null; if( !flushed ) { flushed = true; if( (bufTmp != null) && (bufLenTmp > 0) ) { // ...then flush buffer write( bufTmp, 0, bufLenTmp ); } } super.close(); } /** * Format string besorgen */ public String getFormat() throws IOException { return( "temporary file; "+getSize() + " 16-bit floating point numbers" ); } }