Skip to content

Commit

Permalink
fix a bug
Browse files Browse the repository at this point in the history
  • Loading branch information
TheAlan404 committed Oct 30, 2024
1 parent 019f28e commit 4df0e0f
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 5 deletions.
6 changes: 5 additions & 1 deletion src/api/platforms/invid/invidious.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ export class InvidiousAPIProvider implements APIProvider {
signal: opts?.signal,
});

return await res.json() as T;
let json = await res.json();

if(json["error"]) throw new Error(json["error"]);

return json as T;
}

convertVideoInfo = (d: InvidiousVideo): Renderer => {
Expand Down
6 changes: 3 additions & 3 deletions src/api/provider/APIController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ export const APIControllerProvider = ({ children }: React.PropsWithChildren) =>
const refreshAvailableInstances = async () => {
setIsRefreshing(true);
setAvailableInstances([
...await fetchLightTubePublicInstances(),
...await fetchInvidiousPublicInstances(),
...await fetchPoketubePublicInstances(),
...(await fetchLightTubePublicInstances().catch(()=>[]) || []),
...(await fetchInvidiousPublicInstances().catch(()=>[]) || []),
...(await fetchPoketubePublicInstances().catch(()=>[]) || []),
...CUSTOM_INSTANCES,
]);
setIsRefreshing(false);
Expand Down
156 changes: 156 additions & 0 deletions src/components/extra/BassDetector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// reference: https://github.com/stasilo/BeatDetector/blob/master/beatdetector.js

export type BassDetector = ReturnType<typeof createBassDetector>;

export const createBassDetector = (analyser: AnalyserNode) => {
const MAX_COLLECT_SIZE = 43 * (analyser.fftSize / 2);
const COLLECT_SIZE = 1;
const sens = 0.05;
const bufferLength = analyser.frequencyBinCount;

let historyBuffer = [];
let instantEnergy = 0;
let prevTime = 0;
let bpmTable = [];

const isOnBeat = () => {
let localAverageEnergy = 0;
let instantCounter = 0;
let isBeat = false;

let bpmArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(bpmArray);

// fill history buffer
for (var i = 0; i < bpmArray.length - 1; i++, ++instantCounter) {
historyBuffer.push(bpmArray[i]); //add sample to historyBuffer
instantEnergy += bpmArray[i];
}

//done collecting MAX_COLLECT_SIZE history samples
//have COLLECT_SIZE nr of samples as instant energy value
if (instantCounter > COLLECT_SIZE - 1 &&
historyBuffer.length > MAX_COLLECT_SIZE - 1) {
instantEnergy = instantEnergy / (COLLECT_SIZE * (analyser.fftSize / 2));

var average = 0;
for (var i = 0; i < historyBuffer.length - 1; i++) {
average += historyBuffer[i];
}

localAverageEnergy = average / historyBuffer.length;

var timeDiff = analyser.context.currentTime - prevTime;

// timeDiff > 2 is out of normal song bpm range, but if it is a multiple of range [0.3, 1.5]
// we probably have missed a beat before but now have a match in the bpm table.
if (timeDiff > 2 && bpmTable.length > 0) {
//check if we have a multiple of range in bpm table
for (var j = 0; j < bpmTable.length - 1; j++) {
// mutiply by 10 to avoid float rounding errors
var timeDiffInteger = Math.round((timeDiff / bpmTable[j]['time']) * 1000);

// timeDiffInteger should now be a multiple of a number in range [3, 15]
// if we have a match

if (timeDiffInteger % (Math.round(bpmTable[j]['time']) * 1000) == 0) {
timeDiff = Number(bpmTable[j]['time']);
}
}
}


//still?
if (timeDiff > 3) {
prevTime = timeDiff = 0;
}

// MAIN BPM HIT CHECK //

// CHECK IF WE HAVE A BEAT BETWEEN 200 AND 40 BPM (every 0.29 to 2s), or else ignore it.
// Also check if we have _any_ found prev beats
if (analyser.context.currentTime > 0.29 && instantEnergy > localAverageEnergy &&
(instantEnergy > (sens * localAverageEnergy)) &&
((timeDiff < 2.0 && timeDiff > 0.29) || prevTime == 0)) {

isBeat = true;

prevTime = analyser.context.currentTime;

let bpm: any = {
time: Number(timeDiff.toFixed(3)),
counter: 1,
};


for (var j = 0; j < bpmTable.length; j++) {
//FOUND ANOTHER MATCH FOR ALREADY GUESSED BEAT

if (bpmTable[j]['time'] == bpm['time']) {
bpmTable[j]['counter']++;
bpm = 0;

if (bpmTable[j]['counter'] > 3 && j < 2) {
console.log("WE HAVE A BEAT MATCH IN TABLE!!!!!!!!!!");
}

break;
}
}

if (bpm || bpmTable.length == 0) {
bpmTable.push(bpm);
}

//sort and draw 10 most current bpm-guesses
bpmTable.sort((a, b) =>
b.counter - a.counter
);
}

var temp = historyBuffer.slice(0); //get copy of buffer

historyBuffer = []; //clear buffer

// make room in array by deleting the last COLLECT_SIZE samples.
historyBuffer = temp.slice(COLLECT_SIZE * (analyser.fftSize / 2), temp.length);

instantCounter = 0;
instantEnergy = 0;

localAverageEnergy = 0;

return isBeat;
}
};

const getBPMGuess = () => {
let allGuesses = 0;
let guesses = 0;
let counter = 0;

if (bpmTable.length <= 2) {
return null;
}

for (var i = 0; i < bpmTable.length; i++) {
allGuesses += Number(bpmTable[i].time);

if (bpmTable[i]['counter'] > 1) {
guesses += Number(bpmTable[i].time);

counter++;
}
}

return {
conservative: Math.round(60 / (guesses / counter)),
all: Math.round(60 / (allGuesses / bpmTable.length))
};
}

return {
isOnBeat,
getBPMGuess,
};
};
48 changes: 48 additions & 0 deletions src/components/extra/Particles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export interface Coord {
x: number;
y: number;
}

export type Particle = Coord & {
d: number[];
};

export interface ParticleHandler {
generate: (ctx: CanvasRenderingContext2D) => Particle;
move: (ctx: CanvasRenderingContext2D, particle: Particle, dt: number) => Particle | null;
draw: (ctx: CanvasRenderingContext2D, particle: Particle) => void;
};

const identity = <T>(x: T) => x;

export const store: Record<string, ParticleHandler> = {
bass: {
generate: (ctx) => ({
d: [],
x: 0,
y: ctx.canvas.height,
}),

draw(ctx, { y }) {
//ctx.filter = "invert(1)";
ctx.beginPath();
ctx.strokeStyle = "white";
ctx.globalAlpha = (y / ctx.canvas.height) * 0.5;
ctx.moveTo(0, y);
ctx.lineTo(ctx.canvas.width, y);
ctx.stroke();
ctx.globalAlpha = 1;
//ctx.filter = "";
},

move: (ctx, {
d,
x,
y,
}, dt) => (y < 0) ? null : ({
d,
x,
y: y - (1 * dt),
}),
},
} as const;
28 changes: 28 additions & 0 deletions src/components/extra/Spectrum.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Box, BoxProps } from "@mantine/core";
import { useCanvas } from "../../utils/useCanvas";
import { useContext, useEffect, useMemo, useRef } from "react";
import { VideoPlayerContext } from "../../api/player/VideoPlayerContext";
import { BassDetector, createBassDetector } from "./BassDetector";
import { Particle, store } from "./Particles";

const RAINBOW = [ "#ef5350", "#f48fb1", "#7e57c2", "#2196f3", "#26c6da", "#43a047", "#eeff41", "#f9a825", "#ff5722" ];

Expand All @@ -12,6 +14,8 @@ export const Spectrum = (props: BoxProps) => {

const audioContextRef = useRef<AudioContext>();
const analyserRef = useRef<AnalyserNode>();
const bassDetectorRef = useRef<BassDetector>();
const particleStore = useRef<(Particle & { t: keyof typeof store })[]>([]);

const spacing = "linear" as "linear" | "logarithmic";

Expand All @@ -32,6 +36,7 @@ export const Spectrum = (props: BoxProps) => {
source.connect(audioContextRef.current.destination);

analyserRef.current = analyser;
bassDetectorRef.current = createBassDetector(analyserRef.current);
})();
}, [videoElement]);

