SciSnap2 extension update (FFT), thanks, Eckart!

snap8
Jens Mönig 2022-03-03 09:29:57 +01:00
rodzic e9a0e6f402
commit ce63da9cef
4 zmienionych plików z 220 dodań i 20 usunięć

Wyświetl plik

@ -5,12 +5,16 @@
* **New Features:**
* variadic commutative infix reporters
* **Notable Changes:**
* removed now redundant variadic reporters from the variadic reporters library
* SciSnap2 extension update (FFT), thanks, Eckart!
* removed now redundant variadic reporters from the variadic reporters library
* **Notable Fixes:**
* **Documentation Updates:**
* **Translation Updates:**
* German
### 2022-03-03
* SciSnap2 extension update (FFT), thanks, Eckart!
### 2022-03-02
* gui: never close a dev-warning

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -3253,28 +3253,27 @@ SnapExtensions.primitives.set(
SnapExtensions.primitives.set(
'SciS_drawGraph(amatrix,vlist,cAttributes,vAttributes,lAttributes,oldCostume)',
function (amatrix, vlist, cAttributes, vAttributes, lAttributes,oldCostume) {
function (amatrix, vlist, cAttributes, vAttributes, lAttributes, oldCostume) {
var costume = new Costume(), ctx = costume.contents.getContext('2d'), c, row, anz,
w = Number(cAttributes.at(1)), h = Number(cAttributes.at(2)), v1, v2, x1, y1, x2, y2, n = amatrix.length(), label, textheight,
weight, directed, marked, showWeights, xp, yp, alpha, l, dx, dy, dl, size, minsize = Number(vAttributes.at(2)), growing = vAttributes.at(3);
//create new costume or take old one
//create new costume or take old one
if(oldCostume==="null"){
costume.contents.width = w;
costume.contents.height = h;
ctx.beginPath();
ctx.fillStyle = new Color(cAttributes.at(3), cAttributes.at(4), cAttributes.at(5)).toString();
ctx.strokeStyle = new Color(0, 0, 0).toString();
ctx.fillRect(0, 0, w, h);
ctx.strokeRect(0, 0, w, h);
ctx.closePath();
ctx.fill();
ctx.stroke();
costume.rotationCenter = new Point(w / 2, h / 2);
}
else{
costume = oldCostume;
ctx = costume.contents.getContext('2d');
//create new costume or take old one
if (oldCostume === "null") {
costume.contents.width = w;
costume.contents.height = h;
ctx.beginPath();
ctx.fillStyle = new Color(cAttributes.at(3), cAttributes.at(4), cAttributes.at(5)).toString();
ctx.strokeStyle = new Color(0, 0, 0).toString();
ctx.fillRect(0, 0, w, h);
ctx.strokeRect(0, 0, w, h);
ctx.closePath();
ctx.fill();
ctx.stroke();
costume.rotationCenter = new Point(w / 2, h / 2);
} else {
costume = oldCostume;
ctx = costume.contents.getContext('2d');
}
//count edges per vertex an set size per vertex
@ -4317,6 +4316,150 @@ SnapExtensions.primitives.set(
}
);
SnapExtensions.primitives.set(
'SciS_FFTops(data,freq,choice)',
function (data, freq, choice) {
function newComplex(re, im) {
return [re, im];
}
function addComplex(c1, c2) {
return newComplex(c1[0] + c2[0], c1[1] + c2[1]);
}
function subComplex(c1, c2) {
return newComplex(c1[0] - c2[0], c1[1] - c2[1]);
}
function mulComplex(c1, c2) {
return newComplex(c1[0] * c2[0] - c1[1] * c2[1], c1[0] * c2[1] + c1[1] * c2[0]);
}
function absComplex(c) {
return Math.sqrt(c[0] * c[0] + c[1] * c[1]);
}
function complexToPolar(c) {
return newComplex(absComplex(c), Math.atan(c[1] / c[0]));
}
function polarToComplex(c) {
return newComplex(c[0] * Math.cos(c[1]), c[0] * Math.sin(c[1]));
}
function FFT(d) {
var n = d.length, nDIV2 = n / 2, even = [], odd = [], result = [], evenPart, oddPart;
if (n <= 1)
return d;
for (var i = 0; i < n; i++) {
result.push(0);
}
for (var i = 0; i < nDIV2; i++) {
even.push(d[2 * i]);
odd.push(d[2 * i + 1]);
}
evenPart = FFT(even);
oddPart = FFT(odd);
f = -2 * Math.PI / n;
for (var k = 0; k < nDIV2; k++) {
g = mulComplex(newComplex(Math.cos(f * k), Math.sin(f * k)), oddPart[k]);
result[k] = addComplex(evenPart[k], g);
result[k + nDIV2] = subComplex(evenPart[k], g);
}
return result;
}
var rData = [], cData = [], N = 0, result, maxLength = data.length(), n;
if (choice === "frequency_spectrum") {
rData = data.asArray();
//complete data up to length 2^N
while (Math.pow(2, N) < rData.length) {
N++;
}
while (rData.length < Math.pow(2, N)) {
rData.push(0);
}
//convert to complex numbers
for (var i = 0; i < rData.length; i++) {
cData.push([Number(rData[i]), Number(0)]);
}
//calculate FFT
result = FFT(cData);
//shorten to length of original data
while (result.length > maxLength) {
result.pop();
}
//calculate normalized FFT data
n = cData.length;
for (var i = 0; i < result.length; i++) {
result[i] = [1.0 * i / n * freq, 2 * absComplex(result[i]) / maxLength];
}
result[0][1] = result[0][1] / 2;
//convert to List
for (var i = 0; i < result.length; i++) {
result[i] = new List(result[i]);
}
return new List(result);
}
if (choice === 'complex_FFTdata') {
rData = data.asArray();
//complete data up to length 2^N
while (Math.pow(2, N) < rData.length) {
N++;
}
while (rData.length < Math.pow(2, N)) {
rData.push(0);
}
//convert to complex numbers
for (var i = 0; i < rData.length; i++) {
cData.push([Number(rData[i]), Number(0)]);
}
//calculate FFT
result = FFT(cData);
//use SciSnap! format for complex numbers
for (var i = 0; i < result.length; i++) {
result[i] = ["complexNumberCartesianStyle", result[i][0], result[i][1]];
}
//shorten to length of original data
while (result.length > maxLength) {
result.pop();
}
//convert to List
for (var i = 0; i < result.length; i++) {
result[i] = new List(result[i]);
}
return new List(result);
}
if (choice === 'iFFT_of_FFTdata') {
//convert data to conjugate complex array
for (var i = 1; i <= data.length(); i++) {
rData.push([data.at(i).at(1), -data.at(i).at(2)]);
}
//complete data up to length 2^N
while (Math.pow(2, N) < rData.length) {
N++;
}
while (rData.length < Math.pow(2, N)) {
rData.push([0, 0]);
}
//calculate FFT
result = FFT(rData);
n = result.length;
for (var i = 0; i < n; i++) {
result[i] = result[i][0] / n;
}
//shorten to length of original data
while (result.length > maxLength) {
result.pop();
}
//convert to List
return new List(result);
}
return "ERROR: incorrect selection!";
}
);
@ -4338,4 +4481,15 @@ SnapExtensions.primitives.set(

Wyświetl plik

@ -3413,6 +3413,48 @@ Process.prototype.decodeSound = function (sound, callback) {
this.pushContext();
};
Process.prototype.decodeSpectrum = function (sound, callback) {
// private - callback is optional and invoked with sound as argument
var base64, binaryString, len, bytes, i, arrayBuffer, audioCtx, analyzer;
if (sound.audioBuffer) {
return (callback || nop)(sound);
}
if (!sound.isDecoding) {
base64 = sound.audio.src.split(',')[1];
binaryString = window.atob(base64);
len = binaryString.length;
bytes = new Uint8Array(len);
for (i = 0; i < len; i += 1) {
bytes[i] = binaryString.charCodeAt(i);
}
arrayBuffer = bytes.buffer;
audioCtx = Note.prototype.getAudioContext();
sound.isDecoding = true;
analyser = audioCtx.createAnalyser(); // +++
analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(dataArray);
audioCtx.decodeAudioData(
arrayBuffer,
buffer => {
sound.audioBuffer = buffer;
sound.isDecoding = false;
},
err => {
sound.isDecoding = false;
this.handleError(err);
}
);
}
this.pushContext('doYield');
this.pushContext();
};
Process.prototype.encodeSound = function (samples, rate) {
// private
var rcvr = this.blockReceiver(),