Package de.sciss.eisenkraut.io

Source Code of de.sciss.eisenkraut.io.DecimatedSonaTrail$Decimator

/*
*  DecimatedSonaTrail.java
*  Eisenkraut
*
*  Copyright (c) 2004-2014 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:
*    18-Feb-08  created
*    15-Apr-08  extracted back from DecimatedWaveTrail
*/

package de.sciss.eisenkraut.io;

import java.awt.Graphics2D;
//import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
//import java.awt.TexturePaint;
import java.awt.image.BufferedImage;
//import java.awt.image.ColorModel;
//import java.awt.image.MemoryImageSource;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.prefs.Preferences;

import de.sciss.app.AbstractApplication;
import de.sciss.app.AbstractCompoundEdit;
import de.sciss.eisenkraut.gui.WaveformView;
import de.sciss.eisenkraut.math.ConstQ;
import de.sciss.eisenkraut.math.FastLog;
import de.sciss.eisenkraut.math.MathUtil;
//import de.sciss.eisenkraut.math.SlidingDFT;
import de.sciss.eisenkraut.util.PrefsUtil;
import de.sciss.io.AudioFile;
import de.sciss.io.CacheManager;
import de.sciss.io.IOUtil;
import de.sciss.io.Span;
import de.sciss.util.MutableInt;

