mirror of
https://github.com/Astatin3/Code.org-Final-Projects.git
synced 2026-06-08 16:08:02 -06:00
Add code
This commit is contained in:
+133
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @author Orlando Selenu
|
||||
* Originally written in the Summer of 2008
|
||||
* Based on the algorithms originally published by E. Oran Brigham "The Fast Fourier Transform" 1973, in ALGOL60 and FORTRAN
|
||||
*/
|
||||
public class FFT {
|
||||
/**
|
||||
* The Fast Fourier Transform (generic version, with NO optimizations).
|
||||
*
|
||||
* @param inputReal
|
||||
* an array of length n, the real part
|
||||
* @param inputImag
|
||||
* an array of length n, the imaginary part
|
||||
* @param DIRECT
|
||||
* TRUE = direct transform, FALSE = inverse transform
|
||||
* @return a new array of length 2n
|
||||
*/
|
||||
public static double[] fft(final double[] inputReal, double[] inputImag,
|
||||
boolean DIRECT) {
|
||||
// - n is the dimension of the problem
|
||||
// - nu is its logarithm in base e
|
||||
int n = inputReal.length;
|
||||
|
||||
// If n is a power of 2, then ld is an integer (_without_ decimals)
|
||||
double ld = Math.log(n) / Math.log(2.0);
|
||||
|
||||
// Here I check if n is a power of 2. If exist decimals in ld, I quit
|
||||
// from the function returning null.
|
||||
|
||||
// System.out.println(n);
|
||||
// System.out.println(((int) ld) - ld);
|
||||
|
||||
if (((int) ld) - ld != 0) {
|
||||
System.out.println("The number of elements is not a power of 2.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Declaration and initialization of the variables
|
||||
// ld should be an integer, actually, so I don't lose any information in
|
||||
// the cast
|
||||
int nu = (int) ld;
|
||||
int n2 = n / 2;
|
||||
int nu1 = nu - 1;
|
||||
double[] xReal = new double[n];
|
||||
double[] xImag = new double[n];
|
||||
double tReal, tImag, p, arg, c, s;
|
||||
|
||||
// Here I check if I'm going to do the direct transform or the inverse
|
||||
// transform.
|
||||
double constant;
|
||||
if (DIRECT)
|
||||
constant = -2 * Math.PI;
|
||||
else
|
||||
constant = 2 * Math.PI;
|
||||
|
||||
// I don't want to overwrite the input arrays, so here I copy them. This
|
||||
// choice adds \Theta(2n) to the complexity.
|
||||
for (int i = 0; i < n; i++) {
|
||||
xReal[i] = inputReal[i];
|
||||
xImag[i] = inputImag[i];
|
||||
}
|
||||
|
||||
// First phase - calculation
|
||||
int k = 0;
|
||||
for (int l = 1; l <= nu; l++) {
|
||||
while (k < n) {
|
||||
for (int i = 1; i <= n2; i++) {
|
||||
p = bitreverseReference(k >> nu1, nu);
|
||||
// direct FFT or inverse FFT
|
||||
arg = constant * p / n;
|
||||
c = Math.cos(arg);
|
||||
s = Math.sin(arg);
|
||||
tReal = xReal[k + n2] * c + xImag[k + n2] * s;
|
||||
tImag = xImag[k + n2] * c - xReal[k + n2] * s;
|
||||
xReal[k + n2] = xReal[k] - tReal;
|
||||
xImag[k + n2] = xImag[k] - tImag;
|
||||
xReal[k] += tReal;
|
||||
xImag[k] += tImag;
|
||||
k++;
|
||||
}
|
||||
k += n2;
|
||||
}
|
||||
k = 0;
|
||||
nu1--;
|
||||
n2 /= 2;
|
||||
}
|
||||
|
||||
// Second phase - recombination
|
||||
k = 0;
|
||||
int r;
|
||||
while (k < n) {
|
||||
r = bitreverseReference(k, nu);
|
||||
if (r > k) {
|
||||
tReal = xReal[k];
|
||||
tImag = xImag[k];
|
||||
xReal[k] = xReal[r];
|
||||
xImag[k] = xImag[r];
|
||||
xReal[r] = tReal;
|
||||
xImag[r] = tImag;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
// Here I have to mix xReal and xImag to have an array (yes, it should
|
||||
// be possible to do this stuff in the earlier parts of the code, but
|
||||
// it's here to readability).
|
||||
double[] newArray = new double[xReal.length * 2];
|
||||
double radice = 1 / Math.sqrt(n);
|
||||
for (int i = 0; i < newArray.length; i += 2) {
|
||||
int i2 = i / 2;
|
||||
// I used Stephen Wolfram's Mathematica as a reference so I'm going
|
||||
// to normalize the output while I'm copying the elements.
|
||||
newArray[i] = xReal[i2] * radice;
|
||||
newArray[i + 1] = xImag[i2] * radice;
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* The reference bit reverse function.
|
||||
*/
|
||||
private static int bitreverseReference(int j, int nu) {
|
||||
int j2;
|
||||
int j1 = j;
|
||||
int k = 0;
|
||||
for (int i = 1; i <= nu; i++) {
|
||||
j2 = j1 / 2;
|
||||
k = 2 * k + j1 - 2 * j2;
|
||||
j1 = j2;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import org.code.theater.*;
|
||||
import org.code.media.*;
|
||||
|
||||
|
||||
// Utility function to make messing with code.org's libraries easier
|
||||
public class Renderer extends Scene {
|
||||
private Theater Theater;
|
||||
private final Image image;
|
||||
private final double delay = 0.5;
|
||||
|
||||
public final int width;
|
||||
public final int height;
|
||||
|
||||
public Renderer() {
|
||||
Theater = new Theater();
|
||||
width = getWidth();
|
||||
height = getHeight();
|
||||
image = new Image(width, height);
|
||||
}
|
||||
|
||||
public void refreshImage(){
|
||||
this.drawImage(image, 0, 0, width);
|
||||
pause(delay);
|
||||
}
|
||||
|
||||
public void setPixel(int x, int y, Color color){
|
||||
image.setPixel(x, y, color);
|
||||
}
|
||||
|
||||
public void render() {
|
||||
System.out.println("Sending video to client...");
|
||||
Theater.playScenes(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import org.code.theater.*;
|
||||
import org.code.media.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Spectrogram {
|
||||
public static void run(String filepath) {
|
||||
//get raw double array containing .WAV data
|
||||
//code.org has a function to get data from a .wav file, this makes things a lot easier
|
||||
double[] rawData = SoundLoader.read(filepath);
|
||||
|
||||
int length = rawData.length;
|
||||
|
||||
Renderer r = new Renderer();
|
||||
|
||||
//initialize parameters for FFT
|
||||
int WS = 512; //WS = window size
|
||||
int OF = 8; //OF = overlap factor
|
||||
int windowStep = WS/OF;
|
||||
|
||||
//initialize plotData array
|
||||
int nX = (length-WS)/windowStep;
|
||||
int nY = WS/2 + 1;
|
||||
double[][] plotData = new double[nX][nY];
|
||||
|
||||
//apply FFT and find MAX and MIN amplitudes
|
||||
double maxAmp = Double.MIN_VALUE;
|
||||
double minAmp = Double.MAX_VALUE;
|
||||
|
||||
double amp_square;
|
||||
|
||||
double[] inputImag = new double[length];
|
||||
|
||||
System.out.println("Calculating Spectrogram...");
|
||||
|
||||
// Do the FFT Stuff
|
||||
for (int i = 0; i < nX; i++){
|
||||
Arrays.fill(inputImag, 0.0);
|
||||
double[] WS_array = FFT.fft(Arrays.copyOfRange(rawData, i*windowStep, i*windowStep+WS), inputImag, true);
|
||||
for (int j = 0; j < nY; j++){
|
||||
|
||||
// IDK, Stolen code. I think this converts the data from the fft
|
||||
amp_square = (WS_array[2*j]*WS_array[2*j]) + (WS_array[2*j+1]*WS_array[2*j+1]);
|
||||
if (amp_square == 0.0){
|
||||
plotData[i][j] = amp_square;
|
||||
}
|
||||
else{
|
||||
plotData[i][nY-j-1] = 10 * Math.log10(amp_square);
|
||||
}
|
||||
|
||||
//find MAX and MIN amplitude
|
||||
if (plotData[i][j] > maxAmp)
|
||||
maxAmp = plotData[i][j];
|
||||
else if (plotData[i][j] < minAmp)
|
||||
minAmp = plotData[i][j];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Normalization, to show the details in the spectrograph
|
||||
final double diff = maxAmp - minAmp;
|
||||
for (int i = 0; i < nX; i++){
|
||||
for (int j = 0; j < nY; j++){
|
||||
plotData[i][j] = (plotData[i][j]-minAmp)/diff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Video settings
|
||||
//The docs get the sample rate wrong, by a factor of 10, lol.
|
||||
final double SR = 44100;
|
||||
|
||||
// This is around the max framerate that code.org allows
|
||||
final double maxFramerate = 2.05;
|
||||
final int maxFrameCount = 120;
|
||||
|
||||
// Most ways to get the length of the audio require getting the bitrate of the audio. This seems to work.
|
||||
final double audioLength = length/SR;
|
||||
final int frameCount = Math.min((int)Math.round(audioLength*maxFramerate), maxFrameCount);
|
||||
final double delay = Math.max(((audioLength/frameCount)-(double)(1/maxFramerate)), 0);
|
||||
|
||||
|
||||
final int initialXOffset = (int)(r.width/2)*-1;
|
||||
|
||||
System.out.println("Rendering Video...");
|
||||
System.out.println("Audio length: " + Math.round(audioLength) + " seconds");
|
||||
// System.out.println(frameCount);
|
||||
// System.out.println(delay);
|
||||
|
||||
r.playSound(filepath);
|
||||
|
||||
// Loop through all frames
|
||||
for(int f = 0; f<=frameCount; f++){
|
||||
|
||||
int xOffset = initialXOffset+(int)(f*((nX+r.width)/frameCount));
|
||||
|
||||
// Loop through pixels in image
|
||||
for(int y = 0; y<r.height; y++){
|
||||
int specY = (int)((double)(((double)y/(double)r.height)*(double)nY));
|
||||
for(int x = 0; x<r.width; x++){
|
||||
|
||||
|
||||
if(x+xOffset<0 || x+xOffset+1 > nX){
|
||||
// If pixel is outside image, set to black.
|
||||
r.setPixel(x, y, Color.BLACK);
|
||||
}else{
|
||||
// Greyscale image
|
||||
final int L = (int)(plotData[x+xOffset][specY]*255);
|
||||
r.setPixel(x, y, new Color(L, L, L));
|
||||
}
|
||||
}
|
||||
}
|
||||
r.refreshImage();
|
||||
// r.pause(delay);
|
||||
}
|
||||
r.render();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
public class main {
|
||||
public static void main(String[] args) {
|
||||
|
||||
// This is a spectrogram, that I made in-class.
|
||||
// Code.org has a max framerate around 2 fps, so the visual isn't the best.
|
||||
// Also, the audio doesn't sync up with the spectrograph the visual as well as it could.
|
||||
|
||||
///// My personal narrative: ////
|
||||
// I have seen a spectrograph of a 56kb dialup connection before,
|
||||
// So... Here is a spectrograph of a 56kb dialup connection.
|
||||
/////////////////////
|
||||
|
||||
// Also, here is me using a method in the String class:
|
||||
" ".trim();
|
||||
|
||||
|
||||
Spectrogram.run("dialup.wav");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user