Expand All @@ -41,8 +46,16 @@ export const Spectrum = (props: BoxProps) => {
let gradientOffset = 0;
const historyLength = 1000;


const ref = useCanvas((ctx, dt) => {
if(!analyserRef.current) return;

const spawn = (t: keyof typeof store) => {
particleStore.current.push({
t,
...store[t].generate(ctx),
});
};

analyserRef.current.getByteFrequencyData(dataArray);

Expand Down Expand Up @@ -99,6 +112,21 @@ export const Spectrum = (props: BoxProps) => {

ctx.lineTo(ctx.canvas.width, ctx.canvas.height);
ctx.stroke();

if(bassDetectorRef.current.isOnBeat()) {
spawn("bass");
};

ctx.fillText(`${bassDetectorRef.current.getBPMGuess()?.conservative} BPM
${particleStore.current.length} particles`, 20, ctx.canvas.height);

for(let p of particleStore.current) {
store[p.t].draw(ctx, p);
}

particleStore.current = particleStore.current
.map(p => ({ ...store[p.t].move(ctx, p, dt), t: p.t }))
.filter(x => x);
}, [analyserRef.current]);

return (
Expand Down
2 changes: 1 addition & 1 deletion src/site/layouts/LayoutDesktopVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const LayoutInner = ({
}}>
<TabsRenderer panelHeight="calc(100vh - var(--app-shell-header-height) - calc(var(--app-shell-padding) * 2) - 3em)" />
</Box>
{false && <Spectrum
{true && <Spectrum
style={{
position: "absolute",
width: "100vw",
Expand Down

0 comments on commit 4df0e0f

Please sign in to comment.