diff --git a/Envelope.java b/Envelope.java new file mode 100644 index 0000000..19aad01 --- /dev/null +++ b/Envelope.java @@ -0,0 +1,81 @@ +/** + * Implementation der ADSR-Huellkurve + * Fuer die Facharbeit vom 16.03.2017 + * Objekte dieser Klasse fuehren die Berechnung + * der Funktionswerte der Huellkurve aus. + * 2017, Soeren Richter + * soeren@dreieck-project.de + * Version 0.1.5 + * Code vollstaendig selbst geschrieben, fuer Hintergruende und + * Referenzen siehe Abschnitt 3.6 der Facharbeit. + * @author Soeren Richter + * @version 0.1.5 + */ + +public class Envelope +{ + // konstante ADSR-Attribute der Huellkurve: + private final double attack; + private final double decay; + private final double sustain; + private final double release; + + // veraenderbare Hold-Zeit/Tonlaenge: + private double length; + + /** + * Konstruktor der Klasse Envelope. + * @param pAttack Attack-Zeit der Huellkurve + * @param pDecay Decay-Zeit der Huellkurve + * @param pSustain Sustain-Amplitude der Huellkurve + * @param pRelease Release-Zeit der Huellkurve + */ + public Envelope(double pAttack, double pDecay, double pSustain, double pRelease) + { + attack = pAttack; + decay = pDecay; + sustain = pSustain; + release = pRelease; + length = 1; + } + + /** + * Rueckgabe des Huellkurven-Funktionswertes zum Zeitpunkt time + * @param time Zeitpunkt in s + * @return Huellkurven-Funktionswert + */ + public double getAmplitude(double time) + { + double amp = 0; + if (time <= attack) { + amp = time/attack; + } else if (time <= attack+decay && time > attack) { + amp = (-1 + sustain) / decay * time + (1 - sustain) + / decay * (attack + decay) + sustain; + } else if (time <= length && time > attack+decay) { + amp = sustain; + } else if (time > length) { + amp = (-sustain) / release * time + sustain + / release * length + sustain; + } + return amp; + } + + /** + * Festlegen der Hold-/Tonlaenge + * @param pLength Hold-/Tonlaenge in s + */ + public void setLength(double pLength) + { + length = pLength; + } + + /** + * Rueckgabe der vollstaendigen Huellkurvenlaenge inklusive Release-Zeit + * @return vollstaendige Laenge der Huellkurve + */ + public double getFullLength() + { + return length+release; + } +} diff --git a/Oscillator.java b/Oscillator.java new file mode 100644 index 0000000..b6c4d0d --- /dev/null +++ b/Oscillator.java @@ -0,0 +1,128 @@ +/** + * Implementation der Fourier-Reihen + * Fuer die Facharbeit vom 16.03.2017 + * Objekte dieser Klasse fuehren die Berechnung der Funktionswerte + * der Fourier-Reihe ihrer entsprechnden Wellenform und Parameter aus. + * 2017, Soeren Richter + * soeren@dreieck-project.de + * Version 0.1.5 + * Code vollstaendig selbst geschrieben. Fuer Referenzen und mathematische + * Grundlage siehe Kapitel 3 der Facharbeit. + * @author Soeren Richter + * @version 0.1.5 + */ + +public class Oscillator +{ + // Wellenform; "sine", "saw", "square" oder "triangle": + private final String waveform; + + // Grenzfrequenz der berechnung der Fourier-Reihen-Summen + private final int cutoff; + + /** + * Konstruktor der Klasse Oscillator. + * @param pWaveform Wellenform ("sine", "saw", "square" oder "triangle") + * @param pCutoff Grenzfrequenz der berechnung der Fourier-Reihen-Summen + */ + public Oscillator(String pWaveform, int pCutoff) + { + waveform = pWaveform; + cutoff = pCutoff; + } + + /** + * Berechnung des Funktionswertes einer Sinus-Funktion (Sine) der Frequenz frequency zum Zeitpunkt timeSample + * @param frequency Frequenz in Hz + * @param timeSample Zeitpunkt in s + * @return Funktionswert + */ + private double synthesizeSine(double frequency, double timeSample) + { + return Math.sin(timeSample * frequency * Math.PI * 2); + } + + // Die folgenden Funktionen nutzen die Fourier-Reihen der entsprechenden + // Wellenformen zur Berechnung der Funktionswerte. Der Algorithmus ist + // an das Pseudocode-Konzept (Beispiel: Rechteck/Square) angelehnt: + /** + * Funktion sq(t): + * funktionswert = 0; + * von order = 1 bis 22050/freq/2, Schritt 1: + * funktionswert = funktionswert + + * (sin(2 * PI * freq * (order * 2 - 1) * t) + * / (order * 2 - 1)); + * Rückgabe von 4 / PI * funktionswert; + */ + + /** + * Berechnung des Funktionswertes einer Saegezahn-Funktion (Sawtooth) der Frequenz frequency zum Zeitpunkt timeSample + * @param frequency Frequenz in Hz + * @param timeSample Zeitpunkt in s + * @return Funktionswert + */ + private double synthesizeSaw(double frequency, double timeSample) + { + double tempSample = 0; + for (int order = 1; order <= cutoff/frequency; order++) { + tempSample += (Math.sin(timeSample * frequency * order + * Math.PI * 2) / order); + } + return (2 / Math.PI * tempSample); + } + + /** + * Berechnung des Funktionswertes einer Rechteck-Funktion (Square) der Frequenz frequency zum Zeitpunkt timeSample + * @param frequency Frequenz in Hz + * @param timeSample Zeitpunkt in s + * @return Funktionswert + */ + private double synthesizeSquare(double frequency, double timeSample) + { + double tempSample = 0; + for (int order = 1; order <= cutoff/frequency/2; order++) { + tempSample += (Math.sin(timeSample * frequency * (order * 2 - 1) + * Math.PI * 2) / (order * 2 - 1)); + } + return (4 / Math.PI * tempSample); + } + + /** + * Berechnung des Funktionswertes einer Dreieck-Funktion (Triangle) der Frequenz frequency zum Zeitpunkt timeSample + * @param frequency Frequenz in Hz + * @param timeSample Zeitpunkt in s + * @return Funktionswert + */ + private double synthesizeTriangle(double frequency, double timeSample) + { + double tempSample = 0; + for (int order = 1; order <= cutoff/frequency/2; order++) { + tempSample += (Math.cos(timeSample * frequency * + (order * 2 - 1) * Math.PI * 2)) + / ((order * 2 - 1)*(order * 2 - 1)); + } + return ((8 / (Math.PI*Math.PI)) * tempSample); + } + + /** + * Methode zur Ausgabe des Funktionswertes der festgelegten Wellenform der Frequenz frequency zum Zeitpunkt timeSample + * @param frequency Frequenz in Hz + * @param timeSample Zeitpunkt in s + * @return Funktionswert + */ + public float getSample(double frequency, double timeSample) + { + if (null != waveform) switch (waveform) { + case "sine": + return (float)synthesizeSine(frequency, timeSample); + case "saw": + return (float)synthesizeSaw(frequency, timeSample); + case "square": + return (float)synthesizeSquare(frequency, timeSample); + case "triangle": + return (float)synthesizeTriangle(frequency, timeSample); + default: + return 0; + } else return 0; + } +} diff --git a/Synth.java b/Synth.java new file mode 100644 index 0000000..d0f460c --- /dev/null +++ b/Synth.java @@ -0,0 +1,187 @@ +/** + * Synth-Klasse der Implementation von Fourier-Reihen zur Klangsynthese. + * Fuer die Facharbeit vom 16.03.2017 + * Dies ist "das Herz" der Anwendung. + * Objekte dieser Klasse fuehren Berechnung und WAVE-Speichrung des Klanges aus. + * 2017, Soeren Richter + * soeren@dreieck-project.de + * Version 0.1.5 + * Code selbst geschrieben, WAVE-Speicherung basierend auf + * http://stackoverflow.com/questions/3297749/java-reading-manipulating-and-writing-wav-files + * und Abschnitt 2.2 sowie 4 der Facharbeit. + * @author Soeren Richter + * @version 0.1.5 + */ + +import java.io.ByteArrayInputStream; +import java.io.File; +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import java.awt.*; +import java.io.IOException; +import javax.swing.*; + +public class Synth { + // Samplerate des zu erzeugenden PCM-Datenstroms: + private final double samplerate; + + // Tonlänge exklusive Relese der Huellkurve (Hold-Zeit): + private final double length; + + // Frequenz der zu synthetisierenden Tons: + private final double freq; + + // relative Amplitude: + private final double amplitude; + + // Wellenform; "sine", "saw", "square" oder "triangle": + private final String waveform; + + // Attribute der Huellkurve: + private final double attack; + private final double decay; + private final double sustain; + private final double release; + + /** + * Konstruktor der Synth-Klasse. + * @param pSamplerate Samplerate des zu erzeugenden PCM-Datenstroms + * @param pLength Tonlänge exklusive Relese der Huellkurve (Hold-Zeit) + * @param pFreq Frequenz der zu synthetisierenden Tons + * @param pAmp relative Amplitude + * @param pWaveform Wellenform ("sine", "saw", "square" oder "triangle") + * @param pAttack Attack-Zeit der Huellkurve + * @param pDecay Decay-Zeit der Huellkurve + * @param pSustain Sustain-Amplitude der Huellkurve + * @param pRelease Release-Zeit der Huellkurve + */ + public Synth(double pSamplerate, double pLength, double pFreq, double pAmp, + String pWaveform, double pAttack, double pDecay, double pSustain, + double pRelease) { + samplerate = pSamplerate; + length = pLength; + freq = pFreq; + amplitude = pAmp; + waveform = pWaveform; + attack = pAttack; + decay = pDecay; + sustain = pSustain; + release = pRelease; + } + + /** + * Methode zur Durchfuehrung der Klangsynthese. + */ + public void synthesize_standard() { + // Die Zeildatei der Synthese wird nach Auswahl festgelegt. + File filePath = getFilePath(); + + // Wenn eine Datei gewaehlt wurde, wird die Klangsynthese + // durchgefuehrt, ansonsten mit entsprechendem Hinweis nicht. + if (filePath != null) { + // Oscillator- und Envelope-Objekt werden den jeweils + // festgelegten konstanten Attributen erzeugt. + Oscillator osc = new Oscillator(waveform, (int)samplerate/2); + Envelope env = new Envelope(attack,decay,sustain,release); + + // Hold-Laenge des Tons wird an das Envelope-Objekt uebergeben. + env.setLength(length); + + // Fliesskomma-Array buffer zur Aufnahme der + // berechneten Werte wird erzeugt. + float[] buffer = new float[(int)(samplerate * env.getFullLength())]; + + // Werteberechnung der synthetisierten Schallwelle wird + // entsprechend Pseudocode-Konzept durchgefuehrt, siehe: + /** + * von sample = 0 bis 44100 * (length + release), Schritt 1: + * schreibe amp * rect(sample/44100) * env(sample/44100); + */ + for (int sample = 0; sample < buffer.length; sample++) { + buffer[sample] = (float)(osc.getSample(freq, sample / samplerate) + * env.getAmplitude(sample / samplerate) + * amplitude); + } + + // Der Fliesskomma-Buffer wird an die Speichermethode weitergegeben. + save_as_wave(buffer, filePath); + + } else { + JOptionPane.showMessageDialog(new JFrame("Info"), + "Es wurde keine Datei geschrieben."); + } + + } + + /** + * Methode, die die Fliesskommareihe sampleBuffer in einen 16-Bit PCM Datenstrom konvertiert und als WAVE-Datei nach outputFile speichert. + * @param sampleBuffer Umzuwandelnde Fliesskommareihe + * @param outputFile Zieldatei der WAV-Speicherung + */ + private void save_as_wave(float[] sampleBuffer, File outputFile) { + // Erstellung eines Byte-Arrays zur Aufnahme der 16-Bit PCM-Werte. + // Fuer jeden Datenwert werden 2 Byte (2*8 Bit) benötigt. Daher muss + // der Byte-Buffer doppelt so lang sein, wie der bisherige Buffer. + final byte[] byteBuffer = new byte[sampleBuffer.length * 2]; + + int bufferIndex = 0; + + // Umwandlung der Fliesskommawerte in einen 16-Bit Datenstrom, der + // aus jeweils zwei Byte (8-Bit) Werten für jeden PCM-Wert besteht. + // Jeder zweite Wert wird vor der Zuweisung um 8 Bit-Stellen verschoben. + for (int i = 0; i < byteBuffer.length; i++) { + final int x = (int) (sampleBuffer[bufferIndex++] * 32767.0); + byteBuffer[i] = (byte) x; + i++; + byteBuffer[i] = (byte) (x >>> 8); + } + + // Nutzung der javax.sound.sampled-API zum Schreiben des Byte-Buffers + // in eine WAVE-Datei outputFile mit korrektem Header. Moegliche + // Probleme bei der Speicherung werden abgefangen. + try { + AudioFormat format = new AudioFormat((float)samplerate, 16, 1, + true, false); + ByteArrayInputStream bais = new ByteArrayInputStream(byteBuffer); + + try (AudioInputStream audioInputStream = new AudioInputStream(bais, + format, sampleBuffer.length)) { + AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, + outputFile); + } + + JOptionPane.showMessageDialog(new JFrame("Info"), + "Die WAV-Datei wurde erfolgreich generiert und nach " + + outputFile + " geschrieben."); + + } catch (HeadlessException | IOException e) { + JOptionPane.showMessageDialog(new JFrame("Info"), + "Es wurde keine Datei geschrieben, da folgender Fehler auftrat: " + + e); + } + } + + /** + * Methode zur Auswahl einer Zieldatei + * @return Pfad der WAVE-Zieldatei + */ + private File getFilePath() { + // Dateiwahldialog wird aufgerufen. + JFileChooser fc = new JFileChooser(); + int returnVal = fc.showOpenDialog(new JFrame("parent")); + + // Wurde eine Datei gewaehlt, wird diese zurueckgegeben. Falls die + // Endung ".wav" bisher fehlte, wird diese zusaetzlich angefuegt. + if (returnVal == JFileChooser.APPROVE_OPTION) { + if (fc.getSelectedFile().toString().endsWith(".wav")) + return fc.getSelectedFile(); + else { + return new File(fc.getSelectedFile().toString() + ".wav"); + } + } else { + return null; + } + } +} diff --git a/mainWindow.form b/mainWindow.form new file mode 100644 index 0000000..03bf24e --- /dev/null +++ b/mainWindow.form @@ -0,0 +1,771 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mainWindow.java b/mainWindow.java new file mode 100644 index 0000000..10b541e --- /dev/null +++ b/mainWindow.java @@ -0,0 +1,844 @@ +/** + * mainWindow-Klasse. + * Diese Klasse beherbergt die graphische Benutzeroberfläche und Ereignisse dieser. + * 2017, Soeren Richter + * soeren@dreieck-project.de + * Version 0.1.5 + * Code vollstaendig selbst geschrieben, fuer Hintergruende und + * Referenzen siehe Abschnitt 4 der Facharbeit. + * @author Soeren Richter + * @version 0.1.5 + */ + +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import java.util.Random; + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +/** + * + * @author Soeren Richter + */ +public class mainWindow extends javax.swing.JFrame { + + /** + * Creates new form mainWindow + */ + public mainWindow() { + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jButton1 = new javax.swing.JButton(); + jPanel1 = new javax.swing.JPanel(); + sineRad = new javax.swing.JRadioButton(); + sawRad = new javax.swing.JRadioButton(); + squareRad = new javax.swing.JRadioButton(); + triangleRad = new javax.swing.JRadioButton(); + jLabel8 = new javax.swing.JLabel(); + lenSpinner = new javax.swing.JSpinner(); + jLabel9 = new javax.swing.JLabel(); + jPanel2 = new javax.swing.JPanel(); + jLabel7 = new javax.swing.JLabel(); + jLabel11 = new javax.swing.JLabel(); + jLabel12 = new javax.swing.JLabel(); + jLabel13 = new javax.swing.JLabel(); + jLabel15 = new javax.swing.JLabel(); + amplFact = new javax.swing.JSpinner(); + jPanel5 = new javax.swing.JPanel(); + attackSlider = new javax.swing.JSlider(); + decaySlider = new javax.swing.JSlider(); + sustainSlider = new javax.swing.JSlider(); + releaseSlider = new javax.swing.JSlider(); + aLabel = new javax.swing.JLabel(); + dLabel = new javax.swing.JLabel(); + sLabel = new javax.swing.JLabel(); + rLabel = new javax.swing.JLabel(); + jPanel3 = new javax.swing.JPanel(); + jLabel3 = new javax.swing.JLabel(); + jLabel4 = new javax.swing.JLabel(); + keyPitch = new javax.swing.JSpinner(); + freqPitch = new javax.swing.JSpinner(); + centPitch = new javax.swing.JSpinner(); + jRadioButton1 = new javax.swing.JRadioButton(); + jRadioButton2 = new javax.swing.JRadioButton(); + jPanel4 = new javax.swing.JPanel(); + jLabel5 = new javax.swing.JLabel(); + jLabel6 = new javax.swing.JLabel(); + srBox = new javax.swing.JComboBox<>(); + jComboBox2 = new javax.swing.JComboBox<>(); + jButton2 = new javax.swing.JButton(); + jButton3 = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + setTitle("Fourier-Synthesizer Implementation Facharbeit Sören Richter"); + setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); + setResizable(false); + addWindowListener(new java.awt.event.WindowAdapter() { + public void windowOpened(java.awt.event.WindowEvent evt) { + formWindowOpened(evt); + } + }); + + jButton1.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jButton1.setText("WAVE-Datei generieren und speichern unter"); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + + jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Wellenform", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Segoe UI", 0, 12))); // NOI18N + jPanel1.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + + sineRad.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + sineRad.setSelected(true); + sineRad.setText("Sinus"); + sineRad.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + sineRadActionPerformed(evt); + } + }); + + sawRad.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + sawRad.setText("Sägezahn"); + sawRad.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + sawRadActionPerformed(evt); + } + }); + + squareRad.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + squareRad.setText("Rechteck"); + squareRad.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + squareRadActionPerformed(evt); + } + }); + + triangleRad.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + triangleRad.setText("Dreieck"); + triangleRad.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + triangleRadActionPerformed(evt); + } + }); + + jLabel8.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jLabel8.setText("Länge:"); + + lenSpinner.setModel(new javax.swing.SpinnerNumberModel(1.0d, 0.0d, 60.0d, 0.1d)); + lenSpinner.setName(""); // NOI18N + lenSpinner.setValue(1); + + jLabel9.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jLabel9.setText("sec"); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(15, 15, 15) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jLabel8) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lenSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel9)) + .addComponent(sineRad) + .addComponent(sawRad) + .addComponent(squareRad) + .addComponent(triangleRad)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(sineRad) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(sawRad) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(squareRad) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(triangleRad) + .addGap(18, 18, 18) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel8) + .addComponent(lenSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel9)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Hüllkurve", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Segoe UI", 0, 12))); // NOI18N + jPanel2.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + + jLabel7.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N + jLabel7.setText("A"); + + jLabel11.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N + jLabel11.setText("D"); + + jLabel12.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N + jLabel12.setText("S"); + + jLabel13.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N + jLabel13.setText("R"); + + jLabel15.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jLabel15.setText("Amplitudenfaktor"); + + amplFact.setModel(new javax.swing.SpinnerNumberModel(0.8d, 0.0d, 1.0d, 0.05d)); + + attackSlider.setMaximum(1000); + attackSlider.setValue(100); + attackSlider.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + attackSliderMouseClicked(evt); + } + }); + attackSlider.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + attackSliderPropertyChange(evt); + } + }); + + decaySlider.setMaximum(1000); + decaySlider.setValue(100); + decaySlider.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + decaySliderMouseClicked(evt); + } + }); + decaySlider.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + decaySliderPropertyChange(evt); + } + }); + + sustainSlider.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + sustainSliderMouseClicked(evt); + } + }); + sustainSlider.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + sustainSliderPropertyChange(evt); + } + }); + + releaseSlider.setMaximum(1000); + releaseSlider.setValue(100); + releaseSlider.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + releaseSliderMouseClicked(evt); + } + }); + releaseSlider.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + releaseSliderPropertyChange(evt); + } + }); + + javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5); + jPanel5.setLayout(jPanel5Layout); + jPanel5Layout.setHorizontalGroup( + jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel5Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel5Layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(decaySlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel5Layout.createSequentialGroup() + .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(attackSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(sustainSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(releaseSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + jPanel5Layout.setVerticalGroup( + jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel5Layout.createSequentialGroup() + .addContainerGap() + .addComponent(attackSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(decaySlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(sustainSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(releaseSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + aLabel.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + aLabel.setText("100 ms"); + + dLabel.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + dLabel.setText("100 ms"); + + sLabel.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + sLabel.setText("50%"); + + rLabel.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + rLabel.setText("100 ms"); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(18, 18, 18) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(jLabel15) + .addGap(18, 18, 18) + .addComponent(amplFact, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel7) + .addComponent(jLabel11) + .addComponent(jLabel12) + .addComponent(jLabel13)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jPanel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(aLabel) + .addComponent(dLabel) + .addComponent(sLabel) + .addComponent(rLabel)))) + .addContainerGap(19, Short.MAX_VALUE)) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(4, 4, 4) + .addComponent(aLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(dLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(sLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(rLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(67, 67, 67)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(jLabel7) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel11) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel12) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel13) + .addGap(26, 26, 26) + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel15) + .addComponent(amplFact, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(jPanel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(51, 51, 51)) + ); + + jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Tonhöhe", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Segoe UI", 0, 12))); // NOI18N + jPanel3.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + + jLabel3.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jLabel3.setText("+"); + + jLabel4.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jLabel4.setText("cents"); + + keyPitch.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + keyPitch.setModel(new javax.swing.SpinnerNumberModel(0.0d, -66.0d, 66.0d, 1.0d)); + keyPitch.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + keyPitchPropertyChange(evt); + } + }); + + freqPitch.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + freqPitch.setModel(new javax.swing.SpinnerNumberModel(440.0d, 0.0d, 20000.0d, 0.1d)); + freqPitch.setEnabled(false); + freqPitch.setValue(440.0); + freqPitch.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + freqPitchPropertyChange(evt); + } + }); + + centPitch.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + centPitch.setModel(new javax.swing.SpinnerNumberModel(0.0d, 0.0d, 100.0d, 0.1d)); + centPitch.addPropertyChangeListener(new java.beans.PropertyChangeListener() { + public void propertyChange(java.beans.PropertyChangeEvent evt) { + centPitchPropertyChange(evt); + } + }); + + jRadioButton1.setSelected(true); + jRadioButton1.setText("Ton (a1 = 0)"); + jRadioButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jRadioButton1ActionPerformed(evt); + } + }); + + jRadioButton2.setText("Freq. / Hz"); + jRadioButton2.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jRadioButton2ActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); + jPanel3.setLayout(jPanel3Layout); + jPanel3Layout.setHorizontalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(jPanel3Layout.createSequentialGroup() + .addGap(33, 33, 33) + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addComponent(jLabel3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(centPitch, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel4)) + .addGroup(jPanel3Layout.createSequentialGroup() + .addComponent(keyPitch, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(freqPitch, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addGroup(jPanel3Layout.createSequentialGroup() + .addGap(14, 14, 14) + .addComponent(jRadioButton1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jRadioButton2))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + jPanel3Layout.setVerticalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addGap(8, 8, 8) + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jRadioButton1) + .addComponent(jRadioButton2)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(keyPitch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(freqPitch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent(centPitch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel4)) + .addContainerGap(22, Short.MAX_VALUE)) + ); + + jPanel4.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "PCM/WAVE-Parameter", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Segoe UI", 0, 12))); // NOI18N + jPanel4.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + + jLabel5.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jLabel5.setText("Samplerate"); + + jLabel6.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jLabel6.setText("Bits/Sample"); + jLabel6.setEnabled(false); + + srBox.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + srBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "8000", "11025", "16000", "22050", "32000", "44100", "48000", "88200", "96000" })); + srBox.setSelectedIndex(5); + + jComboBox2.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jComboBox2.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "8", "16", "24", "32" })); + jComboBox2.setSelectedIndex(1); + jComboBox2.setSelectedItem(1); + jComboBox2.setEnabled(false); + + javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4); + jPanel4.setLayout(jPanel4Layout); + jPanel4Layout.setHorizontalGroup( + jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel4Layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel5, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(srBox, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(27, 27, 27) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel6, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jComboBox2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(29, 29, 29)) + ); + jPanel4Layout.setVerticalGroup( + jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel4Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel5) + .addComponent(jLabel6)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(srBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + jButton2.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jButton2.setText("Parameter zufällig setzen"); + jButton2.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2ActionPerformed(evt); + } + }); + + jButton3.setFont(new java.awt.Font("Segoe UI", 0, 12)); // NOI18N + jButton3.setText("Programminfo"); + jButton3.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton3ActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addGroup(layout.createSequentialGroup() + .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 119, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 177, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 269, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(layout.createSequentialGroup() + .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jButton1) + .addComponent(jButton2) + .addComponent(jButton3)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + Thread.yield(); + + final String tempWaveform; + + if (sineRad.isSelected()) { + tempWaveform = "sine"; + } else if (sawRad.isSelected()) { + tempWaveform = "saw"; + } else if (squareRad.isSelected()) { + tempWaveform = "square"; + } else if (triangleRad.isSelected()) { + tempWaveform = "triangle"; + } else { + tempWaveform = "sine"; + } + + java.awt.EventQueue.invokeLater(() -> { + Synth gen = new Synth(Double.parseDouble((String) srBox.getSelectedItem()), + (double)lenSpinner.getValue(), + (double)freqPitch.getValue(), + (double)amplFact.getValue(), + tempWaveform, + attackSlider.getValue() / 1000.0, + decaySlider.getValue() / 1000.0, + sustainSlider.getValue() / 100.0, + releaseSlider.getValue() / 1000.0); + gen.synthesize_standard(); + }); + }//GEN-LAST:event_jButton1ActionPerformed + + private void sineRadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sineRadActionPerformed + sawRad.setSelected(false); + squareRad.setSelected(false); + triangleRad.setSelected(false); + }//GEN-LAST:event_sineRadActionPerformed + + private void attackSliderPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_attackSliderPropertyChange + aLabel.setText(attackSlider.getValue() + " ms"); + }//GEN-LAST:event_attackSliderPropertyChange + + private void decaySliderPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_decaySliderPropertyChange + dLabel.setText(decaySlider.getValue() + " ms"); + }//GEN-LAST:event_decaySliderPropertyChange + + private void sustainSliderPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_sustainSliderPropertyChange + sLabel.setText(sustainSlider.getValue() + "%"); + }//GEN-LAST:event_sustainSliderPropertyChange + + private void releaseSliderPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_releaseSliderPropertyChange + rLabel.setText(releaseSlider.getValue() + " ms"); + }//GEN-LAST:event_releaseSliderPropertyChange + + private void sawRadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sawRadActionPerformed + sineRad.setSelected(false); + squareRad.setSelected(false); + triangleRad.setSelected(false); + }//GEN-LAST:event_sawRadActionPerformed + + private void squareRadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_squareRadActionPerformed + sawRad.setSelected(false); + sineRad.setSelected(false); + triangleRad.setSelected(false); + }//GEN-LAST:event_squareRadActionPerformed + + private void triangleRadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_triangleRadActionPerformed + sawRad.setSelected(false); + squareRad.setSelected(false); + sineRad.setSelected(false); + }//GEN-LAST:event_triangleRadActionPerformed + + private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed + Random r = new Random(); + + switch (r.nextInt(4)){ + case 0: + sineRad.setSelected(true); + sawRad.setSelected(false); + squareRad.setSelected(false); + triangleRad.setSelected(false); + break; + case 1: + sineRad.setSelected(false); + sawRad.setSelected(true); + squareRad.setSelected(false); + triangleRad.setSelected(false); + break; + case 2: + sineRad.setSelected(false); + sawRad.setSelected(false); + squareRad.setSelected(true); + triangleRad.setSelected(false); + break; + case 3: + sineRad.setSelected(false); + sawRad.setSelected(false); + squareRad.setSelected(false); + triangleRad.setSelected(true); + break; + } + + freqPitch.setValue(r.nextDouble()*20000.0); + lenSpinner.setValue(r.nextDouble()); + attackSlider.setValue(r.nextInt(1001)); + decaySlider.setValue(r.nextInt(1001)); + sustainSlider.setValue(r.nextInt(101)); + releaseSlider.setValue(r.nextInt(1001)); + amplFact.setValue(r.nextDouble()); + + aLabel.setText(attackSlider.getValue() + " ms"); + dLabel.setText(decaySlider.getValue() + " ms"); + sLabel.setText(sustainSlider.getValue() + "%"); + rLabel.setText(releaseSlider.getValue() + " ms"); + keyPitch.setValue(pitch.key((double) freqPitch.getValue()) + - pitch.key((double) freqPitch.getValue()) % 1); + centPitch.setValue((pitch.key((double) freqPitch.getValue()) + % 1) * 100); + + Thread.yield(); + }//GEN-LAST:event_jButton2ActionPerformed + + private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed + JOptionPane.showMessageDialog(new JFrame("Programminfo"), + "Referenz-Implementation eines Fourier-" + + "Synthesizers für die Facharbeit vom " + + "16.03.2017\n" + + "2016-2017, Sören Richter\n" + + "Version: 0.1.5"); + }//GEN-LAST:event_jButton3ActionPerformed + + private void jRadioButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jRadioButton1ActionPerformed + if (jRadioButton1.isSelected()){ + freqPitch.setEnabled(false); + jRadioButton2.setSelected(false); + keyPitch.setEnabled(true); + centPitch.setEnabled(true); + } + }//GEN-LAST:event_jRadioButton1ActionPerformed + + private void jRadioButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jRadioButton2ActionPerformed + if (jRadioButton2.isSelected()){ + keyPitch.setEnabled(false); + centPitch.setEnabled(false); + jRadioButton1.setSelected(false); + freqPitch.setEnabled(true); + } + }//GEN-LAST:event_jRadioButton2ActionPerformed + + private void freqPitchPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_freqPitchPropertyChange + if (evt.getSource()==freqPitch){ + keyPitch.setValue(pitch.key((double) freqPitch.getValue()) + - pitch.key((double) freqPitch.getValue()) % 1); + centPitch.setValue((pitch.key((double) freqPitch.getValue()) + % 1) * 100); + } + }//GEN-LAST:event_freqPitchPropertyChange + + private void keyPitchPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_keyPitchPropertyChange + if (evt.getSource()==keyPitch){ + freqPitch.setValue(pitch.freq((double) keyPitch.getValue() + + (double) centPitch.getValue() / 100.0)); + } + }//GEN-LAST:event_keyPitchPropertyChange + + private void centPitchPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_centPitchPropertyChange + if (evt.getSource()==centPitch){ + freqPitch.setValue(pitch.freq((double) keyPitch.getValue() + + (double) centPitch.getValue() / 100.0)); + } + }//GEN-LAST:event_centPitchPropertyChange + + private void attackSliderMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_attackSliderMouseClicked + aLabel.setText(attackSlider.getValue() + " ms"); + }//GEN-LAST:event_attackSliderMouseClicked + + private void decaySliderMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_decaySliderMouseClicked + dLabel.setText(decaySlider.getValue() + " ms"); + }//GEN-LAST:event_decaySliderMouseClicked + + private void sustainSliderMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sustainSliderMouseClicked + sLabel.setText(sustainSlider.getValue() + "%"); + }//GEN-LAST:event_sustainSliderMouseClicked + + private void releaseSliderMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_releaseSliderMouseClicked + rLabel.setText(releaseSlider.getValue() + " ms"); + }//GEN-LAST:event_releaseSliderMouseClicked + + private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened + keyPitch.setValue(0.0); + centPitch.setValue(0.0); + freqPitch.setValue(440.0); + lenSpinner.setValue(0.9); + attackSlider.setValue(100); + decaySlider.setValue(100); + sustainSlider.setValue(50); + releaseSlider.setValue(100); + amplFact.setValue(0.8); + + Thread.yield(); + }//GEN-LAST:event_formWindowOpened + + /** + * @param args the command line arguments + */ + public static void main(String args[]) { + // + /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. + * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html + */ + try { +// for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { +// if ("Nimbus".equals(info.getName())) { +// javax.swing.UIManager.setLookAndFeel(info.getClassName()); +// break; +// } +// } + javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException ex) { + java.util.logging.Logger.getLogger(mainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (InstantiationException ex) { + java.util.logging.Logger.getLogger(mainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (IllegalAccessException ex) { + java.util.logging.Logger.getLogger(mainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (javax.swing.UnsupportedLookAndFeelException ex) { + java.util.logging.Logger.getLogger(mainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } + // + + /* Create and display the form */ + java.awt.EventQueue.invokeLater(() -> { + new mainWindow().setVisible(true); + }); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel aLabel; + private javax.swing.JSpinner amplFact; + private javax.swing.JSlider attackSlider; + private javax.swing.JSpinner centPitch; + private javax.swing.JLabel dLabel; + private javax.swing.JSlider decaySlider; + private javax.swing.JSpinner freqPitch; + private javax.swing.JButton jButton1; + private javax.swing.JButton jButton2; + private javax.swing.JButton jButton3; + private javax.swing.JComboBox jComboBox2; + private javax.swing.JLabel jLabel11; + private javax.swing.JLabel jLabel12; + private javax.swing.JLabel jLabel13; + private javax.swing.JLabel jLabel15; + private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel4; + private javax.swing.JLabel jLabel5; + private javax.swing.JLabel jLabel6; + private javax.swing.JLabel jLabel7; + private javax.swing.JLabel jLabel8; + private javax.swing.JLabel jLabel9; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JPanel jPanel3; + private javax.swing.JPanel jPanel4; + private javax.swing.JPanel jPanel5; + private javax.swing.JRadioButton jRadioButton1; + private javax.swing.JRadioButton jRadioButton2; + private javax.swing.JSpinner keyPitch; + private javax.swing.JSpinner lenSpinner; + private javax.swing.JLabel rLabel; + private javax.swing.JSlider releaseSlider; + private javax.swing.JLabel sLabel; + private javax.swing.JRadioButton sawRad; + private javax.swing.JRadioButton sineRad; + private javax.swing.JRadioButton squareRad; + private javax.swing.JComboBox srBox; + private javax.swing.JSlider sustainSlider; + private javax.swing.JRadioButton triangleRad; + // End of variables declaration//GEN-END:variables +} diff --git a/pitch.java b/pitch.java new file mode 100644 index 0000000..55a88ff --- /dev/null +++ b/pitch.java @@ -0,0 +1,33 @@ +/** + * Statische Klasse zur Frequenz-/Halbtonumrechnung in der gleichstufigen Stimmung + * Fuer die Facharbeit vom 16.03.2017 + * 2017, Soeren Richter + * soeren@dreieck-project.de + * Version 0.1.5 + * Auf Grundlage von Abschnitt 2.3 der Facharbeit. + * @author Soeren Richter + * @version 0.1.5 + */ + +public class pitch +{ + /** + * Rechnet Halbtonschritte von a aus zu Frequenzwert in Hz um. + * @param key Halbtonschritte vom Kammerton a aus + * @return Frequenz in Hz + */ + public static double freq(double key) + { + return Math.pow(2.0, key/12.0)*440.0; + } + + /** + * Rechnet Frequenzwert in Hz zu Halbtonschritten von a aus um. + * @param freq Frequenz in Hz + * @return Halbtonschritte vom Kammerton a aus + */ + public static double key(double freq) + { + return (Math.log(freq/440.0)/Math.log(2.0))*12.0; + } +}