/*
* WaveformView.java
* Eisenkraut
*
* Copyright (c) 2004-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.eisenkraut.gui;
import de.sciss.eisenkraut.io.AudioTrail;
import de.sciss.eisenkraut.io.DecimatedSonaTrail;
import de.sciss.eisenkraut.io.DecimatedWaveTrail;
import de.sciss.eisenkraut.io.DecimationInfo;
import de.sciss.eisenkraut.session.Session;
import de.sciss.eisenkraut.util.PrefsUtil;
import de.sciss.gui.ComponentHost;
import de.sciss.io.Span;
import de.sciss.util.Disposable;
import javax.swing.*;
import java.awt.*;
@SuppressWarnings("serial")
public class WaveformView
extends JComponent
implements Disposable {
private int fullChannels;
private Insets insets = new Insets( 0, 0, 0, 0 );
private int vGap = 1;
private Rectangle r = new Rectangle();
private static final Paint pntNull = new Color(0x7F, 0x7F, 0x00, 0xC0);
private static final Stroke strkNull = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL,
1.0f, new float[]{4.0f, 4.0f}, 0.0f);
private int verticalScale = PrefsUtil.VSCALE_AMP_LIN;
private float ampLinMin = -1.0f; // minimum vector value
private float ampLinMax = 1.0f; // maximum vector value
private float ampLogMin = -60f;
private float ampLogMax = 0f;
private float freqMin = 27.5f;
private float freqMax = 20000f;
private boolean nullLine = false;
private final Session doc;
private Span viewSpan = new Span();
private DecimationInfo info = null; // most recent one!
private final ComponentHost host;
public WaveformView(Session doc) {
this(doc, null);
}
public WaveformView(Session doc, ComponentHost host) {
super();
this.host = host;
final AudioTrail at = doc.getAudioTrail();
fullChannels = at.getChannelNum();
// final int[] channelMap = new int[fullChannels];
// for (int i = 0; i < fullChannels; i++) {
// channelMap[i] = i;
// }
this.doc = doc;
}
public void setNullLine(boolean onOff) {
if (nullLine != onOff) {
nullLine = onOff;
triggerRedisplay();
}
}
public boolean getNullLine() {
return nullLine;
}
public void setVerticalScale(int mode) {
if (verticalScale != mode) {
verticalScale = mode;
triggerRedisplay();
}
}
public int getVerticalScale() {
return verticalScale;
}
/**
* Gets the minimum allowed y value
*
* @return the minimum specified function value
*/
public float getAmpLinMin()
{
return ampLinMin;
}
/**
* Gets the maximum allowed y value
*
* @return the maximum specified function value
*/
public float getAmpLinMax()
{
return ampLinMax;
}
public float getAmpLogMin()
{
return ampLogMin;
}
public float getAmpLogMax()
{
return ampLogMax;
}
/**
* Changes the allowed range for vector values.
* Influences the graphics display such that
* the top margin of the panel corresponds to max
* and the bottom margin corresponds to min. Also
* user drawings are limited to these values unless
* wrapY is set to true (not yet implemented).
*
* Warning: the current vector is left untouched,
* even if values lie outside the new
* allowed range.
*
* @param min new minimum y value
* @param max new maximum y value
*/
public void setAmpLinMinMax(float min, float max) {
if ((this.ampLinMin != min) || (this.ampLinMax != max)) {
this.ampLinMin = min;
this.ampLinMax = max;
if (verticalScale == PrefsUtil.VSCALE_AMP_LIN) triggerRedisplay();
}
}
public void setAmpLogMinMax(float min, float max) {
if ((this.ampLogMin != min) || (this.ampLogMax != max)) {
this.ampLogMin = min;
this.ampLogMax = max;
if (verticalScale != PrefsUtil.VSCALE_AMP_LIN) triggerRedisplay();
}
}
public float getFreqMin() {
return freqMin;
}
public float getFreqMax() {
return freqMax;
}
public void setFreqMinMax(float min, float max) {
if ((this.freqMin != min) || (this.freqMax != max)) {
this.freqMin = min;
this.freqMax = max;
if (verticalScale == PrefsUtil.VSCALE_FREQ_SPECT) triggerRedisplay();
}
}
public void update(Span s) {
viewSpan = s;
triggerRedisplay();
}
public int getNumChannels()
{
return fullChannels;
}
/**
* Synchronization: this uses and alters one internal rectangle object,
* be sure to not use this rectangle outside the swing thread,
* otherwise make a copy. do not modify the returned rectangle
*/
public Rectangle rectForChannel(int ch) {
final int ht = getHeight() - (insets.top + insets.bottom);
final int temp = ht * ch / fullChannels;
final int y = insets.top + temp;
final int h = (ht * (ch + 1) / fullChannels) - temp - vGap;
r.setBounds(insets.left, y, getWidth() - (insets.left + insets.right), h);
return r;
}
public int channelForPoint(Point p) {
final int py = p.y - insets.top;
final int ht = getHeight();
int y1 = 0;
int y2;
for( int ch = 0; ch < fullChannels; ch++ ) {
y2 = ht * (ch + 1) / fullChannels;
if( (py >= y1) && (py < (y2 - vGap)) ) return ch;
y1 = y2;
}
return -1;
}
public DecimationInfo getDecimationInfo() { return info; }
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (viewSpan.isEmpty()) return;
final Graphics2D g2 = (Graphics2D) g;
switch (verticalScale) {
case PrefsUtil.VSCALE_AMP_LIN:
paintAmpLin(g2);
break;
case PrefsUtil.VSCALE_AMP_LOG:
paintAmpLog(g2);
break;
case PrefsUtil.VSCALE_FREQ_SPECT:
paintFreqSpect(g2);
break;
default:
assert false : verticalScale;
}
}
private void paintAmpLin(Graphics2D g2) {
final DecimatedWaveTrail dt = doc.getDecimatedWaveTrail();
if (dt == null) return;
final int w = getWidth();
Rectangle cr;
int y;
info = dt.getBestSubsample(new Span(viewSpan.start, viewSpan.stop + 1), w);
dt.drawWaveform(info, this, g2);
if (nullLine) {
g2.setPaint(pntNull);
g2.setStroke(strkNull);
for (int ch = 0; ch < fullChannels; ch++) {
cr = rectForChannel(ch);
y = cr.y + (cr.height >> 1);
g2.drawLine(cr.x, y, cr.x + cr.width, y);
}
}
}
private void paintAmpLog(Graphics2D g2) {
final DecimatedWaveTrail dt = doc.getDecimatedWaveTrail();
if (dt == null) return;
final int w = getWidth();
Rectangle cr;
int y;
info = dt.getBestSubsample( new Span( viewSpan.start, viewSpan.stop + 1 ), w );
dt.drawWaveform( info, this, g2 );
if (nullLine) {
g2.setPaint(pntNull);
g2.setStroke(strkNull);
for (int ch = 0; ch < fullChannels; ch++) {
cr = rectForChannel(ch);
y = cr.y + cr.height - 1;
g2.drawLine(cr.x, y, cr.x + cr.width, y);
}
}
}
private void paintFreqSpect(Graphics2D g2) {
final DecimatedSonaTrail dt = doc.getDecimatedSonaTrail();
if (dt == null) return;
final int w = getWidth();
info = dt.getBestSubsample(new Span(viewSpan.start, viewSpan.stop + 1), w);
dt.drawWaveform(info, this, g2);
}
private void triggerRedisplay() {
if (host != null) {
host.update(this);
} else if (isVisible()) {
repaint();
}
}
// -------------- Disposable interface --------------
public void dispose() { /* empty */ }
}