/**
*   Sonagram trail with automatic handling of subsampled versions.
*   This class is dedicated to the memory of Niklas Werner, a
*   former fellow student of mine. The sonagram colour table is
*   taken from his work sonasound (http://sonasound.sourceforge.net/).
*   <P>
*   Performance tests with the new SlidingDFT class were quite
*   disappointing. Even in the worst scenario, fftSize=32768 and stepSize=128,
*   the plain FFT approach is still 7.5 times faster than the
*   sliding DFT variant...
*
@version  0.70, 28-Jun-08
@author    Hanns Holger Rutz
*
*  @todo    editing (addAllDep)
*  @todo    delay compensation (fftSize data seems to be missing,
*        the necessary pre-delay seems to be fftSize / 2)
*  @todo    cache management
*  @todo    cost-calculation of potential sliding DFT
*  @todo    there's an efficient multi-channel FFT?
*  @todo    the AsyncEvent should have Span info so DocumentFrame
*        doesn't always need to repaint
*/
public class DecimatedSonaTrail
extends DecimatedTrail
{
  private static final int    UPDATE_PERIOD      = 4000; // millisecs in async overview calculation

  protected final Decimator    decimator;
 
  protected int          fftSize; //        = 1024;
  protected final int        stepSize;
//  protected final float[]     inpWin;
 
  protected final ConstQ      constQ;
  protected final int        numKernels;
  protected final float[]      filterBuf;
//  protected final boolean      doSlide;
//  protected final  SlidingDFT    slide;
 
  private static final int[]     colors = // from niklas werner's sonasound!
    0x000000, 0x050101, 0x090203, 0x0E0304, 0x120406, 0x160507, 0x1A0608, 0x1D0609,
    0x20070A, 0x23080B, 0x25080C, 0x27090D, 0x290A0D, 0x2B0A0E, 0x2D0B0F, 0x2E0B0F,
    0x300C10, 0x310C10, 0x320C10, 0x320D11, 0x330D11, 0x340D11, 0x340D11, 0x350E12,
    0x350E12, 0x360E12, 0x360E12, 0x360E12, 0x360F13, 0x370F13, 0x370F13, 0x370F13,
    0x381014, 0x381014, 0x381014, 0x391014, 0x391015, 0x3A1115, 0x3A1115, 0x3B1115,
    0x3B1116, 0x3C1216, 0x3D1216, 0x3D1217, 0x3E1217, 0x3E1317, 0x3F1317, 0x3F1317,
    0x401418, 0x401418, 0x401418, 0x401418, 0x411518, 0x411518, 0x411518, 0x411517,
    0x421617, 0x421617, 0x421617, 0x421617, 0x421717, 0x431717, 0x431717, 0x431717,
    0x441818, 0x441818, 0x441818, 0x451818, 0x451818, 0x461919, 0x461919, 0x471919,
    0x471919, 0x481A1A, 0x481A1A, 0x491A1A, 0x4A1A1B, 0x4A1B1B, 0x4B1B1B, 0x4B1B1B,
    0x4C1C1C, 0x4C1C1C, 0x4C1C1C, 0x4D1C1C, 0x4D1D1C, 0x4D1D1C, 0x4D1D1C, 0x4E1D1C,
    0x4E1E1C, 0x4E1E1C, 0x4E1E1C, 0x4F1E1B, 0x4F1F1B, 0x4F1F1B, 0x4F1F1B, 0x4F1F1B,
    0x50201C, 0x50201C, 0x50201C, 0x50201C, 0x50201C, 0x51211C, 0x51211C, 0x51211D,
    0x51211D, 0x52221D, 0x52221E, 0x52221E, 0x52221E, 0x53231F, 0x53231F, 0x53231F,
    0x542420, 0x542420, 0x542420, 0x542420, 0x552521, 0x552521, 0x552521, 0x552522,
    0x562622, 0x562622, 0x562622, 0x562623, 0x572723, 0x572723, 0x572723, 0x572723,
    0x582824, 0x582824, 0x582824, 0x582824, 0x582824, 0x592924, 0x592924, 0x592925,
    0x592925, 0x5A2A25, 0x5A2A25, 0x5A2A25, 0x5B2A26, 0x5B2B26, 0x5B2B27, 0x5B2B27,
    0x5C2C28, 0x5C2C28, 0x5C2C29, 0x5C2C29, 0x5C2D2A, 0x5D2D2B, 0x5D2D2C, 0x5D2D2C,
    0x5D2E2D, 0x5E2E2E, 0x5E2E2F, 0x5E2E30, 0x5E2F30, 0x5F2F31, 0x5F2F32, 0x5F2F33,
    0x603034, 0x603034, 0x603035, 0x603035, 0x613036, 0x613136, 0x613137, 0x613137,
    0x623138, 0x623238, 0x623239, 0x623239, 0x63323A, 0x63333A, 0x63333B, 0x63333B,
    0x64343C, 0x64343C, 0x64343C, 0x64343D, 0x64353D, 0x65353E, 0x65353E, 0x65353F,
    0x65363F, 0x653640, 0x663641, 0x663641, 0x663742, 0x673742, 0x673743, 0x673743,
    0x683844, 0x683844, 0x683844, 0x693845, 0x693845, 0x6A3946, 0x6A3946, 0x6B3947,
    0x6B3947, 0x6C3A48, 0x6C3A48, 0x6D3A49, 0x6D3A49, 0x6E3B4A, 0x6E3B4A, 0x6F3B4B,
    0x703C4C, 0x703C4C, 0x713C4D, 0x713C4E, 0x723D4E, 0x723D4F, 0x733D50, 0x733E51,
    0x743E52, 0x743E52, 0x753E53, 0x753F54, 0x763F55, 0x763F55, 0x773F56, 0x773F57,
    0x784058, 0x784058, 0x784059, 0x784059, 0x79405A, 0x79405A, 0x79405B, 0x79405B,
    0x7A405B, 0x7A405C, 0x7A405C, 0x7A405D, 0x7A405D, 0x7B405E, 0x7B405E, 0x7B405F,
    0x7C4060, 0x7C3F60, 0x7C3F61, 0x7D3F62, 0x7D3F62, 0x7D3F63, 0x7E3F64, 0x7E4065,
    0x7F4066, 0x7F4067, 0x804067, 0x804068, 0x814069, 0x81406A, 0x82406A, 0x82406B,
    0x83406C, 0x833F6C, 0x833F6C, 0x843F6D, 0x843F6D, 0x853F6D, 0x853F6E, 0x863F6E,
    0x863F6E, 0x873F6E, 0x873F6E, 0x883F6E, 0x883F6F, 0x893F6F, 0x893F6F, 0x8A3F6F,
    0x8B4070, 0x8B4070, 0x8C4070, 0x8C4070, 0x8D4071, 0x8D4171, 0x8E4172, 0x8E4172,
    0x8F4173, 0x8F4273, 0x904274, 0x904274, 0x914375, 0x914376, 0x924376, 0x924377,
    0x934478, 0x934478, 0x934479, 0x944479, 0x94447A, 0x94447B, 0x94447B, 0x95447C,
    0x95447D, 0x95447D, 0x95447E, 0x95447F, 0x964480, 0x964480, 0x964481, 0x964482,
    0x974483, 0x974383, 0x974384, 0x974385, 0x974386, 0x984386, 0x984387, 0x984388,
    0x984389, 0x99438A, 0x99438A, 0x99438B, 0x99438C, 0x9A438D, 0x9A438D, 0x9A438E,
    0x9B448F, 0x9B448F, 0x9B4490, 0x9B4490, 0x9C4491, 0x9C4491, 0x9C4492, 0x9C4492,
    0x9D4493, 0x9D4493, 0x9D4493, 0x9D4494, 0x9E4494, 0x9E4495, 0x9E4495, 0x9E4496,
    0x9F4497, 0x9F4397, 0x9F4398, 0x9F4398, 0xA04399, 0xA0439A, 0xA0439B, 0xA0439B,
    0xA1439C, 0xA1439D, 0xA1439E, 0xA1439F, 0xA2439F, 0xA243A0, 0xA243A1, 0xA243A2,
    0xA344A3, 0xA344A3, 0xA344A4, 0xA344A5, 0xA344A6, 0xA444A6, 0xA445A7, 0xA445A8,
    0xA445A9, 0xA545A9, 0xA546AA, 0xA546AB, 0xA546AB, 0xA647AC, 0xA647AD, 0xA647AE,
    0xA748AF, 0xA748AF, 0xA748B0, 0xA748B1, 0xA849B2, 0xA849B2, 0xA849B3, 0xA949B4,
    0xA94AB5, 0xA94AB6, 0xA94AB6, 0xAA4AB7, 0xAA4BB8, 0xAA4BB8, 0xAA4BB9, 0xAA4BBA,
    0xAB4CBB, 0xAB4CBB, 0xAB4CBC, 0xAB4CBC, 0xAB4CBD, 0xAB4DBD, 0xAB4DBE, 0xAB4DBE,
    0xAB4DBF, 0xAB4EBF, 0xAB4EC0, 0xAB4EC0, 0xAB4EC1, 0xAB4FC1, 0xAB4FC2, 0xAB4FC2,
    0xAB50C3, 0xAA50C3, 0xAA50C3, 0xAA50C4, 0xAA51C4, 0xAA51C5, 0xAA51C5, 0xAA51C6,
    0xAA52C6, 0xAA52C7, 0xAA52C7, 0xAA52C8, 0xAA53C8, 0xAA53C9, 0xAA53C9, 0xAA53CA,
    0xAB54CB, 0xAB54CB, 0xAB54CC, 0xAB54CC, 0xAB54CD, 0xAC55CD, 0xAC55CE, 0xAC55CE,
    0xAC55CF, 0xAD56CF, 0xAD56D0, 0xAD56D0, 0xAE56D1, 0xAE57D1, 0xAE57D2, 0xAE57D2,
    0xAF58D3, 0xAF58D3, 0xAF58D3, 0xAF58D4, 0xAF59D4, 0xAF59D4, 0xAF59D5, 0xAF59D5,
    0xAF5AD5, 0xAF5AD5, 0xAF5AD6, 0xAF5AD6, 0xAF5BD6, 0xAF5BD6, 0xAF5BD6, 0xAF5BD6,
    0xAF5CD7, 0xAE5CD7, 0xAE5CD7, 0xAE5CD7, 0xAD5CD7, 0xAD5DD7, 0xAD5DD6, 0xAD5DD6,
    0xAC5DD6, 0xAC5ED6, 0xAC5ED6, 0xAB5ED6, 0xAB5ED6, 0xAB5FD6, 0xAB5FD6, 0xAB5FD6,
    0xAB60D7, 0xAA60D7, 0xAA60D7, 0xAA60D7, 0xAA61D7, 0xAA61D7, 0xAA61D8, 0xAA61D8,
    0xAB62D8, 0xAB62D8, 0xAB62D9, 0xAB62D9, 0xAB63D9, 0xAB63DA, 0xAB63DA, 0xAB63DA,
    0xAB64DB, 0xAA64DB, 0xAA64DB, 0xAA64DB, 0xAA64DC, 0xA965DC, 0xA965DC, 0xA965DC,
    0xA965DC, 0xA865DD, 0xA866DD, 0xA866DD, 0xA766DD, 0xA766DE, 0xA767DE, 0xA767DE,
    0xA768DF, 0xA668DF, 0xA668DF, 0xA669DF, 0xA669E0, 0xA66AE0, 0xA66AE0, 0xA66BE1,
    0xA66BE1, 0xA66CE1, 0xA66CE1, 0xA76DE2, 0xA76DE2, 0xA76EE2, 0xA76EE2, 0xA76FE2,
    0xA770E3, 0xA670E3, 0xA671E3, 0xA671E3, 0xA672E3, 0xA672E3, 0xA573E2, 0xA573E2,
    0xA574E2, 0xA574E2, 0xA475E2, 0xA475E2, 0xA476E2, 0xA376E2, 0xA377E2, 0xA377E2,
    0xA378E3, 0xA278E3, 0xA279E3, 0xA279E3, 0xA17AE3, 0xA17AE4, 0xA17BE4, 0xA17BE4,
    0xA07CE5, 0xA07CE5, 0xA07DE5, 0xA07DE5, 0x9F7EE6, 0x9F7EE6, 0x9F7FE6, 0x9F7FE6,
    0x9F80E7, 0x9E80E7, 0x9E80E7, 0x9E81E7, 0x9E81E7, 0x9D82E7, 0x9D82E7, 0x9D83E7,
    0x9D83E6, 0x9C83E6, 0x9C84E6, 0x9C84E6, 0x9C85E6, 0x9B85E6, 0x9B86E6, 0x9B86E6,
    0x9B87E7, 0x9A87E7, 0x9A87E7, 0x9A88E7, 0x9988E7, 0x9989E8, 0x9989E8, 0x988AE8,
    0x988AE9, 0x988BE9, 0x988BE9, 0x978CEA, 0x978CEA, 0x978DEA, 0x978DEA, 0x978EEA,
    0x978FEB, 0x968FEB, 0x9690EA, 0x9690EA, 0x9691EA, 0x9691EA, 0x9692EA, 0x9692EA,
    0x9693E9, 0x9693E9, 0x9694E9, 0x9694E8, 0x9695E8, 0x9695E8, 0x9696E7, 0x9696E7,
    0x9797E7, 0x9697E6, 0x9697E6, 0x9698E6, 0x9698E5, 0x9699E5, 0x9699E5, 0x969AE5,
    0x969AE4, 0x969BE4, 0x969BE4, 0x969CE4, 0x969CE3, 0x969DE3, 0x969DE3, 0x969EE3,
    0x979FE3, 0x979FE2, 0x97A0E2, 0x97A0E2, 0x97A1E1, 0x98A1E1, 0x98A2E1, 0x98A2E1,
    0x98A3E0, 0x99A3E0, 0x99A4E0, 0x99A4E0, 0x9AA5DF, 0x9AA5DF, 0x9AA6DF, 0x9AA6DF,
    0x9BA7DF, 0x9BA7DE, 0x9BA7DE, 0x9BA7DE, 0x9BA8DE, 0x9BA8DE, 0x9BA8DD, 0x9BA8DD,
    0x9BA8DD, 0x9BA9DD, 0x9BA9DC, 0x9BA9DC, 0x9BA9DC, 0x9BAADC, 0x9BAADB, 0x9BAADB,
    0x9BABDB, 0x9AABDA, 0x9AABDA, 0x9AACD9, 0x9AACD9, 0x99ADD8, 0x99ADD8, 0x99AED7,
    0x99AED7, 0x98AFD6, 0x98AFD5, 0x98B0D5, 0x98B0D4, 0x97B1D4, 0x97B1D3, 0x97B2D3,
    0x97B3D3, 0x96B3D2, 0x96B4D2, 0x96B4D1, 0x95B5D1, 0x95B5D1, 0x95B6D1, 0x95B6D0,
    0x94B7D0, 0x94B7D0, 0x94B7D0, 0x94B8CF, 0x93B8CF, 0x93B9CF, 0x93B9CF, 0x93BACF,
    0x93BBCF, 0x92BBCE, 0x92BCCE, 0x92BCCE, 0x91BDCE, 0x91BDCD, 0x91BECD, 0x91BECD,
    0x90BFCD, 0x90BFCC, 0x90C0CC, 0x90C0CC, 0x8FC1CC, 0x8FC1CB, 0x8FC2CB, 0x8FC2CB,
    0x8FC3CB, 0x8EC3CA, 0x8EC3CA, 0x8EC4CA, 0x8EC4C9, 0x8DC4C9, 0x8DC4C9, 0x8DC5C9,
    0x8DC5C8, 0x8CC5C8, 0x8CC5C8, 0x8CC5C8, 0x8CC6C7, 0x8BC6C7, 0x8BC6C7, 0x8BC6C7,
    0x8BC7C7, 0x8AC7C6, 0x8AC7C6, 0x8AC7C6, 0x89C7C5, 0x89C8C5, 0x89C8C5, 0x88C8C5,
    0x88C8C4, 0x88C9C4, 0x88C9C4, 0x87C9C4, 0x87C9C3, 0x87CAC3, 0x87CAC3, 0x87CAC3,
    0x87CBC3, 0x86CBC2, 0x86CBC2, 0x86CBC2, 0x86CCC2, 0x86CCC1, 0x86CCC1, 0x87CCC1,
    0x87CDC1, 0x87CDC0, 0x87CDC0, 0x87CDC0, 0x87CEC0, 0x87CEBF, 0x87CEBF, 0x87CEBF,
    0x87CFBF, 0x86CFBE, 0x86CFBE, 0x86CFBE, 0x86CFBD, 0x85D0BD, 0x85D0BD, 0x85D0BC,
    0x85D0BC, 0x84D0BC, 0x84D1BC, 0x84D1BB, 0x83D1BB, 0x83D2BB, 0x83D2BB, 0x83D2BB,
    0x83D3BB, 0x82D3BA, 0x82D3BA, 0x82D4BA, 0x82D4BA, 0x82D5BA, 0x82D5BA, 0x82D6BA,
    0x82D6BA, 0x82D7B9, 0x82D7B9, 0x82D8B9, 0x82D8B9, 0x82D9B8, 0x82D9B8, 0x82DAB7,
    0x83DBB7, 0x83DBB6, 0x83DCB5, 0x83DCB4, 0x83DDB3, 0x83DDB2, 0x84DEB1, 0x84DEB0,
    0x84DFAF, 0x84DFAE, 0x84E0AD, 0x85E0AB, 0x85E1AA, 0x85E1A9, 0x86E2A8, 0x86E2A7,
    0x87E3A7, 0x87E3A6, 0x87E3A5, 0x88E3A4, 0x88E4A4, 0x89E4A3, 0x89E4A3, 0x8AE5A2,
    0x8AE5A2, 0x8BE5A1, 0x8BE5A1, 0x8CE5A0, 0x8CE6A0, 0x8DE6A0, 0x8DE69F, 0x8EE69F,
    0x8FE79F, 0x8FE79E, 0x90E79E, 0x90E79D, 0x91E79D, 0x91E89C, 0x92E89C, 0x92E89B,
    0x93E89B, 0x93E99A, 0x94E99A, 0x94E999, 0x94E999, 0x95EA98, 0x95EA98, 0x96EA97,
    0x97EB97, 0x97EB96, 0x98EB95, 0x98EB95, 0x99EC94, 0x99EC94, 0x9AEC93, 0x9AEC93,
    0x9BED92, 0x9BED92, 0x9CED91, 0x9CED91, 0x9DEE90, 0x9DEE90, 0x9EEE8F, 0x9EEE8F,
    0x9FEF8F, 0x9FEF8E, 0x9FEF8E, 0xA0EF8D, 0xA0F08D, 0xA1F08C, 0xA1F08C, 0xA1F08B,
    0xA2F18B, 0xA2F18A, 0xA2F18A, 0xA3F189, 0xA3F288, 0xA3F288, 0xA4F287, 0xA4F287,
    0xA5F387, 0xA5F386, 0xA5F386, 0xA6F385, 0xA6F385, 0xA6F484, 0xA7F484, 0xA7F483,
    0xA7F483, 0xA8F582, 0xA8F582, 0xA9F581, 0xA9F581, 0xA9F681, 0xAAF680, 0xAAF680,
    0xABF780, 0xABF77F, 0xABF77F, 0xACF77F, 0xACF87E, 0xADF87E, 0xADF87E, 0xADF97E,
    0xAEF97D, 0xAEF97D, 0xAFF97D, 0xAFFA7D, 0xB0FA7C, 0xB1FA7C, 0xB1FA7C, 0xB2FA7C,
    0xB3FB7C, 0xB3FB7B, 0xB4FB7B, 0xB5FB7B, 0xB5FB7A, 0xB6FB7A, 0xB7FB7A, 0xB8FB7A,
    0xB9FB79, 0xB9FB79, 0xBAFB79, 0xBBFB79, 0xBCFB78, 0xBCFB78, 0xBDFB78, 0xBEFB78,
    0xBFFB78, 0xBFFA77, 0xC0FA77, 0xC0FA77, 0xC1FA77, 0xC1FA77, 0xC2FA77, 0xC2FA77,
    0xC3FA77, 0xC3FA77, 0xC4FA77, 0xC4FA77, 0xC4FA77, 0xC5FA77, 0xC5FA77, 0xC6FA77,
    0xC7FB78, 0xC7FB78, 0xC8FB78, 0xC8FB78, 0xC9FB78, 0xCAFB79, 0xCAFB79, 0xCBFB79,
    0xCCFB7A, 0xCDFB7A, 0xCEFB7A, 0xCEFB7A, 0xCFFB7B, 0xD0FB7B, 0xD1FB7B, 0xD2FB7B,
    0xD3FB7C, 0xD3FA7C, 0xD4FA7C, 0xD5FA7C, 0xD6FA7C, 0xD7FA7C, 0xD7FA7C, 0xD8FA7C,
    0xD9FA7D, 0xDAFA7D, 0xDAFA7D, 0xDBFA7D, 0xDCFA7E, 0xDDFA7E, 0xDDFA7E, 0xDEFA7F,
    0xDFFB80, 0xDFFB80, 0xE0FB81, 0xE0FB82, 0xE1FB82, 0xE1FB83, 0xE2FB84, 0xE2FB85,
    0xE3FB86, 0xE3FB87, 0xE4FB88, 0xE4FB89, 0xE5FB8A, 0xE5FB8B, 0xE6FB8C, 0xE6FB8E,
    0xE7FB8F, 0xE7FA8F, 0xE8FA90, 0xE8FA91, 0xE9FA92, 0xE9FA93, 0xEAFA94, 0xEAFA95,
    0xEBFA95, 0xEBFA96, 0xECFA97, 0xECFA97, 0xEDFA98, 0xEDFA99, 0xEEFB99, 0xEEFB9A,
    0xEFFB9B, 0xEFFA9B, 0xEFFA9C, 0xF0FA9C, 0xF0FA9D, 0xF0FA9D, 0xF0FA9E, 0xF1FA9E,
    0xF1FA9F, 0xF1FA9F, 0xF1FAA0, 0xF2FAA0, 0xF2FAA1, 0xF2FAA1, 0xF2FAA2, 0xF2FAA2,
    0xF3FBA3, 0xF3FBA3, 0xF3FBA3, 0xF3FBA4, 0xF3FBA5, 0xF4FBA5, 0xF4FBA6, 0xF4FBA6,
    0xF4FBA7, 0xF5FBA8, 0xF5FBA8, 0xF5FBA9, 0xF5FBAA, 0xF6FBAB, 0xF6FBAC, 0xF6FBAD,
    0xF7FBAF, 0xF7FAB0, 0xF7FAB1, 0xF7FAB3, 0xF8FAB4, 0xF8FAB6, 0xF8FAB7, 0xF9FAB9,
    0xF9FABA, 0xF9FABC, 0xF9FABE, 0xFAFABF, 0xFAFAC1, 0xFAFAC2, 0xFAFAC4, 0xFAFAC5,
    0xFBFBC7, 0xFBFBC8, 0xFBFBC9, 0xFBFBCA, 0xFBFBCB, 0xFBFBCC, 0xFBFBCE, 0xFBFBCF,
    0xFBFBD0, 0xFBFBD1, 0xFBFBD2, 0xFBFBD3, 0xFBFBD5, 0xFBFBD6, 0xFBFBD7, 0xFBFBD9,
    0xFBFBDB, 0xFAFADC, 0xFAFADE, 0xFAFAE0, 0xFAFAE2, 0xFAFAE4, 0xFAFAE6, 0xFAFAE8,
    0xFAFAEA, 0xFAFAED, 0xFAFAEF, 0xFAFAF1, 0xFAFAF3, 0xFAFAF5, 0xFAFAF7, 0xFAFAF9,
    0xFBFBFB, 0xFBFBFC, 0xFBFBFE, 0xFBFBFF, 0xFBFBFF, 0xFBFBFF, 0xFBFBFF, 0xFBFBFF,
    0xFBFBFF, 0xFCFCFF, 0xFCFCFF, 0xFCFCFF, 0xFCFCFF, 0xFDFDFF, 0xFDFDFF, 0xFDFDFF,
    0xFEFEFE
  };
 
  private static FastLog log10 = null;

  public DecimatedSonaTrail( AudioTrail fullScale, int model /*, int[] decimations */ )
  throws IOException
  {
    super();

    switch( model ) {
    case MODEL_SONA:
      decimator    = new SonaDecimator();
      break;
    default:
      throw new IllegalArgumentException( "Model " + model );
    }

    this.fullScale  = fullScale;
    this.model    = model;

    constQ      = new ConstQ();
   
    final Preferences cqPrefs = AbstractApplication.getApplication().getUserPrefs().node( PrefsUtil.NODE_VIEW ).node( PrefsUtil.NODE_SONAGRAM );
    constQ.readPrefs( cqPrefs );
    constQ.setSampleRate( fullScale.getRate() );
    System.out.println( "Creating ConstQ Kernels..." );
    constQ.createKernels();
    numKernels    = constQ.getNumKernels();
    filterBuf    = new float[ numKernels ];
    fftSize      = constQ.getFFTSize();
    modelChannels  = constQ.getNumKernels();
    fullChannels  = fullScale.getChannelNum();
    decimChannels  = fullChannels * modelChannels;
//    doSlide      = true;
//    if( doSlide ) {
//      slide    = new SlidingDFT( fftSize, fullChannels );
//    } else {
//      slide    = null;
//    }
    System.out.println( "...done." );
   
//    numMag      = fftSize >> 1;
//    stepSize    = Math.min( fftSize, 256 );
    // approx. 5 milliseconds resolution (for the high freqs)
//    stepSize    = Math.max( 64, Math.min( fftSize, (int) (0.005 * fullScale.getRate() + 0.5) & ~1 ));
    // the division by 1.41 is providing proper rounding such that
    // stepSize / theorSize == min( ceilFFTSize / theorSize, theorSize / floorFFTSize )!
    stepSize    = Math.max( 64, Math.min( fftSize, MathUtil.nextPowerOfTwo( (int) (constQ.getMaxTimeRes() / 1000 * fullScale.getRate() / Math.sqrt( 2 )))));

    int decimKorr, j;
    for( decimKorr = 1, j = stepSize; j > 2; decimKorr++, j >>= 1 ) ;

//    inpWin      = Filter.createFullWindow( fftSize, Filter.WIN_HANNING );
   
final int decimations[] = { decimKorr }; // , decimKorr + 8 };
    SUBNUM      = decimations.length; // the first 'subsample' is actually fullrate
    this.decimHelps  = new DecimationHelp[ SUBNUM ];
    for( int i = 0; i < SUBNUM; i++ ) {
      this.decimHelps[ i ] = new DecimationHelp( fullScale.getRate(), decimations[ i ]);
    }
//    MAXSHIFT    = decimations[ SUBNUM - 1 ]; // + decimKorr;
//    MAXCOARSE    = Math.max( fftSize, 1 << MAXSHIFT );
    MAXSHIFT    = decimations[ 0 ]; // + decimKorr;
    MAXCOARSE    = 1 << MAXSHIFT;
    MAXMASK      = -MAXCOARSE;
    MAXCEILADD    = MAXCOARSE - 1;

    if( log10 == null ) {
      log10 = new FastLog( 10, 11 );
    }

    tmpBufSize    = fftSize; // Math.max( 4096, MAXCOARSE << 1 );
    // XXX generates OutOfMemoryError ; need to use a different approach in the decimation steps
    // (should "just" use the maximum per-step decimation, which is 256 in the Session defaults)
//    tmpBufSize2    = SUBNUM > 0 ? Math.max( 4096, tmpBufSize >> (decimations[ 0 ] + decimKorr)) : tmpBufSize;
    tmpBufSize2    = 1 << decimations[ 0 ];
    for( int i = 1; i < SUBNUM; i++ ) {
      tmpBufSize2 = Math.max( tmpBufSize2, 1 << (decimations[ i ] - decimations[ i - 1 ]));
    }
   
    setRate( fullScale.getRate() );
   
    fullScale.addDependant( this );
   
    // ok, the fullScale file might have already been populated
    // final List stakes = fullScale.getAll( true );
    // if( !stakes.isEmpty() ) {
    // XXX TEST
    // addAllDep( null, stakes, null, fullScale.getSpan() );
    addAllDepAsync();
    // addAllDepAsync( null, stakes, null, fullScale.getSpan() );
    // }
   
//System.out.println( "tmpBufSize " + tmpBufSize + "; tmpBufSize2 " + tmpBufSize2 + "; decimKorr " + decimKorr + "; decimations[0] " + decimations[0] );
  }

  /**
   * @synchronization must be called in the event thread
   */
  public void drawWaveform( DecimationInfo info, WaveformView view, Graphics2D g2 )
  {
//if( true ) return;

//    final boolean      fromPCM     = false; // info.idx == -1;
//    final int        imgW      = fromPCM ?
//      Math.min( tmpBufSize, tmpBufSize2 * info.getDecimationFactor() )
//    : tmpBufSize2; //  << info.shift;
    final int        imgW      = tmpBufSize2; //  << info.shift;
    final int        maxLen      = imgW << info.shift; // * stepSize;

//    final int        imgW      = view.getWidth(); // (int) info.sublength;
    final BufferedImage    bufImg      = new BufferedImage( imgW, modelChannels, BufferedImage.TYPE_INT_RGB );
    final WritableRaster  raster      = bufImg.getRaster();
    final int[]        data      = new int[ imgW * modelChannels ];
    final int        dataStartOff  = imgW * (modelChannels - 1);

//    final AffineTransform  atOrig      = g2.getTransform();
    final Shape        clipOrig    = g2.getClip();
   
//    float[]          chanBuf;
    long          start      = info.span.start;
    long          totalLength    = info.getTotalLength();
    Span          chunkSpan;
    long          fullLen;
//    long          fullStop;
    int            chunkLen; // decimLen;
    float          scaleX, ampLog;
    Rectangle        r;
   
//    final float        scaleX      = (float) (view.getWidth() * stepSize) / totalLength;
   
//final float pixScale = 1072 / (view.getAmpLogMax() - view.getAmpLogMin());
//final float pixOff   = -view.getAmpLogMin();
final float pixScale = 10720 / (view.getAmpLogMax() - view.getAmpLogMin());
final float pixOff   = -view.getAmpLogMin() / 10;

    g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
   
    try {
      drawBusyList.clear(); // "must be called in the event thread"

      synchronized( bufSync ) {
        createBuffers();

long screenOffX = 0;
        while( totalLength > 0 ) {
          fullLen    = Math.min( maxLen, totalLength );
//          chunkLen  = (int) (fromPCM ? fullLen : decimHelps[ info.idx ].fullrateToSubsample( fullLen ));
          chunkLen  = (int) (decimHelps[ info.idx ].fullrateToSubsample( fullLen ));
//          decimLen  = chunkLen / info.inlineDecim;
//          chunkLen  = decimLen * info.inlineDecim;
          fullLen    = (long) chunkLen << info.shift;
//          chunkLen  = (int) (fullLen / stepSize);

//          if( fromPCM ) {
//            fullStop = fullScale.getSpan().stop;
//            if( start + fullLen <= fullStop ) {
//              chunkSpan = new Span( start, start + fullLen );
//              fullScale.readFrames( tmpBuf, 0, chunkSpan );
//            } else {
//              chunkSpan = new Span( start, fullStop );
//              fullScale.readFrames( tmpBuf, 0, chunkSpan );
//              // duplicate last frames
////              for( int i = (int) chunkSpan.getLength(), j = i - 1; i < (int) fullLen; i++ ) {
////                for( int ch = 0; ch < fullChannels; ch++ ) {
////                  sPeakP    = tmpBuf[ ch ];
////                  sPeakP[ i ]  = sPeakP[ j ];
////                }
////              }
//            }
//            decimator.decimatePCM( tmpBuf, tmpBuf2, fftBuf, 0, decimLen, info.inlineDecim );
//          } else {
////            chunkSpan = new Span( start, start + fullLen );
//            chunkSpan = new Span( start, start + fullLen );
//            readFrames( info.idx, tmpBuf2, 0, drawBusyList, chunkSpan, null);
//            if( info.inlineDecim > 1 ) decimator.decimate( tmpBuf2, tmpBuf2, 0, decimLen, info.inlineDecim );
//          }

          chunkSpan = new Span( start, start + fullLen );
         
//System.out.println( "chunkSpan = " + chunkSpan + "; chunkLen = " + chunkLen + "; fullLen = " + fullLen + "; screenOffX " + screenOffX + "; subLength " + info.sublength + "; shift " + info.shift + "; totalLen " + info.getTotalLength() );
         
          if( readFrames( info.idx, tmpBuf2, 0, drawBusyList, chunkSpan, null )) {
//          if( tempFAsync == null || tempFAsync[0] == null ) break;
//          tempFAsync[0].seekFrame( Math.min( start / stepSize, tempFAsync[0].getFrameNum() ));
//          int gaga = (int) Math.min( fullLen, Math.min( tmpBufSize2, tempFAsync[0].getFrameNum() - tempFAsync[0].getFramePosition() ));
//          tempFAsync[0].readFrames( tmpBuf2, 0, gaga);
         
            for( int ch = 0, tmpChReset = 0; ch < fullChannels; ch++, tmpChReset += modelChannels ) {
              r = view.rectForChannel( ch );
              scaleX = (float) r.width / info.getTotalLength();
  //System.out.println( " ... for ch = " + ch + "; scaleX = " + scaleX );
              for( int x = 0, off = 0; x < chunkLen; x++ ) {
                for( int y = 0, off2 = x + dataStartOff, tmpCh = tmpChReset; y < modelChannels; y++, tmpCh++, off2 -= imgW, off++ ) {
  //                ampLog = log10.calc( tmpBuf2[ tmpCh ][ x ]) * 20;
                  ampLog = log10.calc( Math.max( 1.0e-9f, tmpBuf2[ tmpCh ][ x ]));
                  data[ off2 ] = colors[ Math.max( 0, Math.min( 1072, (int) ((ampLog + pixOff) * pixScale) ))];
                }
              }
              raster.setDataElements( 0, 0, imgW, modelChannels, data );
              g2.drawImage( bufImg, r.x + (int) (screenOffX * scaleX + 0.5f), r.y, r.x + (int) ((screenOffX + fullLen) * scaleX + 0.5f), r.y + r.height, 0, 0, chunkLen, modelChannels, view );
            }
          }
          start += fullLen; // chunkLen * stepSize;
          totalLength -= fullLen; // chunkLen * stepSize;
          screenOffX += fullLen;
//          if( gaga == 0 ) totalLength = 0;
        }
      } // synchronized( bufSync )

      // System.err.println( "busyList.size() = "+busyList.size() );

      for( int ch = 0; ch < fullChannels; ch++ ) {
        r = view.rectForChannel( ch );
        g2.clipRect( r.x, r.y, r.width, r.height );
        if( !drawBusyList.isEmpty() ) {
          // g2.setColor( Color.red );
          g2.setPaint( pntBusy );
          for( int i = 0; i < drawBusyList.size(); i++ ) {
            chunkSpan = (Span) drawBusyList.get( i );
            scaleX = (float) r.width / info.getTotalLength();
            g2.fillRect( (int) ((chunkSpan.start - info.span.start) * scaleX) + r.x, r.y,
                         (int) (chunkSpan.getLength() * scaleX), r.height );
          }
        }
        // g2.setTransform(atOrig);
        g2.setClip( clipOrig );
      }
    } catch( IOException e1 ) {
      System.err.println( e1 );
    }
//    if( bufImg != null )
    bufImg.flush();
  }

  /**
   * Determines which subsampled version is suitable for a given display range
   * (the most RAM and CPU economic while maining optimal display resolution).
   * For a given time span, the lowest resolution is chosen which will produce
   * at least <code>minLen</code> frames.
   *
   * @param tag the time span the caller is interested in
   * @param minLen the minimum number of sampled points wanted.
   * @return an information object describing the best subsample of the track
   *         editor. note that info.sublength will be smaller than minLen if
   *         tag.getLength() was smaller than minLen (in this case the
   *         fullrate version is used).
   */
  public DecimationInfo getBestSubsample( Span tag, int minLen )
  {
    final DecimationInfo  info;
    final boolean      fromPCM;
    final long        fullLength  = tag.getLength();
    long          subLength, n;
    int            idx, inlineDecim;

    subLength = fullLength;
//    for( idx = 0; idx < SUBNUM; idx++ ) {
//      n = decimHelps[ idx ].fullrateToSubsample( fullLength );
//      if( n < minLen ) break;
//      subLength = n;
//    }
//    idx--;
n = decimHelps[ 0 ].fullrateToSubsample( fullLength );
subLength = n;
idx=0;
    // had to change '>= minLen' to '> minLen' because minLen could be zero!
    switch( model ) {
    case MODEL_SONA:
//      for( inlineDecim = 2; subLength / inlineDecim > minLen; inlineDecim++ ) ;
//      inlineDecim--;
inlineDecim=1;
      break;

    default:
      assert false : model;
      inlineDecim = 1; // never gets here
    }
    subLength /= inlineDecim;
    // System.err.println( "minLen = "+minLen+"; subLength = "+subLength+";
    // inlineDecim = "+inlineDecim+" ; idx = "+idx );
    fromPCM  = idx == -1;
//    toPCM  = fromPCM && inlineDecim == 1;
    info  = new DecimationInfo( tag, subLength, fullChannels, idx,
            fromPCM ? 0 : decimHelps[ idx ].shift,
            inlineDecim, model );
    return info;
  }

  /**
   * Reads a block of subsampled frames.
   *
   * @param info
   *            the <code>DecimationInfo</code> as returned by
   *            <code>getBestSubsample</code>, describing the span to read
   *            and which resolution to choose.
   * @param frames
   *            to buffer to fill, where frames[0][] corresponds to the first
   *            channel etc. and the buffer length must be at least off +
   *            info.sublength!
   * @param off
   *            offset in frames, such that the first frame will be placed in
   *            frames[ch][off]
   * @throws IOException
   *             if a read error occurs
   * @see #getBestSubsample( Span, int )
   * @see DecimationInfo#sublength
   */
