1623 lines
48 KiB
Java
1623 lines
48 KiB
Java
// BarWaves.java (C) 2001 by Paul Falstad, www.falstad.com
|
|
|
|
import javax.sound.sampled.AudioFormat;
|
|
import javax.sound.sampled.AudioSystem;
|
|
import javax.sound.sampled.DataLine;
|
|
import javax.sound.sampled.SourceDataLine;
|
|
import java.awt.*;
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.awt.event.AdjustmentEvent;
|
|
import java.awt.event.AdjustmentListener;
|
|
import java.awt.event.ComponentEvent;
|
|
import java.awt.event.ComponentListener;
|
|
import java.awt.event.ItemEvent;
|
|
import java.awt.event.ItemListener;
|
|
import java.awt.event.MouseEvent;
|
|
import java.awt.event.MouseListener;
|
|
import java.awt.event.MouseMotionListener;
|
|
import java.util.Random;
|
|
import java.util.Vector;
|
|
|
|
class BarWavesCanvas extends Canvas {
|
|
BarWavesFrame pg;
|
|
|
|
BarWavesCanvas(BarWavesFrame p) {
|
|
pg = p;
|
|
}
|
|
|
|
public Dimension getPreferredSize() {
|
|
return new Dimension(300, 400);
|
|
}
|
|
|
|
public void update(Graphics g) {
|
|
pg.updateBarWaves(g);
|
|
}
|
|
|
|
public void paint(Graphics g) {
|
|
pg.updateBarWaves(g);
|
|
}
|
|
};
|
|
|
|
class BarWavesLayout implements LayoutManager {
|
|
public BarWavesLayout() {
|
|
}
|
|
|
|
public void addLayoutComponent(String name, Component c) {
|
|
}
|
|
|
|
public void removeLayoutComponent(Component c) {
|
|
}
|
|
|
|
public Dimension preferredLayoutSize(Container target) {
|
|
return new Dimension(500, 500);
|
|
}
|
|
|
|
public Dimension minimumLayoutSize(Container target) {
|
|
return new Dimension(100, 100);
|
|
}
|
|
|
|
public void layoutContainer(Container target) {
|
|
int barwidth = 0;
|
|
int i;
|
|
for (i = 1; i < target.getComponentCount(); i++) {
|
|
Component m = target.getComponent(i);
|
|
if (m.isVisible()) {
|
|
Dimension d = m.getPreferredSize();
|
|
if (d.width > barwidth) {
|
|
barwidth = d.width;
|
|
}
|
|
}
|
|
}
|
|
Insets insets = target.insets();
|
|
int targetw = target.size().width - insets.left - insets.right;
|
|
int cw = targetw - barwidth;
|
|
int targeth = target.size().height - (insets.top + insets.bottom);
|
|
target.getComponent(0).move(insets.left, insets.top);
|
|
target.getComponent(0).resize(cw, targeth);
|
|
cw += insets.left;
|
|
int h = insets.top;
|
|
for (i = 1; i < target.getComponentCount(); i++) {
|
|
Component m = target.getComponent(i);
|
|
if (m.isVisible()) {
|
|
Dimension d = m.getPreferredSize();
|
|
if (m instanceof Scrollbar) {
|
|
d.width = barwidth;
|
|
}
|
|
if (m instanceof Label) {
|
|
h += d.height / 5;
|
|
d.width = barwidth;
|
|
}
|
|
m.move(cw, h);
|
|
m.resize(d.width, d.height);
|
|
h += d.height;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class BarWavesFrame extends Frame
|
|
implements ComponentListener, ActionListener, AdjustmentListener,
|
|
MouseMotionListener, MouseListener, ItemListener {
|
|
|
|
Thread engine = null;
|
|
|
|
Dimension winSize;
|
|
Image dbimage;
|
|
|
|
Random random;
|
|
int maxTerms = 50;
|
|
int modeCount;
|
|
int maxMaxTerms = 90;
|
|
int sampleCount;
|
|
double modeTable[][];
|
|
double modeNorms[];
|
|
public static final double epsilon = .0000001;
|
|
public static final double epsilon2 = .003;
|
|
|
|
public String getAppletInfo() {
|
|
return "BarWaves by Paul Falstad";
|
|
}
|
|
|
|
Button sineButton;
|
|
Button blankButton;
|
|
Checkbox stoppedCheck;
|
|
Checkbox soundCheck;
|
|
Choice modeChooser;
|
|
Choice setupChooser;
|
|
Vector setupList;
|
|
Setup setup;
|
|
Choice displayChooser;
|
|
Scrollbar dampingBar;
|
|
Scrollbar speedBar;
|
|
Scrollbar loadBar;
|
|
Scrollbar baseFreqBar;
|
|
Scrollbar stiffnessBar;
|
|
double magcoef[];
|
|
double dampcoef[];
|
|
double phasecoef[];
|
|
double phasecoefcos[];
|
|
double phasecoefadj[];
|
|
double omega[];
|
|
static final double pi = 3.14159265358979323846;
|
|
double step;
|
|
double func[];
|
|
double funci[];
|
|
int thickness[];
|
|
int xpoints[], ypoints[];
|
|
int selectedCoef;
|
|
int magnitudesY;
|
|
static final int SEL_NONE = 0;
|
|
static final int SEL_FUNC = 1;
|
|
static final int SEL_MAG = 2;
|
|
static final int MODE_SHAPE = 0;
|
|
static final int MODE_FORCE = 1;
|
|
static final int MODE_THICKNESS = 2;
|
|
static final int DISP_PHASE = 0;
|
|
static final int DISP_PHASECOS = 1;
|
|
static final int DISP_MODES = 2;
|
|
static final int BOUND_HINGED = 0;
|
|
static final int BOUND_FREE = 1;
|
|
static final int BOUND_CLAMPED = 2;
|
|
int selection;
|
|
int dragX, dragY;
|
|
boolean dragging;
|
|
boolean java2present;
|
|
double t;
|
|
int pause;
|
|
Color gray1 = new Color(76, 76, 76);
|
|
Color gray2 = new Color(127, 127, 127);
|
|
|
|
int getrand(int x) {
|
|
int q = random.nextInt();
|
|
if (q < 0) {
|
|
q = -q;
|
|
}
|
|
return q % x;
|
|
}
|
|
|
|
BarWavesCanvas cv;
|
|
|
|
BarWavesFrame() {
|
|
super("Bar Waves Applet");
|
|
}
|
|
|
|
public void init() {
|
|
java2present = true;
|
|
if (System.getProperty("java.version").indexOf("1.1") == 0) {
|
|
java2present = false;
|
|
}
|
|
setupList = new Vector();
|
|
Setup s = new FreeBarSetup();
|
|
while (s != null) {
|
|
setupList.addElement(s);
|
|
s = s.createNext();
|
|
}
|
|
selectedCoef = -1;
|
|
setLayout(new BarWavesLayout());
|
|
cv = new BarWavesCanvas(this);
|
|
cv.addComponentListener(this);
|
|
cv.addMouseMotionListener(this);
|
|
cv.addMouseListener(this);
|
|
add(cv);
|
|
|
|
setupChooser = new Choice();
|
|
int i;
|
|
for (i = 0; i != setupList.size(); i++) {
|
|
setupChooser.add("Setup: " +
|
|
((Setup) setupList.elementAt(i)).getName());
|
|
}
|
|
setup = (Setup) setupList.elementAt(0);
|
|
setupChooser.addItemListener(this);
|
|
add(setupChooser);
|
|
|
|
add(sineButton = new Button("Fundamental"));
|
|
sineButton.addActionListener(this);
|
|
add(blankButton = new Button("Clear"));
|
|
blankButton.addActionListener(this);
|
|
stoppedCheck = new Checkbox("Stopped");
|
|
stoppedCheck.addItemListener(this);
|
|
add(stoppedCheck);
|
|
soundCheck = new Checkbox("Sound", false);
|
|
soundCheck.addItemListener(this);
|
|
add(soundCheck);
|
|
|
|
modeChooser = new Choice();
|
|
modeChooser.add("Mouse = Shape bar");
|
|
modeChooser.add("Mouse = Apply static force");
|
|
modeChooser.addItemListener(this);
|
|
add(modeChooser);
|
|
|
|
displayChooser = new Choice();
|
|
displayChooser.add("Display Phases");
|
|
displayChooser.add("Display Phase Cosines");
|
|
displayChooser.add("Display Modes");
|
|
displayChooser.addItemListener(this);
|
|
add(displayChooser);
|
|
|
|
add(new Label("Simulation Speed", Label.CENTER));
|
|
add(speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 166, 1, 24, 300));
|
|
speedBar.addAdjustmentListener(this);
|
|
|
|
add(new Label("Damping", Label.CENTER));
|
|
add(dampingBar = new Scrollbar(Scrollbar.HORIZONTAL, 10, 1, 0, 400));
|
|
dampingBar.addAdjustmentListener(this);
|
|
|
|
add(new Label("Resolution", Label.CENTER));
|
|
add(loadBar = new Scrollbar(Scrollbar.HORIZONTAL,
|
|
maxTerms, 1, 40, maxMaxTerms
|
|
));
|
|
loadBar.addAdjustmentListener(this);
|
|
setLoadCount();
|
|
|
|
add(new Label("Base Frequency", Label.CENTER));
|
|
add(baseFreqBar = new Scrollbar(Scrollbar.HORIZONTAL,
|
|
84, 12, 30, 168
|
|
));
|
|
baseFreqBar.addAdjustmentListener(this);
|
|
baseFreqBar.disable();
|
|
|
|
add(new Label("String Stiffness", Label.CENTER));
|
|
add(stiffnessBar = new Scrollbar(Scrollbar.HORIZONTAL, 10, 1, 0, 100));
|
|
stiffnessBar.addAdjustmentListener(this);
|
|
stiffnessBar.disable();
|
|
|
|
try {
|
|
String param = null; //applet.getParameter("PAUSE");
|
|
if (param != null) {
|
|
pause = Integer.parseInt(param);
|
|
}
|
|
} catch (Exception e) {
|
|
}
|
|
|
|
magcoef = new double[maxMaxTerms];
|
|
phasecoef = new double[maxMaxTerms];
|
|
phasecoefcos = new double[maxMaxTerms];
|
|
phasecoefadj = new double[maxMaxTerms];
|
|
func = new double[maxMaxTerms + 1];
|
|
funci = new double[maxMaxTerms + 1];
|
|
xpoints = new int[4];
|
|
ypoints = new int[4];
|
|
|
|
random = new Random();
|
|
setDamping();
|
|
reinit();
|
|
cv.setBackground(Color.black);
|
|
cv.setForeground(Color.lightGray);
|
|
resize(500, 500);
|
|
handleResize();
|
|
show();
|
|
}
|
|
|
|
void reinit() {
|
|
doFundamental();
|
|
}
|
|
|
|
void handleResize() {
|
|
Dimension d = winSize = cv.getSize();
|
|
if (winSize.width == 0) {
|
|
return;
|
|
}
|
|
dbimage = createImage(d.width, d.height);
|
|
}
|
|
|
|
void doFundamental() {
|
|
doBlank();
|
|
magcoef[0] = 1;
|
|
if (soundCheck.getState()) {
|
|
doPlay();
|
|
}
|
|
}
|
|
|
|
void doBlank() {
|
|
int x;
|
|
for (x = 0; x <= sampleCount; x++) {
|
|
func[x] = 0;
|
|
}
|
|
transform(true);
|
|
}
|
|
|
|
void transform(boolean novel) {
|
|
int x, y;
|
|
t = 0;
|
|
for (y = 0; y != modeCount; y++) {
|
|
double a = 0;
|
|
double b = 0;
|
|
for (x = 1; x != sampleCount; x++) {
|
|
a += modeTable[x][y] * func[x];
|
|
b -= modeTable[x][y] * funci[x];
|
|
}
|
|
a /= modeNorms[y];
|
|
b /= omega[y] * modeNorms[y];
|
|
if (a < epsilon && a > -epsilon) {
|
|
a = 0;
|
|
}
|
|
if (b < epsilon && b > -epsilon) {
|
|
b = 0;
|
|
}
|
|
if (novel) {
|
|
b = 0;
|
|
}
|
|
double r = java.lang.Math.sqrt(a * a + b * b);
|
|
magcoef[y] = r;
|
|
double ph2 = java.lang.Math.atan2(b, a);
|
|
phasecoefadj[y] = ph2;
|
|
phasecoef[y] = ph2;
|
|
}
|
|
}
|
|
|
|
int getPanelHeight() {
|
|
return winSize.height / 3;
|
|
}
|
|
|
|
void centerString(Graphics g, String s, int y) {
|
|
FontMetrics fm = g.getFontMetrics();
|
|
g.drawString(s, (winSize.width - fm.stringWidth(s)) / 2, y);
|
|
}
|
|
|
|
public void paint(Graphics g) {
|
|
cv.repaint();
|
|
}
|
|
|
|
public void updateBarWaves(Graphics realg) {
|
|
if (!java2present) {
|
|
centerString(realg, "Need java2 for this applet.", 100);
|
|
return;
|
|
}
|
|
if (winSize == null || winSize.width == 0) {
|
|
return;
|
|
}
|
|
Graphics g = dbimage.getGraphics();
|
|
boolean allQuiet = true;
|
|
if (!stoppedCheck.getState()) {
|
|
int val = speedBar.getValue() - 100;
|
|
//if (val > 40)
|
|
//val += getrand(10);
|
|
double tadd = java.lang.Math.exp(val / 20.) * (.1 / 50);
|
|
// add random crap into the time to avoid aliasing
|
|
tadd *= 1 + getrand(300) * (.00191171);
|
|
t += tadd;
|
|
}
|
|
g.setColor(cv.getBackground());
|
|
g.fillRect(0, 0, winSize.width, winSize.height);
|
|
g.setColor(cv.getForeground());
|
|
int i;
|
|
int panelHeight = getPanelHeight();
|
|
int midy = panelHeight / 2;
|
|
int halfPanel = panelHeight / 2;
|
|
double ymult = .75 * halfPanel;
|
|
for (i = -1; i <= 1; i++) {
|
|
g.setColor((i == 0) ? gray2 : gray1);
|
|
g.drawLine(0, midy + (i * (int) ymult),
|
|
winSize.width, midy + (i * (int) ymult)
|
|
);
|
|
}
|
|
g.setColor(gray2);
|
|
g.drawLine(winSize.width / 2, midy - (int) ymult,
|
|
winSize.width / 2, midy + (int) ymult
|
|
);
|
|
int sampStart = (setup.leftBoundary() == BOUND_FREE) ? 1 : 0;
|
|
int sampEnd = sampleCount -
|
|
((setup.rightBoundary() == BOUND_FREE) ? 1 : 0);
|
|
if (dragging && selection == SEL_FUNC) {
|
|
g.setColor(Color.cyan);
|
|
allQuiet = true;
|
|
for (i = sampStart; i <= sampEnd; i++) {
|
|
int x = winSize.width * i / sampleCount;
|
|
int y = midy - (int) (ymult * func[i]);
|
|
drawBarPiece(g, x, y, i, sampStart);
|
|
}
|
|
}
|
|
if (!stoppedCheck.getState() && !dragging) {
|
|
for (i = 0; i != modeCount; i++) {
|
|
magcoef[i] *= dampcoef[i];
|
|
}
|
|
}
|
|
|
|
double magcoefdisp[] = magcoef;
|
|
double phasecoefdisp[] = phasecoef;
|
|
double phasecoefcosdisp[] = phasecoefcos;
|
|
|
|
if (!(dragging && selection == SEL_FUNC)) {
|
|
g.setColor(Color.white);
|
|
int j;
|
|
for (j = 0; j != modeCount; j++) {
|
|
if (magcoef[j] < epsilon && magcoef[j] > -epsilon) {
|
|
magcoef[j] = phasecoef[j] = phasecoefadj[j] = 0;
|
|
continue;
|
|
}
|
|
allQuiet = false;
|
|
phasecoef[j] = (omega[j] * t + phasecoefadj[j]) % (2 * pi);
|
|
if (phasecoef[j] > pi) {
|
|
phasecoef[j] -= 2 * pi;
|
|
} else if (phasecoef[j] < -pi) {
|
|
phasecoef[j] += 2 * pi;
|
|
}
|
|
phasecoefcos[j] = java.lang.Math.cos(phasecoef[j]);
|
|
}
|
|
|
|
for (i = sampStart; i <= sampEnd; i++) {
|
|
int x = winSize.width * i / sampleCount;
|
|
double dy = 0;
|
|
for (j = 0; j != modeCount; j++) {
|
|
dy += magcoefdisp[j] *
|
|
modeTable[i][j] * phasecoefcosdisp[j];
|
|
}
|
|
func[i] = dy;
|
|
int y = midy - (int) (ymult * dy);
|
|
drawBarPiece(g, x, y, i, sampStart);
|
|
}
|
|
if (setup.getThickness() == 0) {
|
|
if (setup.leftBoundary() == BOUND_FREE) {
|
|
drawPin(g, 1, midy, ymult);
|
|
}
|
|
if (setup.rightBoundary() == BOUND_FREE) {
|
|
drawPin(g, sampleCount - 1, midy, ymult);
|
|
}
|
|
}
|
|
}
|
|
if (selectedCoef != -1 && !dragging &&
|
|
(
|
|
magcoefdisp[selectedCoef] > .04 ||
|
|
magcoefdisp[selectedCoef] < -.04
|
|
)) {
|
|
g.setColor(Color.yellow);
|
|
ymult *= magcoefdisp[selectedCoef];
|
|
for (i = sampStart; i <= sampEnd; i++) {
|
|
int x = winSize.width * i / sampleCount;
|
|
double dy = modeTable[i][selectedCoef] *
|
|
phasecoefcosdisp[selectedCoef];
|
|
int y = midy - (int) (ymult * dy);
|
|
drawBarPiece(g, x, y, i, sampStart);
|
|
}
|
|
}
|
|
if (selectedCoef != -1) {
|
|
int f = getFreq(selectedCoef);
|
|
g.setColor(Color.yellow);
|
|
centerString(g, f + " Hz", panelHeight);
|
|
} else if (soundCheck.getState()) {
|
|
int f = getFreq(0);
|
|
g.setColor(Color.white);
|
|
centerString(g, "Fundamental = " + f + " Hz", panelHeight);
|
|
}
|
|
int termWidth = getTermWidth();
|
|
ymult = .6 * halfPanel;
|
|
g.setColor(Color.white);
|
|
if (displayChooser.getSelectedIndex() == DISP_PHASE ||
|
|
displayChooser.getSelectedIndex() == DISP_PHASECOS) {
|
|
magnitudesY = panelHeight;
|
|
} else {
|
|
magnitudesY = panelHeight * 2;
|
|
}
|
|
midy = magnitudesY + (panelHeight / 2) + (int) ymult / 2;
|
|
g.setColor(gray2);
|
|
g.drawLine(0, midy, winSize.width, midy);
|
|
g.setColor(gray1);
|
|
g.drawLine(0, midy - (int) ymult, winSize.width, midy - (int) ymult);
|
|
g.drawLine(0, midy + (int) ymult, winSize.width, midy + (int) ymult);
|
|
g.drawLine(0, midy - (int) ymult / 4, winSize.width, midy - (int) ymult / 4);
|
|
g.drawLine(0, midy + (int) ymult / 4, winSize.width, midy + (int) ymult / 4);
|
|
int dotSize = termWidth - 3;
|
|
if (dotSize < 3) {
|
|
dotSize = 3;
|
|
}
|
|
for (i = 0; i != modeCount; i++) {
|
|
int t = termWidth * i + termWidth / 2;
|
|
int y = midy - (int) (logcoef(magcoefdisp[i]) * ymult);
|
|
g.setColor(i == selectedCoef ? Color.yellow : Color.white);
|
|
g.drawLine(t, midy, t, y);
|
|
g.fillOval(t - dotSize / 2, y - dotSize / 2, dotSize, dotSize);
|
|
}
|
|
|
|
if (displayChooser.getSelectedIndex() == DISP_PHASE ||
|
|
displayChooser.getSelectedIndex() == DISP_PHASECOS) {
|
|
g.setColor(Color.white);
|
|
boolean cosines =
|
|
displayChooser.getSelectedIndex() == DISP_PHASECOS;
|
|
ymult = .75 * halfPanel;
|
|
midy = ((panelHeight * 5) / 2);
|
|
for (i = -2; i <= 2; i++) {
|
|
if (cosines && (i == 1 || i == -1)) {
|
|
continue;
|
|
}
|
|
g.setColor((i == 0) ? gray2 : gray1);
|
|
g.drawLine(0, midy + (i * (int) ymult) / 2,
|
|
winSize.width, midy + (i * (int) ymult) / 2
|
|
);
|
|
}
|
|
if (!cosines) {
|
|
ymult /= pi;
|
|
}
|
|
for (i = 0; i != modeCount; i++) {
|
|
int t = termWidth * i + termWidth / 2;
|
|
double ph = (cosines) ? phasecoefcosdisp[i] : phasecoefdisp[i];
|
|
if (magcoef[i] > -epsilon2 / 4 && magcoefdisp[i] < epsilon2 / 4) {
|
|
ph = 0;
|
|
}
|
|
int y = midy - (int) (ph * ymult);
|
|
g.setColor(i == selectedCoef ? Color.yellow : Color.white);
|
|
g.drawLine(t, midy, t, y);
|
|
g.fillOval(t - dotSize / 2, y - dotSize / 2, dotSize, dotSize);
|
|
}
|
|
} else if (displayChooser.getSelectedIndex() == DISP_MODES) {
|
|
int sqw = (winSize.width - 25) / 3;
|
|
int sqh = (int) (sqw / pi);
|
|
int topY = panelHeight;
|
|
int leftX = 0;
|
|
int ox, oy = -1;
|
|
for (i = 0; i != modeCount; i++) {
|
|
if (!(
|
|
magcoefdisp[i] > .06 ||
|
|
magcoefdisp[i] < -.06
|
|
)) {
|
|
continue;
|
|
}
|
|
g.setColor(gray2);
|
|
int centerX = leftX + sqw / 2;
|
|
int centerY = topY + sqh / 2;
|
|
g.drawLine(leftX, centerY, leftX + sqw, centerY);
|
|
g.drawLine(centerX, topY, centerX, topY + sqh);
|
|
g.setColor(i == selectedCoef ? Color.yellow : Color.white);
|
|
g.drawRect(leftX, topY, sqw, sqh);
|
|
ox = -1;
|
|
ymult = sqh * .5 * magcoefdisp[i];
|
|
int j;
|
|
for (j = sampStart; j <= sampEnd; j++) {
|
|
int x = leftX + sqw * j / sampleCount;
|
|
double dy = modeTable[j][i] * phasecoefcosdisp[i];
|
|
int y = centerY - (int) (ymult * dy);
|
|
if (ox != -1) {
|
|
g.drawLine(ox, oy, x, y);
|
|
}
|
|
ox = x;
|
|
oy = y;
|
|
}
|
|
leftX += sqw + 10;
|
|
if (leftX + sqw > winSize.width) {
|
|
leftX = 0;
|
|
topY += sqh + 10;
|
|
if (topY + sqh > panelHeight * 2) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
realg.drawImage(dbimage, 0, 0, this);
|
|
if (!stoppedCheck.getState() && !allQuiet) {
|
|
cv.repaint(pause);
|
|
}
|
|
}
|
|
|
|
void drawPin(Graphics g, int pos, int midy, double ymult) {
|
|
int x = winSize.width * pos / sampleCount;
|
|
g.setColor(gray2);
|
|
g.drawLine(x, (int) (midy - ymult),
|
|
x, (int) (midy + ymult)
|
|
);
|
|
g.setColor(Color.white);
|
|
g.fillOval(x - 2, midy - (int) (func[pos] * ymult) - 2, 5, 5);
|
|
}
|
|
|
|
int getTermWidth() {
|
|
int termWidth = winSize.width / modeCount;
|
|
int maxTermWidth = winSize.width / 30;
|
|
if (termWidth > maxTermWidth) {
|
|
termWidth = maxTermWidth;
|
|
}
|
|
termWidth &= ~1;
|
|
return termWidth;
|
|
}
|
|
|
|
void getVelocities() {
|
|
int k, j;
|
|
for (j = 0; j != sampleCount; j++) {
|
|
double dy = 0;
|
|
for (k = 0; k != modeCount; k++) {
|
|
dy += magcoef[k] * modeTable[j][k] *
|
|
java.lang.Math.sin(phasecoef[k]) * omega[k];
|
|
}
|
|
funci[j] = -dy;
|
|
}
|
|
}
|
|
|
|
void drawBarPiece(Graphics g, int x, int y, int i, int sampStart) {
|
|
int thick = setup.getThickness();
|
|
xpoints[0] = xpoints[3];
|
|
ypoints[0] = ypoints[3];
|
|
xpoints[1] = xpoints[2];
|
|
ypoints[1] = ypoints[2];
|
|
xpoints[2] = x;
|
|
ypoints[2] = y - thick;
|
|
xpoints[3] = x;
|
|
ypoints[3] = y + thick;
|
|
if (i != sampStart) {
|
|
if (thick == 0) {
|
|
g.drawLine(xpoints[0], ypoints[0], xpoints[2], ypoints[2]);
|
|
} else {
|
|
g.fillPolygon(xpoints, ypoints, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
void edit(MouseEvent e) {
|
|
if (selection == SEL_NONE) {
|
|
return;
|
|
}
|
|
int x = e.getX();
|
|
int y = e.getY();
|
|
switch (selection) {
|
|
case SEL_MAG:
|
|
editMag(x, y);
|
|
break;
|
|
case SEL_FUNC:
|
|
editFunc(x, y);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void editMag(int x, int y) {
|
|
if (selectedCoef == -1) {
|
|
return;
|
|
}
|
|
int panelHeight = getPanelHeight();
|
|
double ymult = .6 * panelHeight / 2;
|
|
double midy = magnitudesY + (panelHeight / 2) + (int) ymult / 2;
|
|
double coef = -(y - midy) / ymult;
|
|
coef = unlogcoef(coef);
|
|
if (coef < -1) {
|
|
coef = -1;
|
|
}
|
|
if (coef > 1) {
|
|
coef = 1;
|
|
}
|
|
if (magcoef[selectedCoef] == coef) {
|
|
return;
|
|
}
|
|
magcoef[selectedCoef] = coef;
|
|
cv.repaint(pause);
|
|
}
|
|
|
|
void editFunc(int x, int y) {
|
|
if (modeChooser.getSelectedIndex() == MODE_FORCE) {
|
|
editFuncForce(x, y);
|
|
return;
|
|
}
|
|
if (dragX == x) {
|
|
editFuncPoint(x, y);
|
|
dragY = y;
|
|
} else {
|
|
// need to draw a line from old x,y to new x,y and
|
|
// call editFuncPoint for each point on that line. yuck.
|
|
int x1 = (x < dragX) ? x : dragX;
|
|
int y1 = (x < dragX) ? y : dragY;
|
|
int x2 = (x > dragX) ? x : dragX;
|
|
int y2 = (x > dragX) ? y : dragY;
|
|
dragX = x;
|
|
dragY = y;
|
|
for (x = x1; x <= x2; x++) {
|
|
y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
|
|
editFuncPoint(x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
double logep2 = 0;
|
|
|
|
double logcoef(double x) {
|
|
if (x >= .25 || x <= -.25) {
|
|
return x;
|
|
}
|
|
x *= 4;
|
|
double ep2 = epsilon2;
|
|
int sign = (x < 0) ? -1 : 1;
|
|
x *= sign;
|
|
if (x < ep2) {
|
|
return 0;
|
|
}
|
|
if (logep2 == 0) {
|
|
logep2 = -java.lang.Math.log(2 * ep2);
|
|
}
|
|
return .25 * sign * (java.lang.Math.log(x + ep2) + logep2) / logep2;
|
|
}
|
|
|
|
double unlogcoef(double x) {
|
|
if (x >= .25 || x <= -.25) {
|
|
return x;
|
|
}
|
|
double ep2 = epsilon2;
|
|
int sign = (x < 0) ? -1 : 1;
|
|
return .25 * sign * (java.lang.Math.exp(4 * x * sign * logep2 - logep2) - ep2);
|
|
}
|
|
|
|
void editFuncPoint(int x, int y) {
|
|
int panelHeight = getPanelHeight();
|
|
int midy = panelHeight / 2;
|
|
int halfPanel = panelHeight / 2;
|
|
int periodWidth = winSize.width;
|
|
double ymult = .75 * halfPanel;
|
|
int lox = x * sampleCount / periodWidth;
|
|
int hix = ((x + 1) * sampleCount - 1) / periodWidth;
|
|
double val = (midy - y) / ymult;
|
|
if (val > 1) {
|
|
val = 1;
|
|
}
|
|
if (val < -1) {
|
|
val = -1;
|
|
}
|
|
if (lox < 1) {
|
|
lox = 1;
|
|
}
|
|
if (hix >= sampleCount) {
|
|
hix = sampleCount - 1;
|
|
}
|
|
for (; lox <= hix; lox++) {
|
|
if (modeChooser.getSelectedIndex() == MODE_THICKNESS) {
|
|
thickness[lox] = (midy < y) ? (y - midy) * 2 : (midy - y) * 2;
|
|
if (thickness[lox] == 0) {
|
|
thickness[lox] = 1;
|
|
}
|
|
} else {
|
|
func[lox] = val;
|
|
funci[lox] = 0;
|
|
}
|
|
}
|
|
func[sampleCount] = func[0];
|
|
cv.repaint(pause);
|
|
if (soundCheck.getState() == false) {
|
|
transform(false);
|
|
}
|
|
}
|
|
|
|
void editFuncForce(int x, int y) {
|
|
int panelHeight = getPanelHeight();
|
|
int midy = panelHeight / 2;
|
|
int halfPanel = panelHeight / 2;
|
|
int periodWidth = winSize.width;
|
|
double ymult = .75 * halfPanel;
|
|
int ax = x * sampleCount / periodWidth;
|
|
double val = (midy - y) / ymult;
|
|
if (val > 1) {
|
|
val = 1;
|
|
}
|
|
if (val < -1) {
|
|
val = -1;
|
|
}
|
|
if (ax < 1 || ax >= sampleCount) {
|
|
return;
|
|
}
|
|
|
|
// solve equation A x = b, using diagonalized A,
|
|
// A = M Lambda M^-1. x = M Lambda^-1 M^-1 b. b is
|
|
// all zeros except at ax where it is 1.
|
|
double q[] = new double[modeCount];
|
|
int i, j;
|
|
|
|
// multiply b by Lambda^-1 M^-1. M^-1 has eigenvectors as rows
|
|
// (with the norm of each eigenvector divided out). Lambda^-1
|
|
// is a diagonal matrix with 1/lambdas as elements (lambda=omega^2)
|
|
for (i = 0; i != modeCount; i++) {
|
|
q[i] = modeTable[ax][i] / (omega[i] * omega[i] * modeNorms[i]);
|
|
}
|
|
|
|
// multiply q by M to get result. M has eigenvectors as columns.
|
|
for (i = 0; i != sampleCount; i++) {
|
|
double dy = 0;
|
|
for (j = 0; j != modeCount; j++) {
|
|
dy += q[j] * modeTable[i][j];
|
|
}
|
|
func[i] = dy;
|
|
}
|
|
|
|
// ok now we just scale the whole thing so we get the answer
|
|
// we want at ax.
|
|
double mult = val / func[ax];
|
|
for (i = 0; i <= sampleCount; i++) {
|
|
func[i] *= mult;
|
|
funci[i] = 0;
|
|
}
|
|
|
|
cv.repaint(pause);
|
|
if (soundCheck.getState() == false) {
|
|
transform(true);
|
|
}
|
|
}
|
|
|
|
public void componentHidden(ComponentEvent e) {
|
|
}
|
|
|
|
public void componentMoved(ComponentEvent e) {
|
|
}
|
|
|
|
public void componentShown(ComponentEvent e) {
|
|
cv.repaint(pause);
|
|
}
|
|
|
|
public void componentResized(ComponentEvent e) {
|
|
handleResize();
|
|
cv.repaint(pause);
|
|
}
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
if (e.getSource() == sineButton) {
|
|
doFundamental();
|
|
cv.repaint();
|
|
}
|
|
if (e.getSource() == blankButton) {
|
|
doBlank();
|
|
cv.repaint();
|
|
}
|
|
}
|
|
|
|
public void adjustmentValueChanged(AdjustmentEvent e) {
|
|
System.out.print(((Scrollbar) e.getSource()).getValue() + "\n");
|
|
if (e.getSource() == dampingBar || e.getSource() == speedBar) {
|
|
setDamping();
|
|
}
|
|
if (e.getSource() == loadBar) {
|
|
setLoadCount();
|
|
}
|
|
if (e.getSource() == stiffnessBar) {
|
|
genModes();
|
|
}
|
|
cv.repaint(pause);
|
|
}
|
|
|
|
public boolean handleEvent(Event ev) {
|
|
if (ev.id == Event.WINDOW_DESTROY) {
|
|
//if (applet == null) dispose();
|
|
//else applet.destroyFrame();
|
|
return true;
|
|
}
|
|
return super.handleEvent(ev);
|
|
}
|
|
|
|
void setLoadCount() {
|
|
setup = (Setup)
|
|
setupList.elementAt(setupChooser.getSelectedIndex());
|
|
sampleCount = maxTerms = loadBar.getValue();
|
|
step = pi / sampleCount;
|
|
int x, y;
|
|
thickness = new int[sampleCount + 1];
|
|
int i;
|
|
for (i = 0; i <= sampleCount; i++) {
|
|
thickness[i] = 5;
|
|
}
|
|
genModes();
|
|
setDamping();
|
|
}
|
|
|
|
void setDamping() {
|
|
int i;
|
|
dampcoef = new double[modeCount];
|
|
double tadd = java.lang.Math.exp((speedBar.getValue() - 100) / 20.) * (.1 / 50);
|
|
for (i = 0; i != modeCount; i++) {
|
|
double damper = java.lang.Math.exp(dampingBar.getValue() / 40 - 3) * 30;
|
|
if (dampingBar.getValue() <= 2) {
|
|
damper = 0;
|
|
}
|
|
double damp2 = omega[i] * java.lang.Math.sqrt(
|
|
java.lang.Math.sqrt(1 + damper * damper / (omega[i] * omega[i])) - 1);
|
|
dampcoef[i] = java.lang.Math.exp(-damp2 * tadd * .004);
|
|
}
|
|
}
|
|
|
|
public void mouseDragged(MouseEvent e) {
|
|
dragging = true;
|
|
edit(e);
|
|
}
|
|
|
|
public void mouseMoved(MouseEvent e) {
|
|
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
|
|
return;
|
|
}
|
|
int x = e.getX();
|
|
int y = e.getY();
|
|
dragX = x;
|
|
dragY = y;
|
|
int panelHeight = getPanelHeight();
|
|
int oldCoef = selectedCoef;
|
|
selectedCoef = -1;
|
|
selection = 0;
|
|
if (y < panelHeight) {
|
|
selection = SEL_FUNC;
|
|
}
|
|
if (y >= magnitudesY && y < magnitudesY + panelHeight) {
|
|
int termWidth = getTermWidth();
|
|
selectedCoef = x / termWidth;
|
|
if (selectedCoef >= modeCount) {
|
|
selectedCoef = -1;
|
|
}
|
|
if (selectedCoef != -1) {
|
|
selection = SEL_MAG;
|
|
}
|
|
}
|
|
if (selectedCoef != oldCoef) {
|
|
cv.repaint(pause);
|
|
}
|
|
}
|
|
|
|
public void mouseClicked(MouseEvent e) {
|
|
if (e.getClickCount() == 2 && selectedCoef != -1) {
|
|
int i;
|
|
for (i = 0; i != modeCount; i++) {
|
|
if (selectedCoef != i) {
|
|
magcoef[i] = 0;
|
|
}
|
|
}
|
|
magcoef[selectedCoef] = 1;
|
|
cv.repaint(pause);
|
|
}
|
|
}
|
|
|
|
public void mouseEntered(MouseEvent e) {
|
|
}
|
|
|
|
public void mouseExited(MouseEvent e) {
|
|
if (!dragging && selectedCoef != -1) {
|
|
selectedCoef = -1;
|
|
cv.repaint(pause);
|
|
}
|
|
}
|
|
|
|
public void mousePressed(MouseEvent e) {
|
|
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == 0) {
|
|
return;
|
|
}
|
|
if (selection == SEL_FUNC) {
|
|
getVelocities();
|
|
}
|
|
dragging = true;
|
|
edit(e);
|
|
}
|
|
|
|
public void mouseReleased(MouseEvent e) {
|
|
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == 0) {
|
|
return;
|
|
}
|
|
if (dragging && selection == SEL_FUNC) {
|
|
if (modeChooser.getSelectedIndex() == MODE_THICKNESS) {
|
|
genModes();
|
|
} else {
|
|
transform(false);
|
|
if (soundCheck.getState()) {
|
|
doPlay();
|
|
}
|
|
}
|
|
}
|
|
if (dragging && selection == SEL_MAG && soundCheck.getState()) {
|
|
doPlay();
|
|
}
|
|
dragging = false;
|
|
cv.repaint(pause);
|
|
}
|
|
|
|
public void itemStateChanged(ItemEvent e) {
|
|
if (e.getItemSelectable() == stoppedCheck) {
|
|
cv.repaint(pause);
|
|
return;
|
|
}
|
|
if (e.getItemSelectable() == soundCheck) {
|
|
if (soundCheck.getState()) {
|
|
speedBar.setValue(250);
|
|
dampingBar.setValue(170);
|
|
baseFreqBar.enable();
|
|
setDamping();
|
|
doPlay();
|
|
} else {
|
|
baseFreqBar.disable();
|
|
}
|
|
}
|
|
if (e.getItemSelectable() == displayChooser) {
|
|
cv.repaint(pause);
|
|
}
|
|
if (e.getItemSelectable() == setupChooser) {
|
|
setLoadCount();
|
|
if (setup instanceof StiffStringSetup) {
|
|
stiffnessBar.enable();
|
|
} else {
|
|
stiffnessBar.disable();
|
|
}
|
|
}
|
|
}
|
|
|
|
void dodiff(double matrix[][], int r, int i, int n, double mult) {
|
|
if (i < 1 && setup.leftBoundary() == BOUND_HINGED) {
|
|
return;
|
|
}
|
|
if (i > sampleCount - 1 && setup.rightBoundary() == BOUND_HINGED) {
|
|
return;
|
|
}
|
|
|
|
if (n == 2 && !(setup instanceof StringSetup)) {
|
|
if (i <= 1 && setup.leftBoundary() == BOUND_FREE) {
|
|
return;
|
|
}
|
|
if (i >= sampleCount - 1 && setup.rightBoundary() == BOUND_FREE) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (n > 0) {
|
|
dodiff(matrix, r, i - 1, n - 2, -mult);
|
|
dodiff(matrix, r, i + 1, n - 2, -mult);
|
|
dodiff(matrix, r, i, n - 2, mult * 2);
|
|
return;
|
|
}
|
|
if (i >= 1 && i <= sampleCount - 1) {
|
|
matrix[r][i] += mult;
|
|
}
|
|
}
|
|
|
|
void genModes() {
|
|
int n = sampleCount - 1;
|
|
double matrix[][] = new double[n + 1][n + 1];
|
|
double d[] = new double[n + 1];
|
|
double e[] = new double[n + 1];
|
|
int i, j;
|
|
for (i = 1; i <= n; i++) {
|
|
setup.doMatrixStep(matrix, i, n);
|
|
}
|
|
|
|
if (setup instanceof StringSetup) {
|
|
if (setup.leftBoundary() == BOUND_FREE) {
|
|
matrix[1][1]--;
|
|
}
|
|
if (setup.rightBoundary() == BOUND_FREE) {
|
|
matrix[n][n]--;
|
|
}
|
|
}
|
|
|
|
for (j = 1; j <= n; j++) {
|
|
for (i = 1; i <= n; i++) {
|
|
System.out.print(matrix[i][j] + " ");
|
|
}
|
|
System.out.print("\n");
|
|
}
|
|
|
|
tred2(matrix, n, d, e);
|
|
tqli(d, e, n, matrix);
|
|
|
|
modeCount = sampleCount - 1;
|
|
omega = new double[modeCount];
|
|
|
|
// now get the eigenvalues and sort them
|
|
int omegamap[] = new int[sampleCount];
|
|
for (i = j = 0; i != n; i++) {
|
|
if (d[i + 1] < 1e-8) {
|
|
modeCount--;
|
|
continue;
|
|
}
|
|
omega[j] = java.lang.Math.sqrt(d[i + 1]);
|
|
omegamap[j] = i;
|
|
j++;
|
|
}
|
|
|
|
int si, sj;
|
|
// sort the omegas
|
|
for (si = 1; si < modeCount; si++) {
|
|
double v = omega[si];
|
|
int vm = omegamap[si];
|
|
sj = si;
|
|
while (omega[sj - 1] > v) {
|
|
omega[sj] = omega[sj - 1];
|
|
omegamap[sj] = omegamap[sj - 1];
|
|
sj--;
|
|
if (sj <= 0) {
|
|
break;
|
|
}
|
|
}
|
|
omega[sj] = v;
|
|
omegamap[sj] = vm;
|
|
}
|
|
|
|
modeTable = new double[sampleCount + 1][modeCount];
|
|
modeNorms = new double[modeCount];
|
|
for (i = 0; i != modeCount; i++) {
|
|
int om = omegamap[i] + 1;
|
|
double maxf = 0;
|
|
for (j = 0; j != sampleCount; j++) {
|
|
modeTable[j][i] = matrix[j][om];
|
|
if (modeTable[j][i] > maxf) {
|
|
maxf = modeTable[j][i];
|
|
}
|
|
if (-modeTable[j][i] > maxf) {
|
|
maxf = -modeTable[j][i];
|
|
}
|
|
}
|
|
modeNorms[i] = 1 / (maxf * maxf);
|
|
for (j = 0; j != sampleCount; j++) {
|
|
modeTable[j][i] /= maxf;
|
|
}
|
|
}
|
|
|
|
double mult = 1 / omega[0];
|
|
for (i = 0; i != modeCount; i++) {
|
|
omega[i] *= mult;
|
|
}
|
|
}
|
|
|
|
|
|
void tred2(double a[][], int n, double d[], double e[]) {
|
|
int l, k, j, i;
|
|
double scale, hh, h, g, f;
|
|
|
|
// this loop gets faster as i decreases
|
|
for (i = n; i >= 2; i--) {
|
|
l = i - 1;
|
|
h = scale = 0.0;
|
|
if (l > 1) {
|
|
for (k = 1; k <= l; k++) {
|
|
scale += java.lang.Math.abs(a[i][k]);
|
|
}
|
|
if (scale == 0.0) {
|
|
e[i] = a[i][l];
|
|
} else {
|
|
for (k = 1; k <= l; k++) {
|
|
a[i][k] /= scale;
|
|
h += a[i][k] * a[i][k];
|
|
}
|
|
f = a[i][l];
|
|
g = (f >= 0.0 ? -java.lang.Math.sqrt(h) : java.lang.Math.sqrt(h));
|
|
e[i] = scale * g;
|
|
h -= f * g;
|
|
a[i][l] = f - g;
|
|
f = 0.0;
|
|
for (j = 1; j <= l; j++) {
|
|
a[j][i] = a[i][j] / h;
|
|
g = 0.0;
|
|
for (k = 1; k <= j; k++) {
|
|
g += a[j][k] * a[i][k];
|
|
}
|
|
for (k = j + 1; k <= l; k++) {
|
|
g += a[k][j] * a[i][k];
|
|
}
|
|
e[j] = g / h;
|
|
f += e[j] * a[i][j];
|
|
}
|
|
hh = f / (h + h);
|
|
for (j = 1; j <= l; j++) {
|
|
f = a[i][j];
|
|
e[j] = g = e[j] - hh * f;
|
|
for (k = 1; k <= j; k++) {
|
|
a[j][k] -= (f * e[k] + g * a[i][k]);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
e[i] = a[i][l];
|
|
}
|
|
d[i] = h;
|
|
}
|
|
d[1] = 0.0;
|
|
e[1] = 0.0;
|
|
/* Contents of this loop can be omitted if eigenvectors not
|
|
wanted except for statement d[i]=a[i][i]; */
|
|
// speed decreases as i increases
|
|
for (i = 1; i <= n; i++) {
|
|
l = i - 1;
|
|
if (d[i] != 0) {
|
|
for (j = 1; j <= l; j++) {
|
|
g = 0.0;
|
|
for (k = 1; k <= l; k++) {
|
|
g += a[i][k] * a[k][j];
|
|
}
|
|
for (k = 1; k <= l; k++) {
|
|
a[k][j] -= g * a[k][i];
|
|
}
|
|
}
|
|
}
|
|
d[i] = a[i][i];
|
|
a[i][i] = 1.0;
|
|
for (j = 1; j <= l; j++) {
|
|
a[j][i] = a[i][j] = 0.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// this is from Numerical Recipes in C. It finds the eigenvalues
|
|
// and eigenvectors of an nxn tridiagonal symmetric matrix specified
|
|
// by d[] and e[].
|
|
void tqli(double d[], double e[], int n, double z[][]) {
|
|
int m, l, iter, i, k;
|
|
double s, r, p, g, f, dd, c, b;
|
|
|
|
for (i = 2; i <= n; i++) {
|
|
e[i - 1] = e[i];
|
|
}
|
|
e[n] = 0.0;
|
|
// faster as l increases
|
|
for (l = 1; l <= n; l++) {
|
|
iter = 0;
|
|
do {
|
|
for (m = l; m <= n - 1; m++) {
|
|
dd = java.lang.Math.abs(d[m]) + java.lang.Math.abs(d[m + 1]);
|
|
if ((double) (java.lang.Math.abs(e[m]) + dd) == dd) {
|
|
break;
|
|
}
|
|
}
|
|
if (m != l) {
|
|
if (iter++ == 30) {
|
|
System.out.print("Too many iterations in tqli\n");
|
|
}
|
|
g = (d[l + 1] - d[l]) / (2.0 * e[l]);
|
|
r = pythag(g, 1.0);
|
|
g = d[m] - d[l] + e[l] / (g + SIGN(r, g));
|
|
s = c = 1.0;
|
|
p = 0.0;
|
|
for (i = m - 1; i >= l; i--) {
|
|
f = s * e[i];
|
|
b = c * e[i];
|
|
e[i + 1] = (r = pythag(f, g));
|
|
if (r == 0.0) {
|
|
d[i + 1] -= p;
|
|
e[m] = 0.0;
|
|
break;
|
|
}
|
|
s = f / r;
|
|
c = g / r;
|
|
g = d[i + 1] - p;
|
|
r = (d[i] - g) * s + 2.0 * c * b;
|
|
d[i + 1] = g + (p = s * r);
|
|
g = c * r - b;
|
|
for (k = 1; k <= n; k++) {
|
|
f = z[k][i + 1];
|
|
z[k][i + 1] = s * z[k][i] + c * f;
|
|
z[k][i] = c * z[k][i] - s * f;
|
|
}
|
|
}
|
|
if (r == 0.0 && i >= l) {
|
|
continue;
|
|
}
|
|
d[l] -= p;
|
|
e[l] = g;
|
|
e[m] = 0.0;
|
|
}
|
|
} while (m != l);
|
|
}
|
|
}
|
|
|
|
double SIGN(double a, double b) {
|
|
return b >= 0 ? java.lang.Math.abs(a) : -java.lang.Math.abs(a);
|
|
}
|
|
|
|
double SQR(double a) {
|
|
return a * a;
|
|
}
|
|
|
|
double pythag(double a, double b) {
|
|
double absa, absb;
|
|
absa = java.lang.Math.abs(a);
|
|
absb = java.lang.Math.abs(b);
|
|
if (absa > absb) {
|
|
return absa * java.lang.Math.sqrt(1.0 + SQR(absb / absa));
|
|
} else {
|
|
return (absb == 0.0 ? 0.0 : absb * java.lang.Math.sqrt(1.0 + SQR(absa / absb)));
|
|
}
|
|
}
|
|
|
|
abstract class Setup {
|
|
abstract String getName();
|
|
|
|
abstract Setup createNext();
|
|
|
|
abstract int leftBoundary();
|
|
|
|
abstract int rightBoundary();
|
|
|
|
int getThickness() {
|
|
return 3;
|
|
}
|
|
|
|
void doMatrixStep(double matrix[][], int i, int n) {
|
|
dodiff(matrix, i, i, 4, 1);
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class FreeBarSetup extends Setup {
|
|
String getName() {
|
|
return "bar, free";
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new HingedBarSetup();
|
|
}
|
|
|
|
int leftBoundary() {
|
|
return BOUND_FREE;
|
|
}
|
|
|
|
int rightBoundary() {
|
|
return BOUND_FREE;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class HingedBarSetup extends Setup {
|
|
String getName() {
|
|
return "bar, hinged";
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new ClampedBarSetup();
|
|
}
|
|
|
|
int leftBoundary() {
|
|
return BOUND_HINGED;
|
|
}
|
|
|
|
int rightBoundary() {
|
|
return BOUND_HINGED;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class ClampedBarSetup extends Setup {
|
|
String getName() {
|
|
return "bar, clamped";
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new ClampedFreeBarSetup();
|
|
}
|
|
|
|
int leftBoundary() {
|
|
return BOUND_CLAMPED;
|
|
}
|
|
|
|
int rightBoundary() {
|
|
return BOUND_CLAMPED;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class ClampedFreeBarSetup extends Setup {
|
|
String getName() {
|
|
return "bar, clamped/free";
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new HingedClampedBarSetup();
|
|
}
|
|
|
|
int leftBoundary() {
|
|
return BOUND_CLAMPED;
|
|
}
|
|
|
|
int rightBoundary() {
|
|
return BOUND_FREE;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class HingedClampedBarSetup extends Setup {
|
|
String getName() {
|
|
return "bar, hinged/clamped";
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new StringSetup();
|
|
}
|
|
|
|
int leftBoundary() {
|
|
return BOUND_HINGED;
|
|
}
|
|
|
|
int rightBoundary() {
|
|
return BOUND_CLAMPED;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class StringSetup extends Setup {
|
|
String getName() {
|
|
return "string, pinned";
|
|
}
|
|
|
|
void doMatrixStep(double matrix[][], int i, int n) {
|
|
dodiff(matrix, i, i, 2, 1);
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new String1FreeSetup();
|
|
}
|
|
|
|
int leftBoundary() {
|
|
return BOUND_HINGED;
|
|
}
|
|
|
|
int rightBoundary() {
|
|
return BOUND_HINGED;
|
|
}
|
|
|
|
int getThickness() {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class String1FreeSetup extends StringSetup {
|
|
String getName() {
|
|
return "string, pinned/free";
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new String2FreeSetup();
|
|
}
|
|
|
|
int rightBoundary() {
|
|
return BOUND_FREE;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class String2FreeSetup extends String1FreeSetup {
|
|
String getName() {
|
|
return "string, free/free";
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new StiffStringSetup();
|
|
}
|
|
|
|
int leftBoundary() {
|
|
return BOUND_FREE;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class StiffStringSetup extends StringSetup {
|
|
String getName() {
|
|
return "stiff string, pinned";
|
|
}
|
|
|
|
void doMatrixStep(double matrix[][], int i, int n) {
|
|
dodiff(matrix, i, i, 2, 1);
|
|
double stiff = stiffnessBar.getValue() * .1;
|
|
dodiff(matrix, i, i, 4, stiff);
|
|
}
|
|
|
|
Setup createNext() {
|
|
return new StiffStringClampedSetup();
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
class StiffStringClampedSetup extends StiffStringSetup {
|
|
String getName() {
|
|
return "stiff string, clamped";
|
|
}
|
|
|
|
Setup createNext() {
|
|
return null;
|
|
}
|
|
|
|
int leftBoundary() {
|
|
return BOUND_CLAMPED;
|
|
}
|
|
|
|
int rightBoundary() {
|
|
return BOUND_CLAMPED;
|
|
}
|
|
}
|
|
|
|
;
|
|
|
|
double sndmin, sndmax;
|
|
|
|
int getFreq(int n) {
|
|
double stepsize = java.lang.Math.log(2) / 12;
|
|
double freq = java.lang.Math.exp(baseFreqBar.getValue() * stepsize);
|
|
return (int) (freq * omega[n]);
|
|
}
|
|
|
|
void doPlay() {
|
|
final int rate = 22000;
|
|
final int sampcount = rate;
|
|
|
|
byte b[] = new byte[sampcount];
|
|
|
|
double stepsize = java.lang.Math.log(2) / 12;
|
|
double freq = java.lang.Math.exp(baseFreqBar.getValue() * stepsize);
|
|
double n = 2 * pi * freq / rate;
|
|
n /= omega[0];
|
|
// filter out frequencies above Nyquist freq
|
|
double maxomega = pi / n;
|
|
int m = modeCount;
|
|
while (m > 0 && omega[m - 1] > maxomega) {
|
|
m--;
|
|
}
|
|
if (m == 0) {
|
|
return;
|
|
}
|
|
// filter out frequencies less than 20 Hz (we do that so that
|
|
// they do not throw off the bounds checking of the waveform)
|
|
int m0 = 0;
|
|
// freq = rate*omega*n/(2*pi)
|
|
double minomega = 20 * 2 * pi / (rate * n);
|
|
while (m0 < m && omega[m0] < minomega) {
|
|
m0++;
|
|
}
|
|
if (m0 == m) {
|
|
return;
|
|
}
|
|
boolean failed;
|
|
int i;
|
|
int sampWindow = rate / 40;
|
|
int offset = 0;
|
|
double lastscale = 1000;
|
|
double mag[] = new double[modeCount];
|
|
for (i = 0; i != modeCount; i++) {
|
|
mag[i] = magcoef[i];
|
|
}
|
|
do {
|
|
failed = false;
|
|
double mn = (-sndmin > sndmax) ? -sndmin : sndmax;
|
|
if (mn < .02) {
|
|
mn = .02;
|
|
}
|
|
double scale = 126 / mn;
|
|
if (scale > lastscale) {
|
|
scale = lastscale;
|
|
}
|
|
sndmin = sndmax = 0;
|
|
for (i = 0; i != sampWindow; i++) {
|
|
double dy = 0;
|
|
int j;
|
|
int ii = i + offset;
|
|
for (j = m0; j != m; j++) {
|
|
dy += mag[j] * java.lang.Math.sin(ii * n * omega[j]) * scale;
|
|
}
|
|
if (dy < sndmin) {
|
|
sndmin = dy;
|
|
}
|
|
if (dy > sndmax) {
|
|
sndmax = dy;
|
|
}
|
|
b[ii] = (byte) dy;
|
|
if (dy < -127 || dy > 127) {
|
|
failed = true;
|
|
}
|
|
}
|
|
sndmin /= scale;
|
|
sndmax /= scale;
|
|
if (failed) {
|
|
continue;
|
|
}
|
|
offset += sampWindow;
|
|
for (i = 0; i != modeCount; i++) {
|
|
mag[i] *= dampcoef[i];
|
|
}
|
|
if (offset >= sampcount) {
|
|
break;
|
|
}
|
|
} while (true);
|
|
|
|
AudioFormat format = new AudioFormat(rate, 8, 1, true, true);
|
|
DataLine.Info info = new DataLine.Info(
|
|
SourceDataLine.class,
|
|
format
|
|
);
|
|
SourceDataLine line = null;
|
|
try {
|
|
line = (SourceDataLine) AudioSystem.getLine(info);
|
|
line.open(format, sampcount);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
line.start();
|
|
line.write(b, 0, sampcount);
|
|
cv.repaint();
|
|
}
|
|
};
|