Add files via upload
This commit is contained in:
1 parent
afbe80adcf
commit
186f862656
4 files changed
+385
No files matched your search
@@ -0,0 +1,89 @@
|
|||||||
|
package me.SoundDraw;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.function.IntSupplier;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioFormat;
|
||||||
|
import javax.sound.sampled.AudioSystem;
|
||||||
|
import javax.sound.sampled.DataLine;
|
||||||
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
|
import javax.sound.sampled.SourceDataLine;
|
||||||
|
|
||||||
|
public class AudioPlayer extends Thread {
|
||||||
|
|
||||||
|
private final IntToByteFunction func;
|
||||||
|
private final IntSupplier volume;
|
||||||
|
|
||||||
|
private final AudioFormat format;
|
||||||
|
private final SourceDataLine sourceDataLine;
|
||||||
|
|
||||||
|
public AudioPlayer(IntToByteFunction func, IntSupplier volume) throws LineUnavailableException {
|
||||||
|
this.func = func;
|
||||||
|
this.volume = volume;
|
||||||
|
|
||||||
|
format = new AudioFormat(Main.SAMPLE_RATE, 8, 1, true, false);
|
||||||
|
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
|
||||||
|
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
sourceDataLine.open(format);
|
||||||
|
} catch (LineUnavailableException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
sourceDataLine.start();
|
||||||
|
|
||||||
|
try (AudioGen gen = new AudioGen()) {
|
||||||
|
byte[] data = new byte[4096];
|
||||||
|
int read = 0;
|
||||||
|
|
||||||
|
while ((read = gen.read(data)) > 0) {
|
||||||
|
sourceDataLine.write(data, 0, read);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
sourceDataLine.drain();
|
||||||
|
sourceDataLine.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AudioGen extends InputStream {
|
||||||
|
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return (int) (func.apply(index++) * volume.getAsInt() / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte b[], int off, int len) throws IOException {
|
||||||
|
if (b == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
} else if (off < 0 || len < 0 || len > b.length - off) {
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
} else if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int c = read();
|
||||||
|
b[off] = (byte) c;
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
try {
|
||||||
|
for (; i < len; i++) {
|
||||||
|
c = read();
|
||||||
|
b[off + i] = (byte) c;
|
||||||
|
}
|
||||||
|
} catch (IOException ee) {
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package me.SoundDraw;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.event.ComponentAdapter;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ContainerAdapter;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
public class DrawingBoard extends JPanel {
|
||||||
|
|
||||||
|
private static final int STEP = 100;
|
||||||
|
private static final int DEFAULT_SIZE = STEP * 5;
|
||||||
|
|
||||||
|
private final MouseListener listener;
|
||||||
|
private byte[] data = new byte[DEFAULT_SIZE];
|
||||||
|
private int lastEditedSize = DEFAULT_SIZE;
|
||||||
|
|
||||||
|
private boolean down = false;
|
||||||
|
private Point lastPos;
|
||||||
|
|
||||||
|
public DrawingBoard() {
|
||||||
|
listener = new MouseListener();
|
||||||
|
setBackground(Color.black);
|
||||||
|
addMouseListener(listener);
|
||||||
|
addMouseMotionListener(listener);
|
||||||
|
addComponentListener(new ComponentAdapter() {
|
||||||
|
@Override
|
||||||
|
public void componentResized(ComponentEvent e) {
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getSampleAt(int idx) {
|
||||||
|
return data[idx % data.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int resizeData(int mul) {
|
||||||
|
int size = mul * STEP;
|
||||||
|
|
||||||
|
if (data.length == size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int oldSize = data.length;
|
||||||
|
data = Arrays.copyOf(data, size);
|
||||||
|
|
||||||
|
if (oldSize < size) {
|
||||||
|
for (int i = lastEditedSize; i + lastEditedSize <= size; i += lastEditedSize) {
|
||||||
|
System.arraycopy(data, 0, data, i, lastEditedSize);
|
||||||
|
}
|
||||||
|
} else if (size < lastEditedSize) {
|
||||||
|
lastEditedSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(this::repaint);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
|
||||||
|
int width = getWidth();
|
||||||
|
int height = getHeight();
|
||||||
|
|
||||||
|
g.setColor(Color.darkGray);
|
||||||
|
g.drawLine(0, height / 2, width, height / 2);
|
||||||
|
|
||||||
|
g.setColor(Color.white);
|
||||||
|
for (int i = 0; i < data.length - 1; i++) {
|
||||||
|
g.drawLine(i * width / data.length, (data[i] + 128) * height / 256, (i + 1) * width / data.length,
|
||||||
|
(data[i + 1] + 128) * height / 256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MouseListener extends MouseAdapter {
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
down = true;
|
||||||
|
lastPos = e.getPoint();
|
||||||
|
firePaint(e.getPoint());
|
||||||
|
e.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
firePaint(e.getPoint());
|
||||||
|
down = false;
|
||||||
|
lastPos = null;
|
||||||
|
e.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
firePaint(e.getPoint());
|
||||||
|
e.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void firePaint(Point pos) {
|
||||||
|
if (!down)
|
||||||
|
return;
|
||||||
|
lastEditedSize = data.length;
|
||||||
|
|
||||||
|
int width = getWidth();
|
||||||
|
int height = getHeight();
|
||||||
|
int stepHalfX = width / data.length / 2;
|
||||||
|
int stepHalfY = height / 256 / 2;
|
||||||
|
|
||||||
|
if (pos.y < 0)
|
||||||
|
pos.y = 0;
|
||||||
|
else if (pos.y >= height)
|
||||||
|
pos.y = height - 1;
|
||||||
|
|
||||||
|
if (lastPos == null)
|
||||||
|
lastPos = pos;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Point pLow = pos.x < lastPos.x ? pos : lastPos;
|
||||||
|
Point pHigh = pos.x < lastPos.x ? lastPos : pos;
|
||||||
|
|
||||||
|
int lastIdx = (pLow.x + stepHalfX) * data.length / width;
|
||||||
|
int newIdx = (pHigh.x + stepHalfX) * data.length / width;
|
||||||
|
int lastVal = pLow.y;
|
||||||
|
int newVal = pHigh.y;
|
||||||
|
int diffIdx = newIdx - lastIdx + 1;
|
||||||
|
float diff = newVal - lastVal;
|
||||||
|
|
||||||
|
for (int curIdx = lastIdx, off = 0; (curIdx = lastIdx + off) <= newIdx; off++) {
|
||||||
|
if (curIdx < 0 || curIdx >= data.length)
|
||||||
|
continue;
|
||||||
|
int val = Math.round(lastVal + diff * off / diffIdx);
|
||||||
|
data[curIdx] = (byte) (((val * 256 + stepHalfY) / height) - 128);
|
||||||
|
curIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
repaint();
|
||||||
|
} finally {
|
||||||
|
lastPos = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package me.SoundDraw;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface IntToByteFunction {
|
||||||
|
byte apply(int value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package me.SoundDraw;
|
||||||
|
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import javax.swing.JSlider;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.GroupLayout;
|
||||||
|
import javax.swing.GroupLayout.Alignment;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import javax.swing.border.LineBorder;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import javax.swing.SpringLayout;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import java.awt.Component;
|
||||||
|
import javax.swing.Box;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static final int SAMPLE_RATE = 44100;
|
||||||
|
|
||||||
|
private JFrame frmAudiodraw;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch the application.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
EventQueue.invokeLater(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Main window = new Main();
|
||||||
|
window.frmAudiodraw.setVisible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the application.
|
||||||
|
*
|
||||||
|
* @throws LineUnavailableException
|
||||||
|
*/
|
||||||
|
public Main() throws LineUnavailableException {
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
|
} catch (Exception e1) {
|
||||||
|
e1.printStackTrace();
|
||||||
|
}
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the contents of the frame.
|
||||||
|
*
|
||||||
|
* @throws LineUnavailableException
|
||||||
|
*/
|
||||||
|
private void initialize() throws LineUnavailableException {
|
||||||
|
frmAudiodraw = new JFrame();
|
||||||
|
frmAudiodraw.getContentPane().setBackground(Color.DARK_GRAY);
|
||||||
|
frmAudiodraw.setTitle("Sound Draw");
|
||||||
|
frmAudiodraw.setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 250),
|
||||||
|
(int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 150), 500, 300);
|
||||||
|
frmAudiodraw.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
SpringLayout springLayout = new SpringLayout();
|
||||||
|
frmAudiodraw.getContentPane().setLayout(springLayout);
|
||||||
|
|
||||||
|
DrawingBoard board = new DrawingBoard();
|
||||||
|
springLayout.putConstraint(SpringLayout.WEST, board, 10, SpringLayout.WEST, frmAudiodraw.getContentPane());
|
||||||
|
springLayout.putConstraint(SpringLayout.SOUTH, board, -10, SpringLayout.SOUTH, frmAudiodraw.getContentPane());
|
||||||
|
springLayout.putConstraint(SpringLayout.EAST, board, -10, SpringLayout.EAST, frmAudiodraw.getContentPane());
|
||||||
|
frmAudiodraw.getContentPane().add(board);
|
||||||
|
|
||||||
|
JPanel controls = new JPanel();
|
||||||
|
controls.setBackground(Color.DARK_GRAY);
|
||||||
|
springLayout.putConstraint(SpringLayout.NORTH, controls, 10, SpringLayout.NORTH, frmAudiodraw.getContentPane());
|
||||||
|
springLayout.putConstraint(SpringLayout.WEST, controls, 10, SpringLayout.WEST, frmAudiodraw.getContentPane());
|
||||||
|
springLayout.putConstraint(SpringLayout.EAST, controls, -10, SpringLayout.EAST, frmAudiodraw.getContentPane());
|
||||||
|
springLayout.putConstraint(SpringLayout.NORTH, board, 10, SpringLayout.SOUTH, controls);
|
||||||
|
frmAudiodraw.getContentPane().add(controls);
|
||||||
|
|
||||||
|
JPanel panelVolume = new JPanel();
|
||||||
|
panelVolume.setBackground(Color.DARK_GRAY);
|
||||||
|
panelVolume.setLayout(new BoxLayout(panelVolume, BoxLayout.X_AXIS));
|
||||||
|
|
||||||
|
JLabel lblVolume = new JLabel("Volume");
|
||||||
|
lblVolume.setForeground(Color.WHITE);
|
||||||
|
panelVolume.add(lblVolume);
|
||||||
|
|
||||||
|
JSlider sliderVolume = new JSlider();
|
||||||
|
sliderVolume.setBackground(Color.DARK_GRAY);
|
||||||
|
panelVolume.add(sliderVolume);
|
||||||
|
sliderVolume.setFocusable(false);
|
||||||
|
sliderVolume.setValue(10);
|
||||||
|
|
||||||
|
JPanel panelLength = new JPanel();
|
||||||
|
panelLength.setBackground(Color.DARK_GRAY);
|
||||||
|
panelLength.setLayout(new BoxLayout(panelLength, BoxLayout.X_AXIS));
|
||||||
|
|
||||||
|
JLabel lblLength = new JLabel("Length (0.05s)");
|
||||||
|
lblLength.setForeground(Color.WHITE);
|
||||||
|
panelLength.add(lblLength);
|
||||||
|
|
||||||
|
JSlider sliderLength = new JSlider();
|
||||||
|
sliderLength.setMaximum(SAMPLE_RATE / 100 * 2);
|
||||||
|
sliderLength.setBackground(Color.DARK_GRAY);
|
||||||
|
sliderLength.setSnapToTicks(true);
|
||||||
|
sliderLength.setValue(22);
|
||||||
|
sliderLength.setMinimum(22);
|
||||||
|
panelLength.add(sliderLength);
|
||||||
|
sliderLength.setFocusable(false);
|
||||||
|
sliderLength.addChangeListener(e -> {
|
||||||
|
int size = board.resizeData(sliderLength.getValue());
|
||||||
|
if (size != -1) {
|
||||||
|
lblLength
|
||||||
|
.setText("Length (" + String.format(Locale.ENGLISH, "%.2fs", size / (float) SAMPLE_RATE) + ")");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
controls.setLayout(new BoxLayout(controls, BoxLayout.X_AXIS));
|
||||||
|
controls.add(panelVolume);
|
||||||
|
|
||||||
|
Component spacer = Box.createHorizontalStrut(20);
|
||||||
|
controls.add(spacer);
|
||||||
|
controls.add(panelLength);
|
||||||
|
|
||||||
|
AudioPlayer player = new AudioPlayer(board::getSampleAt, sliderVolume::getValue);
|
||||||
|
player.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user