/*
  public boolean readFrame( int sub, long pos, int ch, float[] data )
  throws IOException
  {
    synchronized( bufSync ) {
      createBuffers();

      final int        idx  = indexOf( pos, true );
      final DecimatedWaveStake  ds  = (DecimatedWaveStake) editGetLeftMost( idx, true, null );
      if( ds == null ) return false;

      if( !ds.readFrame( sub, tmpBuf2, 0, pos )) return false;

      for( int i = ch * modelChannels, k = 0; k < modelChannels; i++, k++ ) {
        data[ k ] = tmpBuf2[ i ][ 0 ];
      }

      return true;
    }
  }
*/

  /*
   * Same as in <code>NondestructiveDecimatedSampledTrack</code> but with
   * automaic bias adjust.
   *
   * @param tag unbiased fullrate span @param frames buffer to fill. note that
   * this will not do any interpolation but fill at the decimated rate! @param
   * framesOff offset in frames for the first frame which is read
   *
   * @synchronization  caller must have bufSync !
   */
  private boolean readFrames( int sub, float[][] data, int dataOffset, List busyList,
               Span readSpan, AbstractCompoundEdit ce )
  throws IOException
  {
    int          idx      = editIndexOf( readSpan.start, true, ce );
    if( idx < 0 ) idx = -(idx + 2);
    final long      startR    = decimHelps[ sub ].roundAdd - readSpan.start;
    final List      coll    = editGetCollByStart( ce );
    final MutableInt  readyLen  = new MutableInt( 0 );
    final MutableInt  busyLen    = new MutableInt( 0 );
    // someReady is a transient trick to keep a newly opened documented
    // responsive (since it's zoomed out completely and due to the missing
    // decimation drawWaveform would take ages)
    boolean        someReady  = false;
    DecimatedStake    stake;
    int          chunkLen, discrepancy;
    Span        subSpan;
    int          readOffset, nextOffset = dataOffset;
    int          len      = (int) (readSpan.getLength() >> decimHelps[ sub ].shift );
   
    while( (len > 0) && (idx < coll.size()) ) {
      stake    = (DecimatedStake) coll.get( idx );
      subSpan    = new Span( Math.max( stake.getSpan().start, readSpan.start ),
                  Math.min( stake.getSpan().stop, readSpan.stop ));
      stake.readFrames( sub, data, nextOffset, subSpan, readyLen, busyLen );
      chunkLen  = readyLen.value() + busyLen.value();
      readOffset  = nextOffset + readyLen.value(); // chunkLen;
      nextOffset  = (int) ((subSpan.stop + startR) >> decimHelps[ sub ].shift) + dataOffset;
      discrepancy  = nextOffset - readOffset;
      len      -= readyLen.value() + discrepancy;
      if( readyLen.value() > 0 ) someReady = true;
      if( busyLen.value() == 0 ) {
        if( discrepancy > 0 ) {
          if( readOffset > 0 ) {
            for( int i = readOffset, k = readOffset - 1; i < nextOffset; i++ ) {
              for( int j = 0; j < data.length; j++ ) {
                data[ j ][ i ] = data[ j ][ k ];
              }
            }
          }
        }
      } else {
        final Span busySpan = new Span( subSpan.stop - (subSpan.getLength() * busyLen.value() / chunkLen),
                                        subSpan.stop );
        final int busyLastIdx = busyList.size() - 1;
        if( busyLastIdx >= 0 ) {
          final Span busySpan2 = (Span) busyList.get( busyLastIdx );
          if( busySpan.touches( busySpan2 )) {
            busyList.set( busyLastIdx, busySpan.union( busySpan2 ));
          } else {
            busyList.add( busySpan );
          }
        } else {
          busyList.add( busySpan );
        }
        for( int i = Math.max( 0, readOffset ); i < nextOffset; i++ ) {
          for( int j = 0; j < data.length; j++ ) {
            data[ j ][ i ] = 0f;
          }
        }
      }
      idx++;
    }
    return someReady;
  }

  /*
   * Same as in <code>NondestructiveDecimatedSampledTrack</code> but with
   * automaic bias adjust.
   *
   * @param tag unbiased fullrate span @param frames buffer to fill. note that
   * this will not do any interpolation but fill at the decimated rate! @param
   * framesOff offset in frames for the first frame which is read
   *
   * @see NondestructiveDecimatedSampledTrack#read( Span, float[][], int )
   */
