/* For Copyright and License see LICENSE.txt and COPYING.txt in the root directory */ package com.nerdscentral.audio.io; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import com.nerdscentral.audio.Messages; import com.nerdscentral.audio.core.SFGenerator; import com.nerdscentral.sython.Caster; import com.nerdscentral.sython.SFPL_Operator; import com.nerdscentral.sython.SFPL_RuntimeException; public class SF_ReadSignal implements SFPL_Operator { public static class Generator extends SFGenerator { private static final int DBUFFER_LEN = 1024; private static final int BBUFFER_LEN = 1024 * 8; private final RandomAccessFile file; private final int length; private final double[] buff; private long buffPos; private final byte[] bbuff; protected Generator(RandomAccessFile fileIn) { file = fileIn; try { fileIn.seek(0); length = fileIn.readInt(); buff = new double[DBUFFER_LEN]; bbuff = new byte[BBUFFER_LEN]; updateBuffer(0); } catch (IOException e) { throw new RuntimeException(e); } } private void updateBuffer(long index) { try { buffPos = index * 8l + 4l; ByteArrayInputStream bis = new ByteArrayInputStream(bbuff); DataInputStream dis = new DataInputStream(bis); long len = BBUFFER_LEN; if (file.getFilePointer() + BBUFFER_LEN > file.length()) { len = file.length() - file.getFilePointer(); } file.seek(buffPos); file.read(bbuff, 0, (int) len); // System.err.println("Seeking " + buffPos + " len " + len); for (int i = 0; i < len / 8; ++i) { buff[i] = dis.readDouble(); } } catch (IOException e) { throw new RuntimeException(e); } } @Override public synchronized double getSample(int index) { // TODO: Is simple synchronisation the best solution or would thread locality be a better bet? // We need to do something because two thread can be looking at different places entirely. // If that happens performance will completely suck anyhow and we _should_ convert to just // loading the whole thing into memory. if (index > length || index < 0) { throw new ArrayIndexOutOfBoundsException(); } long toSeek = index; toSeek = toSeek * 8 + 4; toSeek -= buffPos; int off = (int) (toSeek / 8); // System.err.println("Off: " + off); try { return buff[off]; } catch (ArrayIndexOutOfBoundsException e) { // System.err.println("Exception: " + e.getMessage()); updateBuffer(index); toSeek = index; toSeek = toSeek * 8 + 4; toSeek -= buffPos; off = (int) (toSeek / 8); return buff[off]; } } @Override public int getLength() { return length; } @Override public void release() { try { file.close(); } catch (IOException e) { throw new RuntimeException(e); } } } /** * */ private static final long serialVersionUID = 1L; @SuppressWarnings("resource") @Override public Object Interpret(final Object input) throws SFPL_RuntimeException { String fileName = Caster.makeString(input); File file = new File(fileName); try { return new Generator(new RandomAccessFile(file, "r")); //$NON-NLS-1$ } catch (FileNotFoundException e) { throw new RuntimeException(e); } } @Override public String Word() { return Messages.getString("SF_ReadSignal.0"); //$NON-NLS-1$ } }