/*
* GenericFile.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
*
*
* Changelog:
* 07-Jan-05 added WAVE support; switched to AudioFile, killed SoundFile
*/
package de.sciss.fscape.io;
import de.sciss.fscape.spect.SpectralFile;
import de.sciss.io.AudioFile;
import de.sciss.io.AudioFileDescr;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class GenericFile
extends RandomAccessFile {
// -------- public variables --------
/**
* File will be an Input resp. Output Object
*/
public static final int MODE_INPUT = 0x00;
public static final int MODE_OUTPUT = 0x01;
public static final int MODE_FILEMASK = 0x0F;
/**
* Type specific
*/
public static final int MODE_GENERIC = 0x0000;
public static final int MODE_TIFF = 0x0010;
public static final int MODE_AIFF = 0x0020;
public static final int MODE_SND = 0x0030;
public static final int MODE_SPECT = 0x0040;
public static final int MODE_FLOAT = 0x0050;
public static final int MODE_MPEG = 0x0060;
public static final int MODE_MOV = 0x0070;
public static final int MODE_IRCAM = 0x0080;
public static final int MODE_WAVE = 0x0090;
public static final int MODE_WAVE64 = 0x00A0;
public static final int MODE_TYPEMASK = 0xFFF0;
public static final int TYPES_IMAGE[] = { MODE_TIFF };
public static final int TYPES_MOVIE[] = { MODE_MPEG, MODE_MOV };
public static final int TYPES_SOUND[] = { MODE_AIFF, MODE_SND, MODE_IRCAM, MODE_WAVE, MODE_WAVE64 };
public static final int TYPES_SPECT[] = { MODE_SPECT };
public static final int MODE_SPECIALMASK= 0xFF0000; // individuell AudioFile, ImageFile, ...
public int mode; // MODE_...
public static final String ERR_ILLEGALFILE = "Unsupported file format";
public static final String ERR_CORRUPTED = "File is corrupted";
public static final String ERR_MISSINGDATA = "Essential data missing";
public static final String ERR_UNSUPPORTED = "Unsupported encoding";
// -------- private variables --------
protected File file;
// First 4 bytes
protected static final int TIFF_MAC_MAGIC = 0x4D4D002A; // 'MM' Version 42
protected static final int TIFF_IBM_MAGIC = 0x4949002A; // 'II' Version 42
protected static final int FORM_MAGIC = 0x464F524D; // 'FORM'
protected static final int AIFF_MAGIC = 0x41494646; // 'AIFF'
protected static final int AIFC_MAGIC = 0x41494643; // 'AIFC'
protected static final int SND_MAGIC = 0x2E736E64; // '.snd'
protected static final int CSA_MAGIC = 517730;
protected static final int SHA_MAGIC = 0x45726265; // 'Erbe'
protected static final int FLOAT_MAGIC = 0x46536346; // 'FScF' (FScape Float)
protected static final int MPEG_MAGIC1 = 0x000001B3;
protected static final int MPEG_MAGIC2 = 0x000001BA;
protected static final int MOV_MAGIC = 0x6D6F6F76; // 'moov'
protected static final int IRCAM_MAGIC = 0x0001A364;
protected static final int RIFF_MAGIC = 0x52494646; // 'RIFF'
protected static final int WAVE_MAGIC = 0x57415645; // 'WAVE'
// index corresponds to MODE_TYPE >> TYPESHIFT
protected static final int TYPESHIFT = 4;
// "????" "TIFF" "AIFF" "NeXT" "DATA" "FScF" "MPEG" "MooV" "IRCM" "WAVE" "WAVE"
protected static final int FILETYPE[] = { 0x3F3F3F3F, 0x54494646, 0x41494646, 0x4E655854, 0x44415441, 0x46536346, 0x4D504547, 0x4D6F6F56, 0x4952434D, 0x57415645, 0x57415645 };
protected static final String FILETYPESTR[] = { "????" , "TIFF" , "AIFF" , "NeXT" , "DATA" , "FScF" , "MPEG" , "MooV" , "IRCM", "WAVE", "WAVE" };
protected static final String EXTSTR[] = { ".???", ".tif", ".aif", ".snd", ".spc", ".dat", ".mpg",
".mov", ".irc", ".wav", ".w64" };
protected static final String TYPEDESCR[] = { "Unknown type", "Image: TIFF", "Waveform: AIFF",
"Waveform: Snd", "Spectral: SndHack/FScape",
"Float stream", "Movie: MPEG", "Movie: QuickTime",
"Waveform: IRCAM", "Waveform: WAVE", "Waveform: Wave64" };
// array-index = MainPrefs.KEY_IOBUFSIZE
// private static final int[] BUFSIZE = { 8192, 32768, 131072 };
// -------- public methods --------
/**
* Datei, die Daten enthaelt bzw. enthalten soll, oeffnen
*
* @param f entsprechende Datei
* @param mode MODE_INPUT zum Lesen, MODE_OUTPUT zum Schreiben
*/
public GenericFile( File f, int mode )
throws IOException
{
super( prepareOpen( f, mode ), ((mode & MODE_FILEMASK) == MODE_OUTPUT) ? "rw" : "r" );
this.mode = mode;
this.file = f;
if( (mode & MODE_FILEMASK) == MODE_INPUT ) {
retrieveType(); // check if it's TIFF, AIFF etc. and modifiy 'mode' field
}
}
public GenericFile( String fname, int mode )
throws IOException
{
this( new File( fname ), mode );
}
/**
* Datei schliessen
*/
public void close()
throws IOException
{
super.close();
if( (mode & MODE_FILEMASK) == MODE_OUTPUT ) {
// try {
// String type = getFileTypeStr();
// String value = Main.getPrefs().getProperty( MainPrefs.KEY_CREATOR + type );
// XXX causes trouble: AIFF files appear to be QuickTime Movies???
// MRJAdapter.setFileCreatorAndType( file.getAbsoluteFile(), type, "FSc " ); // Constants.OSTypeFScape );
// } catch( IOException e2 ) {} // Filetype nicht essentiell
}
}
/**
* Datei aufraeumen; ggf. schliessen
*/
public void cleanUp()
{
try {
close();
}
catch( IOException e ) {} // jetzt nicht mehr
}
public String getTypeDescr()
{
return getTypeDescr( mode );
}
public static String getTypeDescr( int mode )
{
return( TYPEDESCR[ (mode & MODE_TYPEMASK) >> TYPESHIFT ]);
}
public int getFileType()
{
return getFileType( mode );
}
public String getFileTypeStr()
{
return getFileTypeStr( mode );
}
public static int getFileType( int mode )
{
return( FILETYPE[ (mode & MODE_TYPEMASK) >> TYPESHIFT ]);
}
public static String getFileTypeStr( int mode )
{
return( FILETYPESTR[ (mode & MODE_TYPEMASK) >> TYPESHIFT ]);
}
public static String getExtStr( int mode )
{
return( EXTSTR[ (mode & MODE_TYPEMASK) >> TYPESHIFT ]);
}
/**
* Aus Type-Description String den mode-Wert ermitteln
* ebenso moeglich ist uebergabe eines File-Types oder File-Endung
*
* @return MODE_GENERIC, wenn kein anderer Typ gefunden wurde
*/
public static int getType( String typeStr )
{
for( int i = 0; i < TYPEDESCR.length; i++ ) {
if( TYPEDESCR[ i ].equals( typeStr )) return( i << TYPESHIFT );
}
for( int i = 0; i < FILETYPE.length; i++ ) { // retry with abstract types
if( FILETYPESTR[ i ].equals( typeStr )) return( i << TYPESHIFT );
}
for( int i = 0; i < EXTSTR.length; i++ ) { // retry with abstract types
if( typeStr.endsWith( EXTSTR[ i ])) return( i << TYPESHIFT );
}
return MODE_GENERIC;
}
public static int getAudioFileType( int genericType )
{
final int audioType;
switch( genericType ) {
case MODE_AIFF:
audioType = AudioFileDescr.TYPE_AIFF;
break;
case MODE_SND:
audioType = AudioFileDescr.TYPE_SND;
break;
case MODE_IRCAM:
audioType = AudioFileDescr.TYPE_IRCAM;
break;
case MODE_WAVE:
audioType = AudioFileDescr.TYPE_WAVE;
break;
case MODE_WAVE64:
audioType = AudioFileDescr.TYPE_WAVE64;
break;
default:
audioType = AudioFileDescr.TYPE_UNKNOWN;
}
return audioType;
}
/**
* subclasses MUST override
*/
public String getFormat()
throws IOException
{
String format;
GenericFile f2 = null;
switch( mode & MODE_TYPEMASK ) {
case MODE_TIFF:
f2 = new ImageFile( file, MODE_INPUT );
try {
format = f2.getFormat();
f2.cleanUp();
return format;
}
catch( IOException e1 ) {
f2.cleanUp();
throw e1;
}
case MODE_AIFF:
case MODE_SND:
case MODE_IRCAM:
case MODE_WAVE:
case MODE_WAVE64:
AudioFile af = AudioFile.openAsRead( file );
AudioFileDescr afd = af.getDescr();
format = afd.getFormat();
af.cleanUp();
return format;
case MODE_SPECT:
f2 = new SpectralFile( file, MODE_INPUT );
try {
format = f2.getFormat();
f2.cleanUp();
return format;
}
catch( IOException e1 ) {
f2.cleanUp();
throw e1;
}
// XXX QUICKTIME
// case MODE_MPEG:
// case MODE_MOV:
// f2 = new MovieFile( file, MODE_INPUT );
// break;
default:
throw new IOException( ERR_ILLEGALFILE );
}
}
// -------- private methods --------
/*
* Default BufferSize ermitteln
*/
// protected int getDefaultBufSize()
// {
// int i = Math.max( 0, Math.min( BUFSIZE.length - 1,
// Application.userPrefs.getInt( MainPrefs.KEY_IOBUFSIZE, 0 )));
//
// return BUFSIZE[ i ];
// }
/*
* Datei zum Oeffnen vorbereiten; konkret: loeschen, wenn sie im Schreibmodus schon existiert
*/
private static synchronized File prepareOpen( File f, int mode )
{
if( (mode & MODE_FILEMASK) == MODE_OUTPUT )
{
// ensure the file is killed before we open it to write to
if( f.length() > 0L ) f.delete();
}
return f; // Java austricksen im Konstruktor; wir sind schneller als SuperClass ;)
}
/*
* Datei Header einlesen, um Filetype zu ermitteln
*/
private void retrieveType()
throws IOException
{
int type = MODE_GENERIC;
String osType;
boolean done = false;
final int audioType = AudioFile.retrieveType( file );
if( audioType != AudioFileDescr.TYPE_UNKNOWN ) { // stupid translation
done = true;
switch( audioType ) {
case AudioFileDescr.TYPE_AIFF:
type = MODE_AIFF;
break;
case AudioFileDescr.TYPE_IRCAM:
type = MODE_IRCAM;
break;
case AudioFileDescr.TYPE_SND:
type = MODE_SND;
break;
case AudioFileDescr.TYPE_WAVE:
type = MODE_WAVE;
break;
case AudioFileDescr.TYPE_WAVE64:
type = MODE_WAVE64;
break;
default:
done = false;
break;
}
}
if( !done ) {
final long oldpos = getFilePointer();
try {
seek( 0L );
final int magic = readInt();
switch( magic ) {
case TIFF_MAC_MAGIC: // -------- TIFF image --------
case TIFF_IBM_MAGIC:
type = MODE_TIFF;
break;
case SHA_MAGIC: // -------- spect file --------
case CSA_MAGIC:
type = MODE_SPECT;
break;
case FLOAT_MAGIC: // -------- float file --------
type = MODE_FLOAT;
break;
case MPEG_MAGIC1: // -------- movie --------
case MPEG_MAGIC2:
type = MODE_MPEG;
break;
default:
if( readInt() == MOV_MAGIC ) {
type = MODE_MOV;
} else { // QT may use File Resource for Movie Identification
// osType = MRJAdapter.getFileType( file.getAbsoluteFile() );
// if( osType.equals( FILETYPESTR[ MODE_MOV >> TYPESHIFT ])) {
// type = MODE_MOV;
// }
}
break;
}
}
finally {
seek( oldpos );
}
}
mode = (mode & ~MODE_TYPEMASK) | type;
}
}