kopia lustrzana https://github.com/jameshball/osci-render
commit
02e5c00fa2
|
@ -3,30 +3,47 @@ package audio;
|
|||
import engine.Camera;
|
||||
import engine.Vector3;
|
||||
import engine.WorldObject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import shapes.Shape;
|
||||
import shapes.Shapes;
|
||||
import shapes.Vector2;
|
||||
|
||||
public class AudioClient {
|
||||
private static final int SAMPLE_RATE = 192000;
|
||||
private static final double TARGET_FRAMERATE = 30;
|
||||
public static final int SAMPLE_RATE = 192000;
|
||||
public static final double TARGET_FRAMERATE = 30;
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
public static void main(String[] args) {
|
||||
// TODO: Calculate weight of lines using depth.
|
||||
// Reduce weight of lines drawn multiple times.
|
||||
// Find intersections of lines to (possibly) improve line cleanup.
|
||||
// Improve performance of line cleanup with a heuristic.
|
||||
|
||||
AudioPlayer player = new AudioPlayer(SAMPLE_RATE, 440);
|
||||
|
||||
Camera camera = new Camera(0.6, new Vector3(0, 0, -3));
|
||||
WorldObject cube = new WorldObject(args[0], new Vector3(0, 0, 0), new Vector3());
|
||||
Camera camera = new Camera(0.6, new Vector3(0, 0, -0.08));
|
||||
WorldObject object = new WorldObject(args[0], new Vector3(0, 0, 0), new Vector3());
|
||||
Vector3 rotation = new Vector3(0,Math.PI / 100,Math.PI / 100);
|
||||
List<List<? extends Shape>> preRenderedFrames = new ArrayList<>();
|
||||
|
||||
player.start();
|
||||
int numFrames = (int) (Float.parseFloat(args[1]) * TARGET_FRAMERATE);
|
||||
|
||||
while (true) {
|
||||
AudioPlayer.updateFrame(Shapes.sortLines(camera.draw(cube)));
|
||||
cube.rotate(rotation);
|
||||
Thread.sleep((long) (1000 / TARGET_FRAMERATE));
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
preRenderedFrames.add(new ArrayList<>());
|
||||
}
|
||||
|
||||
IntStream.range(0, numFrames).parallel().forEach((frameNum) -> {
|
||||
WorldObject clone = object.clone();
|
||||
clone.rotate(rotation.scale(frameNum));
|
||||
preRenderedFrames.set(frameNum, Shapes.sortLines(camera.draw(clone)));
|
||||
});
|
||||
|
||||
System.out.println(System.currentTimeMillis() - start);
|
||||
|
||||
AudioPlayer player = new AudioPlayer(SAMPLE_RATE, preRenderedFrames);
|
||||
//AudioPlayer.setRotateSpeed(1);
|
||||
//AudioPlayer.setTranslation(1, new Vector2(1, 1));
|
||||
player.start();
|
||||
}
|
||||
}
|
|
@ -7,17 +7,17 @@ import shapes.Vector2;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class AudioPlayer extends Thread {
|
||||
private static double[] phases = new double[2];
|
||||
private static XtFormat FORMAT;
|
||||
|
||||
private static List<Shape> shapes = new ArrayList<>();
|
||||
private static Lock lock = new ReentrantLock();
|
||||
private static List<List<? extends Shape>> frames = new ArrayList<>();
|
||||
private static int currentFrame = 0;
|
||||
private static int currentShape = 0;
|
||||
private static int framesDrawn = 0;
|
||||
private static long timeOfLastFrame;
|
||||
private static int audioFramesDrawn = 0;
|
||||
|
||||
private static double TRANSLATE_SPEED = 0;
|
||||
private static Vector2 TRANSLATE_VECTOR;
|
||||
|
@ -27,16 +27,17 @@ public class AudioPlayer extends Thread {
|
|||
private static double SCALE = 1;
|
||||
private static double WEIGHT = 100;
|
||||
|
||||
private boolean stopped;
|
||||
private volatile boolean stopped;
|
||||
|
||||
public AudioPlayer(int sampleRate, double frequency) {
|
||||
public AudioPlayer(int sampleRate, List<List<? extends Shape>> frames) {
|
||||
AudioPlayer.FORMAT = new XtFormat(new XtMix(sampleRate, XtSample.FLOAT32), 0, 0, 2, 0);
|
||||
AudioPlayer.frames = frames;
|
||||
AudioPlayer.timeOfLastFrame = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
static void render(XtStream stream, Object input, Object output, int frames,
|
||||
static void render(XtStream stream, Object input, Object output, int audioFrames,
|
||||
double time, long position, boolean timeValid, long error, Object user) {
|
||||
lock.lock();
|
||||
for (int f = 0; f < frames; f++) {
|
||||
for (int f = 0; f < audioFrames; f++) {
|
||||
Shape shape = getCurrentShape();
|
||||
|
||||
shape = shape.setWeight(WEIGHT);
|
||||
|
@ -44,22 +45,27 @@ public class AudioPlayer extends Thread {
|
|||
shape = rotate(shape, FORMAT.mix.rate);
|
||||
shape = translate(shape, FORMAT.mix.rate);
|
||||
|
||||
double framesToDraw = shape.getWeight() * shape.getLength();
|
||||
double drawingProgress = framesToDraw == 0 ? 1 : framesDrawn / framesToDraw;
|
||||
double totalAudioFrames = shape.getWeight() * shape.getLength();
|
||||
double drawingProgress = totalAudioFrames == 0 ? 1 : audioFramesDrawn / totalAudioFrames;
|
||||
|
||||
for (int c = 0; c < FORMAT.outputs; c++) {
|
||||
((float[]) output)[f * FORMAT.outputs] = (float) shape.nextX(drawingProgress);
|
||||
((float[]) output)[f * FORMAT.outputs + 1] = (float) shape.nextY(drawingProgress);
|
||||
((float[]) output)[f * FORMAT.outputs] = shape.nextX(drawingProgress);
|
||||
((float[]) output)[f * FORMAT.outputs + 1] = shape.nextY(drawingProgress);
|
||||
}
|
||||
|
||||
framesDrawn++;
|
||||
audioFramesDrawn++;
|
||||
|
||||
if (framesDrawn > framesToDraw) {
|
||||
framesDrawn = 0;
|
||||
currentShape++;
|
||||
if (audioFramesDrawn > totalAudioFrames) {
|
||||
audioFramesDrawn = 0;
|
||||
currentShape = ++currentShape % AudioPlayer.frames.get(currentFrame).size();
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - timeOfLastFrame > (float) 1000 / AudioClient.TARGET_FRAMERATE) {
|
||||
currentShape = 0;
|
||||
currentFrame = ++currentFrame % AudioPlayer.frames.size();
|
||||
timeOfLastFrame = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
private static Shape rotate(Shape shape, double sampleRate) {
|
||||
|
@ -122,25 +128,19 @@ public class AudioPlayer extends Thread {
|
|||
}
|
||||
|
||||
public static void updateFrame(List<? extends Shape> frame) {
|
||||
lock.lock();
|
||||
currentShape = 0;
|
||||
shapes = new ArrayList<>();
|
||||
shapes.addAll(frame);
|
||||
// Arbitrary function for changing weights based on frame draw-time.
|
||||
AudioPlayer.WEIGHT = 200 * Math.exp(-0.017 * Shapes.totalLength(frame));
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
private static Shape getCurrentShape() {
|
||||
if (shapes.size() == 0) {
|
||||
if (frames.size() == 0 || frames.get(currentFrame).size() == 0) {
|
||||
return new Vector2(0, 0);
|
||||
}
|
||||
|
||||
if (currentShape >= shapes.size()) {
|
||||
currentShape -= shapes.size();
|
||||
}
|
||||
|
||||
return shapes.get(currentShape);
|
||||
return frames.get(currentFrame).get(currentShape);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -84,4 +84,8 @@ public final class Vector3 {
|
|||
|
||||
return mean.scale(1f / (points.size()));
|
||||
}
|
||||
|
||||
public Vector3 clone() {
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,21 @@ public class WorldObject {
|
|||
loadFromFile(filename);
|
||||
}
|
||||
|
||||
public WorldObject(List<Vector3> vertices, List<Integer> edgeData, Vector3 position, Vector3 rotation) {
|
||||
this.vertices = vertices;
|
||||
this.edgeData = edgeData;
|
||||
this.position = position;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public void rotate(Vector3 theta) {
|
||||
rotation = rotation.add(theta);
|
||||
}
|
||||
|
||||
public void resetRotation() {
|
||||
rotation = new Vector3();
|
||||
}
|
||||
|
||||
public List<Vector3> getVertices() {
|
||||
List<Vector3> newVertices = new ArrayList<>();
|
||||
|
||||
|
@ -67,4 +78,20 @@ public class WorldObject {
|
|||
throw new IllegalArgumentException("Cannot load mesh data from: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
public WorldObject clone() {
|
||||
List<Vector3> newVertices = new ArrayList<>();
|
||||
|
||||
for (Vector3 vertex : vertices) {
|
||||
newVertices.add(vertex.clone());
|
||||
}
|
||||
|
||||
List<Integer> newEdgeData = new ArrayList<>();
|
||||
|
||||
for (int edge : edgeData) {
|
||||
newEdgeData.add(edge);
|
||||
}
|
||||
|
||||
return new WorldObject(newVertices, newEdgeData, position.clone(), rotation.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ public class Shapes {
|
|||
.orElse(0d);
|
||||
}
|
||||
|
||||
public static List<Line> sortLines(List<Line> lines) {
|
||||
public static List<Shape> sortLines(List<Line> lines) {
|
||||
Graph<Vector2, DefaultWeightedEdge> graph = new DefaultUndirectedWeightedGraph<>(DefaultWeightedEdge.class);
|
||||
|
||||
for (Line line : lines) {
|
||||
|
@ -79,7 +79,7 @@ public class Shapes {
|
|||
|
||||
ConnectivityInspector<Vector2, DefaultWeightedEdge> inspector = new ConnectivityInspector<>(graph);
|
||||
|
||||
List<Line> sortedLines = new ArrayList<>();
|
||||
List<Shape> sortedLines = new ArrayList<>();
|
||||
|
||||
for (Set<Vector2> vertices : inspector.connectedSets()) {
|
||||
AsSubgraph<Vector2, DefaultWeightedEdge> subgraph = new AsSubgraph<>(graph, vertices);
|
||||
|
|
Ładowanie…
Reference in New Issue