/*
  private void readFrames( int sub, float[][] data, int dataOffset, List busyList,
               Span readSpan, AbstractCompoundEdit ce )
  throws IOException
  {
    int          idx      = editIndexOf( readSpan.start, true, ce );
    if( idx < 0 ) idx = -(idx + 2);
    final long      startR    = decimHelps[sub].roundAdd - readSpan.start;
    final List      coll    = editGetCollByStart( ce );
    final MutableInt  readyLen  = new MutableInt( 0 );
    final MutableInt  busyLen    = new MutableInt( 0 );
    DecimatedWaveStake    stake;
    int          chunkLen, discrepancy;
    Span        subSpan;
    int          readOffset, nextOffset = dataOffset;
    int          len      = (int) (readSpan.getLength() >> decimHelps[ sub ].shift);

    while( (len > 0) && (idx < coll.size()) ) {
      stake    = (DecimatedWaveStake) coll.get( idx );
      subSpan    = new Span( Math.max( stake.getSpan().start, readSpan.start ),
                  Math.min( stake.getSpan().stop, readSpan.stop ));
      stake.readFrames( sub, data, nextOffset, subSpan, readyLen, busyLen );
      chunkLen  = readyLen.value() + busyLen.value();
      readOffset  = nextOffset + readyLen.value(); // chunkLen;
      nextOffset  = (int) ((subSpan.stop + startR) >> decimHelps[ sub ].shift) + dataOffset;
      discrepancy  = nextOffset - readOffset;
      len      -= readyLen.value() + discrepancy;
      if( busyLen.value() == 0 ) {
        if( discrepancy > 0 ) {
          if( readOffset > 0 ) {
            for( int i = readOffset, k = readOffset - 1; i < nextOffset; i++ ) {
              for( int j = 0; j < data.length; j++ ) {
                data[ j ][ i ] = data[ j ][ k ];
              }
            }
          }
        }
      } else {
        busyList.add( new Span( subSpan.stop - (subSpan.getLength() * busyLen.value() / chunkLen),
                    subSpan.stop ));
        for( int i = Math.max( 0, readOffset ); i < nextOffset; i++ ) {
          for( int j = 0; j < data.length; j++ ) {
            data[ j ][ i ] = 0f;
          }
        }
      }
      idx++;
    }
  }
*/
 
  public float getMinFreq()
  {
    return constQ.getMinFreq();
  }
 
  public float getMaxFreq()
  {
    return constQ.getMaxFreq();
  }

  public void debugDump()
  {
    for( int i = 0; i < getNumStakes(); i++ ) {
      ((DecimatedStake) get( i, true )).debugDump();
    }
  }

  // ----------- dependant implementation -----------

//  public void dispose()
//  {
//    super.dispose();
//  }

  // private void addAllDepAsync( Object source, List stakes, SyncCompoundEdit
  // ce, Span union )
  private void addAllDepAsync()
  throws IOException
  {
    if( threadAsync != null ) throw new IllegalStateException();

    final List          stakes    = fullScale.getAll( true );
    if( stakes.isEmpty() ) return;

    final DecimatedStake    das;
    final Span          union    = fullScale.getSpan();
    final Span          extSpan;
    final long          fullrateStop, fullrateLen; // , insertLen;
    final int          numFullBuf;
    // final CacheManager cm = CacheManager.getInstance();
    final AbstractCompoundEdit  ce      = null; // XXX
    final Object        source    = null; // XXX
//    final AudioStake      cacheReadAS;
//    final AudioStake      cacheWriteAS;

    synchronized( fileSync ) {
      das = allocAsync( union );
//das.GOGO = true;
    }
    extSpan      = das.getSpan();
    // insertLen  = extSpan.getLength();
    fullrateStop  = Math.min( extSpan.getStop(), fullScale.editGetSpan( ce ).stop );
    fullrateLen    = fullrateStop - extSpan.getStart();

//    cacheReadAS    = null; // openCacheForRead( model );
//    if( cacheReadAS == null ) {
//      // cacheWriteAS = fullScale.openCacheForWrite( model,
//      // decimHelps[ 0 ].fullrateToSubsample( union.getLength() ));
//      cacheWriteAS = null; // openCacheForWrite( model, (fullrateLen + MAXCEILADD) & MAXMASK );
//      numFullBuf  = (int) (fullrateLen >> MAXSHIFT);
      numFullBuf  = (int) ((fullrateLen - fftSize + stepSize + stepSize - 1) / stepSize);
//    } else {
//      // cached files always have integer fullBufs!
//      numFullBuf  = (int) ((fullrateLen + MAXCEILADD) >> MAXSHIFT);
//      cacheWriteAS = null;
//    }

    synchronized( bufSync ) {
      createBuffers();
    }

    editClear( source, das.getSpan(), ce );
    editAdd( source, das, ce );

    threadAsync = new Thread( new Runnable() {
      public void run()
      {
final int pri = Thread.currentThread().getPriority();
//System.out.println( "pri was " + pri );
Thread.currentThread().setPriority( pri - 2 );
       
//        final CacheManager  cm          = PrefCacheManager.getInstance();
        long        pos;
        // long framesWritten = 0;
        long        framesWrittenCache  = 0;
//        boolean        cacheWriteComplete  = false;
        Span        tag2;
        int          len;
        long        time;
// WARNING: the variant with deferring the continueWrite
// does not work because we loose the bufSync and hence
// any readFrames destroys our buf. For high res analysis
// the speed gain using the outBufOff cache method is
// anyway <3%.
//        int          outBufOff  = 0;
        int          inBufOff = 0, nextLen = fftSize >> 1;
        long        nextTime      = System.currentTimeMillis() + UPDATE_PERIOD;

//        if( cacheReadAS != null ) {
//          pos = decimHelps[ 0 ].fullrateToSubsample( extSpan.getStart() ); // XXX
//        } else {
          pos = extSpan.getStart();
//        }
//        minCoarse = MAXCOARSE >> decimHelps[ 0 ].shift;

        try {
//long t1 = System.currentTimeMillis();
          for( int i = 0; (i < numFullBuf) && keepAsyncRunning; i++ ) {
            synchronized( bufSync ) {
//              if( (i % 100) == 0 ) System.out.println( "ici " + i + " / " + numFullBuf );
              len = (int) Math.min( nextLen, fullrateStop - pos );
//              if( inBufOff + len <= tmpBufSize ) {
                tag2 = new Span( pos, pos + len );
//                fullScale.readFrames( tmpBuf, inBufOff, tag2, null );
                fullScale.readFrames( tmpBuf, fftSize - nextLen, tag2, null );
//              } else { // hitting buffer boundaries
//                tag2 = new Span( pos, pos + (tmpBufSize - inBufOff) );
//                fullScale.readFrames( tmpBuf, inBufOff, tag2, null );
//                tag2 = new Span( tag2.stop, pos + len );
//                fullScale.readFrames( tmpBuf, 0, tag2, null );
//              }
              if( len < nextLen ) {
                for( int ch = 0; ch < fullChannels; ch++ ) {
                  for( int j = (inBufOff + len) % tmpBufSize, k = nextLen - len; k >= 0; k-- ) {
                    tmpBuf[ ch ][ j ] = 0f;
                    if( ++j == tmpBufSize ) j = 0;
                  }
                }
              }
//              decimator.decimatePCM( tmpBuf, tmpBuf2, outBufOff, 1, 1 );
              decimator.decimatePCM( tmpBuf, tmpBuf2, 0, 1, 1 );
//              if( ++outBufOff == 1 ) {
//                das.continueWrite( 0, tmpBuf2, 0, outBufOff );
                das.continueWrite( 0, tmpBuf2, 0, 1 );
//                if( cacheWriteAS != null ) {
////                  cacheWriteAS.writeFrames( tmpBuf2, 0, new Span( framesWrittenCache, framesWrittenCache + outBufOff ));
//                  cacheWriteAS.writeFrames( tmpBuf2, 0, new Span( framesWrittenCache, framesWrittenCache + 1 ));
//                }
//                framesWrittenCache += outBufOff;
                framesWrittenCache++;
//                outBufOff = 0;
//              }
              pos += nextLen;
              inBufOff = (inBufOff + nextLen) % tmpBufSize;
              nextLen = stepSize;
              for( int ch = 0; ch < fullChannels; ch++ ) {
                System.arraycopy( tmpBuf[ ch ], nextLen, tmpBuf[ ch ], 0, fftSize - nextLen );
              }
//              System.out.println( "frame done" );
            }
            time = System.currentTimeMillis();
            if( time >= nextTime ) {
              nextTime = time + UPDATE_PERIOD;
              if( asyncManager != null ) {
                asyncManager.dispatchEvent( new AsyncEvent(
                  DecimatedSonaTrail.this, AsyncEvent.UPDATE, time, DecimatedSonaTrail.this ));
              }
            }
          }
          if( keepAsyncRunning ) {
//            if( outBufOff > 0 ) {  // flush rest
//              das.continueWrite( 0, tmpBuf2, 0, outBufOff );
//              if( cacheWriteAS != null ) {
//                cacheWriteAS.writeFrames( tmpBuf2, 0, new Span( framesWrittenCache, framesWrittenCache + outBufOff ));
//              }
//              framesWrittenCache += outBufOff;
//              outBufOff = 0;
//            }
//            cacheWriteComplete = true;
//            if( cacheWriteAS != null ) cacheWriteAS.addToCache( cm );
          }
//          das.flush();

//final long t2 = System.currentTimeMillis();
//System.out.println( "for doSlide = " + doSlide + "; len = " + extSpan.getLength() + "; calc took " + (t2-t1) + " ms; fftSize = " + fftSize + "; stepSize " + stepSize );
         
        } catch( IOException e1 ) {
          e1.printStackTrace();
        } finally {
//          System.out.println( "finally" );
//          if( cacheReadAS != null ) {
//            cacheReadAS.cleanUp();
//            cacheReadAS.dispose(); // !!!
//          }
//          if( cacheWriteAS != null ) {
//            cacheWriteAS.cleanUp();
//            cacheWriteAS.dispose(); // !!!
//            if( !cacheWriteComplete ) { // indicates process was aborted ...
//              final File[] f = createCacheFileNames();
//              if( f != null ) { // ... therefore delete incomplete cache files!
//                for( int i = 0; i < f.length; i++ ) {
//                  if( !f[ i ].delete() ) f[ i ].deleteOnExit();
//                  // cm.removeFile( f[ i ]);
//                }
//              }
//            }
//          }

          if( asyncManager != null ) {
            asyncManager.dispatchEvent( new AsyncEvent( DecimatedSonaTrail.this,
              AsyncEvent.FINISHED, System.currentTimeMillis(), DecimatedSonaTrail.this ));
          }
          synchronized( threadAsync ) {
            threadAsync.notifyAll();
            // threadAsync = null;
          }
        }
      }
    });

    keepAsyncRunning = true;
    threadAsync.start();
  }

  protected void addAllDep( Object source, List stakes, AbstractCompoundEdit ce, Span union )
  throws IOException
  {
    if( DEBUG ) System.err.println( "addAllDep " + union.toString() );

    final DecimatedStake  das;
    final Span        extSpan;
    final long        fullrateStop, fullrateLen; // , insertLen;
    final int        numFullBuf;
    final double      progWeight;
    int            inBufOff = 0, nextLen = fftSize >> 1;
    long          pos;
    long          framesWritten = 0;
    Span          tag2;
//    float          f1;
    int            len;

    synchronized( fileSync ) {
      das = alloc( union );
    }
    extSpan      = das.getSpan();
    pos        = extSpan.start;
    // insertLen  = extSpan.getLength();
    fullrateStop  = Math.min( extSpan.stop, fullScale.editGetSpan( ce ).stop );
    fullrateLen    = fullrateStop - extSpan.start;
    progWeight    = 1.0 / fullrateLen;
//    numFullBuf    = (int) (fullrateLen >> MAXSHIFT);
    numFullBuf    = (int) ((fullrateLen - fftSize + stepSize + stepSize - 1) / stepSize);
//    numFullBuf    = (int) ((fullrateLen + stepSize - 1) / stepSize);

    synchronized( bufSync ) {
      flushProgression();
      createBuffers();

      for( int i = 0; i < numFullBuf; i++ ) {
        len = (int) Math.min( nextLen, fullrateStop - pos );
        tag2 = new Span( pos, pos + len );
//        fullScale.readFrames( tmpBuf, 0, tag2, ce );
        fullScale.readFrames( tmpBuf, fftSize - nextLen, tag2, null );
        if( len < nextLen ) {
          for( int ch = 0; ch < fullChannels; ch++ ) {
            for( int j = (inBufOff + len) % tmpBufSize, k = nextLen - len; k >= 0; k-- ) {
              tmpBuf[ ch ][ j ] = 0f;
              if( ++j == tmpBufSize ) j = 0;
            }
          }
        }
        decimator.decimatePCM( tmpBuf, tmpBuf2, 0, 1, 1 );
//        subsampleWrite( tmpBuf, tmpBuf, das, MAXCOARSE, null, 0 );
        das.continueWrite( 0, tmpBuf2, 0, 1 );
//        pos += MAXCOARSE;
//        framesWritten += MAXCOARSE;
        pos += nextLen;
        inBufOff = (inBufOff + nextLen) % tmpBufSize;
        nextLen = stepSize;
        for( int ch = 0; ch < fullChannels; ch++ ) {
          System.arraycopy( tmpBuf[ ch ], nextLen, tmpBuf[ ch ], 0, fftSize - nextLen );
        }

        setProgression( framesWritten, progWeight );
      }

//      len = (int) (fullrateStop - pos);
//      if( len > 0 ) {
//        tag2 = new Span( pos, pos + len );
//        fullScale.readFrames( tmpBuf, 0, tag2, ce );
//        for( int ch = 0; ch < fullChannels; ch++ ) {
//          f1 = tmpBuf[ ch ][ len - 1 ];
//          for( int i = len; i < MAXCOARSE; i++ ) {
//            tmpBuf[ ch ][ i ] = f1;
//          }
//        }
//        subsampleWrite( tmpBuf, tmpBuf, das, MAXCOARSE, null, 0 );
//        pos += MAXCOARSE;
//        framesWritten += MAXCOARSE;
//
//        setProgression( framesWritten, progWeight );
//      }
    } // synchronized( bufSync )

    // editRemove( source, das.getSpan(), ce );
    editClear( source, das.getSpan(), ce );
    // System.err.println( "editRemove "+das.getSpan() );
    editAdd( source, das, ce );
    // System.err.println( "editAdd ..." );
  }

  // ----------- private schnucki -----------

  protected DecimatedStake allocAsync( Span span )
  throws IOException
  {
    if( !Thread.holdsLock( fileSync )) throw new IllegalMonitorStateException();

    final long floorStart  = span.start / stepSize * stepSize; // & MAXMASK;
    final long ceilStop    = (span.stop + stepSize - 1) / stepSize * stepSize; // (span.stop + MAXCEILADD) & MAXMASK;
    final Span extSpan    = (floorStart == span.start) && (ceilStop == span.stop) ?
                  span : new Span( floorStart, ceilStop );
    final Span[] fileSpans  = new Span[ SUBNUM ];
    final Span[] biasedSpans = new Span[ SUBNUM ];
    long fileStart;
    long fileStop;

//System.out.println( "allocAsync( " + span + " ) --> " + extSpan );
   
    if( tempFAsync == null ) {
      // XXX THIS IS THE PLACE TO OPEN WAVEFORM CACHE FILE
      tempFAsync = createTempFiles();
    }
    synchronized( tempFAsync ) {
      for( int i = 0; i < SUBNUM; i++ ) {
        fileStart    = tempFAsync[ i ].getFrameNum();
        fileStop    = fileStart + (extSpan.getLength() >> decimHelps[ i ].shift);
        tempFAsync[ i ].setFrameNum( fileStop );
        fileSpans[ i = new Span( fileStart, fileStop );
//System.out.println( "... fileSpan =  " + fileSpans[ i ] + " ( i = " + i + "; shift = " + decimHelps[ i ].shift + " )" );
        biasedSpans[ i ] = extSpan;
      }
    }
    return new DecimatedStake( extSpan, tempFAsync, fileSpans, biasedSpans, decimHelps );
  }

  protected File[] createCacheFileNames()
  {
    final AudioFile[] audioFiles = fullScale.getAudioFiles();
    if( (audioFiles.length == 0) || (audioFiles[0] == null) ) return null;

    final CacheManager cm = PrefCacheManager.getInstance();
    if( !cm.isActive() ) return null;

    final File[] f = new File[ audioFiles.length ];
    for( int i = 0; i < f.length; i++ ) {
      f[i] = cm.createCacheFileName( IOUtil.setFileSuffix( audioFiles[i].getFile(), "fft" ));
    }
    return f;
  }

/*
  private int[][] createCacheChannelMaps()
  {
    final int[][] fullChanMaps  = fullScale.getChannelMaps();
    final int[][] cacheChanMaps  = new int[ fullChanMaps.length ][];

    for( int i = 0; i < fullChanMaps.length; i++ ) {
//      System.out.println( "fullChanMaps[ " + i + " ] = " );
//      for( int k = 0; k < fullChanMaps[ i ].length; k++ ) {
//        System.out.println( "  " + fullChanMaps[ i ][ k ]);
//      }
      cacheChanMaps[ i ] = new int[ fullChanMaps[ i ].length * modelChannels ];
      for( int j = 0; j < cacheChanMaps[ i ].length; j++ ) {
        cacheChanMaps[ i ][ j ] = j;
      }
//      System.out.println( "cacheChanMaps[ " + i + " ] = " );
//      for( int k = 0; k < cacheChanMaps[ i ].length; k++ ) {
//        System.out.println( "  " + cacheChanMaps[ i ][ k ]);
//      }
    }

    return cacheChanMaps;
  }
*/
  /*
   * @returns the cached stake or null if no cache file is available
   */
/*
  private AudioStake openCacheForRead( int model )
  throws IOException
  {
    final File[]    f      = createCacheFileNames();
    if( f == null ) return null;

    final AudioFile[]  audioFiles  = fullScale.getAudioFiles();
    final Span[]    fileSpans  = new Span[ audioFiles.length ];
    final AudioFile[]  cacheAFs  = new AudioFile[ audioFiles.length ];
    final String    ourCode    = AbstractApplication.getApplication().getMacOSCreator();
    final int[][]    channelMaps  = createCacheChannelMaps();
    AudioStake      result    = null;
    AudioFileDescr    afd;
    byte[]        appCode;
    AudioFileCacheInfo  infoA, infoB;

    try {
      for( int i = 0; i < cacheAFs.length; i++ ) {
// System.out.println( "openCacheForRead checking '" + f[ i ].getAbsolutePath() + "'" );
       
        if( !f[ i ].isFile() ) return null;
        cacheAFs[ i ] = AudioFile.openAsRead( f[ i ]);
        cacheAFs[ i ].readAppCode();
        afd = cacheAFs[ i ].getDescr();
        final long expected = ((audioFiles[ i ].getFrameNum() + MAXCEILADD) & MAXMASK) >> decimHelps[ 0 ].shift;
        // System.out.println( "expected " + expected+ "; cacheF " +
        // cacheAFs[ i ].getFile().getAbsolutePath() );
        if( expected != afd.length ) {
          // System.err.println( "expected numFrames = "+ expected +
          // ", but got " + afd.length );
          return null;
        }
        appCode = (byte[]) afd.getProperty( AudioFileDescr.KEY_APPCODE );
        // System.err.println( "ourCode = '" + ourCode + "'; afd.appCode
        // = '" + afd.appCode + "'; appCode = '" + appCode + "'" );
        if( ourCode.equals( afd.appCode ) && (appCode != null) ) {
          infoA = AudioFileCacheInfo.decode( appCode );
          if( infoA != null ) {
            infoB = new AudioFileCacheInfo( audioFiles[ i ], model, audioFiles[ i ].getFrameNum() );
            if( !infoA.equals( infoB )) {
              // System.err.println( "info mismatch!" );
              return null;
            }
            // System.err.println( "ok. numChans = " +
            // infoA.getNumChannels() );
          } else {
            return null;
          }
        } else {
          return null;
        }
        fileSpans[ i ] = new Span( 0, cacheAFs[ i ].getFrameNum() );
      }
      // XXX WE NEED A WAY TO CLOSE THE FILES UPON STAKE DISPOSAL XXX
      if( channelMaps.length == 1 ) {
        result = new InterleavedAudioStake( fileSpans[ 0 ], cacheAFs[ 0 ], fileSpans[ 0 ]);
      } else {
        result = new MultiMappedAudioStake( fileSpans[ 0 ], cacheAFs, fileSpans, channelMaps );
      }
      return result;
    } finally {
      if( result == null ) {
        for( int i = 0; i < cacheAFs.length; i++ ) {
          if( cacheAFs[ i ] != null ) {
            cacheAFs[ i ].cleanUp();
            // if( !cacheAFs[ i ].getFile().delete() ) {
            // cacheAFs[ i ].getFile().deleteOnExit();
            // }
          }
        }
      }
    }
  }
*/

/*
  private AudioStake openCacheForWrite( int model, long decimFrameNum )
  throws IOException
  {
    final File[]      f      = createCacheFileNames();
    if( f == null ) return null;

    final AudioFile[]    audioFiles  = fullScale.getAudioFiles();
    final AudioFileDescr  afdProto  = new AudioFileDescr();
    final CacheManager    cm      = PrefCacheManager.getInstance();
    final Span[]      fileSpans  = new Span[ audioFiles.length ];
    final AudioFile[]    cacheAFs  = new AudioFile[ audioFiles.length ];
    final String      ourCode    = AbstractApplication.getApplication().getMacOSCreator();
    final int[][]      channelMaps  = fullScale.getChannelMaps(); // createCacheChannelMaps();
    AudioStake        result    = null;
    AudioFileDescr      afd;
    AudioFileCacheInfo    info;

    afdProto.type      = AudioFileDescr.TYPE_AIFF;
    afdProto.bitsPerSample  = 32;
    afdProto.sampleFormat  = AudioFileDescr.FORMAT_FLOAT;
    afdProto.rate      = decimHelps[ 0 ].rate; // getRate();
    afdProto.appCode    = ourCode;

    try {
      for( int i = 0; i < f.length; i++ ) {
        cm.removeFile( f[ i ]); // in case it existed
// System.out.println( "openCacheForWrite doing '" + f[ i ].getAbsolutePath() + "'" );
        afd        = new AudioFileDescr( afdProto );
        afd.channels  = channelMaps[ i ].length;
        // System.out.println( "channels = " + afd.channels );
        afd.file    = f[ i ];
        info      = new AudioFileCacheInfo( audioFiles[ i ], model, audioFiles[ i ].getFrameNum() );
        afd.setProperty( AudioFileDescr.KEY_APPCODE, info.encode() );
        cacheAFs[ i ]  = AudioFile.openAsWrite( afd );
        fileSpans[ i ]  = new Span( 0, decimFrameNum );
      }
      // XXX WE NEED A WAY TO CLOSE THE FILES UPON STAKE DISPOSAL XXX
      if( channelMaps.length == 1 ) {
        result = new InterleavedAudioStake( fileSpans[ 0 ], cacheAFs[ 0 ], fileSpans[ 0 ]);
      } else {
        result = new MultiMappedAudioStake( fileSpans[ 0 ], cacheAFs, fileSpans, channelMaps);
      }
      // System.err.println( "Cache was written" );
      return result;
    } finally {
      if( result == null ) {
        for( int i = 0; i < cacheAFs.length; i++ ) {
          if( cacheAFs[ i ] != null ) {
            cacheAFs[ i ].cleanUp();
            if( !cacheAFs[ i ].getFile().delete() ) {
              cacheAFs[ i ].getFile().deleteOnExit();
            }
          }
        }
      }
    }
  }
*/

  /*
   * This is invoked by insert(). it subsamples the given buffer for all
   * subsample STEs and writes it out using continueWrite; therefore the call
   * to this method should be bracketed with beginInsert() and finishWrite().
   * len must be an integer muliple of MAXCOARSE !
   *
   * inBuf == null indicates cache skip
   */
  // private void subsampleWrite( float[][] inBuf, float[][] outBuf,
  // DecimatedStake das, int len )
  protected void subsampleWrite( float[][] inBuf, float[][] outBuf, DecimatedStake das,
                   int len, AudioStake cacheAS, long cacheOff )
  throws IOException
  {
    int decim;

    if( SUBNUM < 1 ) return;

    decim = decimHelps[ 0 ].shift;
    // calculate first decimation from fullrate PCM
    assert len % fftSize == 0;
//    len >>= decim;
    len = (len / stepSize * modelChannels) >> decim;
    if( inBuf != null ) {
//      System.out.println( "decimator.decimatePCM( inBuf, outBuf, fftBuf, 0, " + len + ", " + (1 << decim) + " )" );
      decimator.decimatePCM( inBuf, outBuf, 0, len, 1 << decim );
//      System.out.println( "doing" );
      das.continueWrite( 0, outBuf, 0, len );
      if( cacheAS != null ) {
        cacheAS.writeFrames( outBuf, 0, new Span( cacheOff, cacheOff + len ));
      }
    }

    subsampleWrite2( outBuf, das, len );
  }

  // same as subsampleWrite but input is already at first decim stage
  private void subsampleWrite2( float[][] buf, DecimatedStake das, int len )
  throws IOException
  {
    int decim;

    // calculate remaining decimations from preceding ones
    for( int i = 1; i < SUBNUM; i++ ) {
      decim = decimHelps[ i ].shift - decimHelps[ i - 1 ].shift;
      len >>= decim;
      // framesWritten >>= decim;
      decimator.decimate( buf, buf, 0, len, 1 << decim );
      // ste[i].continueWrite( ts[i], framesWritten, outBuf, 0, len );
      das.continueWrite( i, buf, 0, len );
    } // for( SUBNUM )
  }

  // ---------------------- decimation subclasses ----------------------

  private abstract class Decimator
  {
    protected Decimator() { /* empty */ }
   
    protected abstract void decimate( float[][] inBuf, float[][] outBuf, int outOff, int len, int decim );
    protected abstract void decimatePCM( float[][] inBuf, float[][] outBuf, int outOff, int len, int decim );
    // protected abstract void decimatePCMFast( float[][] inBuf, float[][]
    // outBuf, int outOff, int len, int decim );
    protected abstract int draw( DecimationInfo info, int ch, int[][] peakPolyX, int[][] peakPolyY,
                     int[][] rmsPolyX, int[][] rmsPolyY, int decimLen,
                     Rectangle r, float deltaYN, int off );
  }

  private class SonaDecimator
  extends Decimator
  {
    protected SonaDecimator() { /* empty */ }
   
    protected void decimate( float[][] inBuf, float[][] outBuf, int outOff, int len, int decim )
    {
      int   stop, j, k, m, ch;
      float  f1;
      float[]  inBufCh, outBufCh;

      for( ch = 0; ch < fullChannels; ch++ ) {
        inBufCh    = inBuf[ ch ];
        outBufCh  = outBuf[ ch ];

        for( j = outOff, stop = outOff + len, k = 0; j < stop; j++ ) {
          f1 = inBufCh[ k ];
          for( m = k + decim, k++; k < m; k++ ) {
            f1 += inBufCh[ k ];
          }
          outBufCh[ j ] = f1 / decim;
        }
      }
    }

    protected void decimatePCM( float[][] inBuf, float[][] outBuf, int outOff, int len, int decim )
    {
      final float w = 1.0f / decim;
     
//      for( int inOff = 0, stop = outOff + len, decimCnt = 0; outOff < stop; inOff += stepSize ) {
//        for( int ch = 0; ch < fullChannels; ch++ ) {
//          constQ.transform( inBuf[ ch ], inOff, Math.max( 0, inBuf[ ch ].length - inOff ), outBuf[ ch ], outOff, decimCnt > 0, w );
//        }
//        decimCnt = (decimCnt + 1) % decim;
//        if( decimCnt == 0 ) outOff += modelChannels;
//      }

//int gaga = fftSize;
      for( int inOff = 0, stop = outOff + len, decimCnt = 0; outOff < stop; inOff += stepSize ) {
        for( int ch = 0, outChanOff = 0; ch < fullChannels; ch++ ) {
//System.out.println( "calling with " + inBuf[ ch].length + " -- " + inOff + " ; " + fftSize + "; " + filterBuf.length + " -- " + constQ.getNumKernels() );

//          if( doSlide ) {
//            slide.next( inBuf[ ch ], inOff, stepSize, ch, constQ.getFFTBuffer() );
//            constQ.convolve( filterBuf, 0 );
//          } else {
            constQ.transform( inBuf[ ch ], inOff, fftSize, filterBuf, 0 );
//          }
          if( decimCnt == 0 ) {
            for( int i = 0; i < numKernels; i++ ) {
              outBuf[ outChanOff++ ][ outOff ] = filterBuf[ i ] * w;
            }
          } else {
            for( int i = 0; i < numKernels; i++ ) {
              outBuf[ outChanOff++ ][ outOff ] += filterBuf[ i ] * w;
            }
          }
        }
        decimCnt = (decimCnt + 1) % decim;
        if( decimCnt == 0 ) outOff++;
      }
    }
   
    protected int draw( DecimationInfo info, int ch,
                int[][] peakPolyX, int[][] peakPolyY,
                int[][] rmsPolyX, int[][] rmsPolyY, int decimLen,
                Rectangle r, float deltaYN, int off )
    {
      int      ch2;
      float[]    sPeakP, sPeakN, sRMSP;
      float    offX, scaleX, scaleY;
     
      ch2    = ch * 3;
      sPeakP  = tmpBuf[ ch2++ ];
      sPeakN  = tmpBuf[ ch2++ ];
      sRMSP  = tmpBuf[ ch2 ];
      scaleX  = 4 * r.width / (float) (info.sublength - 1);
      scaleY  = r.height * deltaYN;
      offX  = scaleX * off;
     
      return drawFullWavePeakRMS( sPeakP, sPeakN,
                    sRMSP, decimLen, peakPolyX[ ch ],
                    peakPolyY[ ch ], rmsPolyX[ ch ],
                    rmsPolyY[ ch ], off, offX, scaleX,
                    scaleY );
    }
   
    private int drawFullWavePeakRMS( float[] sPeakP, float[] sPeakN,
        float[] sRMS, int len, int[] peakPolyX, int[] peakPolyY,
        int[] rmsPolyX, int[] rmsPolyY, int off, float offX, float scaleX,
        float scaleY )
    {
      // final float scaleYN = -scaleY;
      int    x;
      float  peakP, peakN, rms;

      for( int i = 0, k = peakPolyX.length - 1 - off; i < len; i++, off++, k-- ) {
        x          = (int) (i * scaleX + offX);
        peakPolyX[ off = x;
        peakPolyX[ k ]    = x;
        rmsPolyX[ off ]    = x;
        rmsPolyX[ k ]    = x;
        peakP        = sPeakP[ i ];
        peakN        = sPeakN[ i ];
        peakPolyY[ off = (int) (peakP * scaleY) + 2;
        peakPolyY[ k ]    = (int) (peakN * scaleY) - 2;
        // peakC = (peakP + peakN) / 2;
        rms          = (float) Math.sqrt( sRMS[ i ]); // / 2;
        rmsPolyY[ off ]    = (int) (Math.min( peakP, rms ) * scaleY);
        rmsPolyY[ k ]    = (int) (Math.max( peakN, -rms ) * scaleY);
      }

      return off;
    }
  } // class FullPeakRMSDecimator
}
TOP

Related Classes of de.sciss.eisenkraut.io.DecimatedSonaTrail$Decimator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
ew');