Rework website with bulma instead of bootstrap #6

Manually merged
tlater merged 43 commits from tlater/bulma2 into master 2022-08-14 00:26:49 +01:00
4 changed files with 64 additions and 41 deletions
Showing only changes of commit 86b6e1a246 - Show all commits

View file

@ -2,15 +2,11 @@ import React from "react";
import Controls from "../controls/Controls";
import Visualizer from "../visualizer/Visualizer";
import { musicPlayer } from "./musicPlayerSlice";
function MusicPlayer() {
return (
<div className="is-flex-grow-1 is-flex is-flex-direction-column">
<Visualizer
audioContext={musicPlayer.audioContext}
audioNode={musicPlayer.audioNode}
/>
<Visualizer />
<div className="is-flex-grow-0">
<Controls />
</div>

View file

@ -30,27 +30,18 @@ enum PlayState {
//*********************
class MusicPlayer {
private context: AudioContext;
private context?: AudioContext;
private source: HTMLAudioElement;
private sourceNode: MediaElementAudioSourceNode;
private volume: GainNode;
private sourceNode?: MediaElementAudioSourceNode;
private volume?: GainNode;
private analyser?: AnalyserNode;
constructor() {
this.context = new AudioContext();
this.source = new Audio();
this.sourceNode = this.context.createMediaElementSource(this.source);
this.volume = this.context.createGain();
this.sourceNode.connect(this.volume);
this.volume.connect(this.context.destination);
}
get audioContext() {
return this.context;
}
get audioNode() {
return this.sourceNode;
get audioAnalyser() {
return this.analyser;
}
set src(source: string) {
@ -61,6 +52,22 @@ class MusicPlayer {
_: null,
{ getState }: { getState: () => RootState }
): Promise<PlayState> => {
if (this.context === undefined) {
this.context = new AudioContext();
this.sourceNode = this.context.createMediaElementSource(
this.source
);
this.volume = this.context.createGain();
this.analyser = this.context.createAnalyser();
this.analyser.fftSize = 2048;
this.analyser.smoothingTimeConstant = 0.8;
this.sourceNode.connect(this.analyser);
this.sourceNode.connect(this.volume);
this.volume.connect(this.context.destination);
}
const playing = getState().musicPlayer.playing;
switch (playing) {

View file

@ -33,16 +33,10 @@ class Renderer {
};
constructor(
context: AudioContext,
node: AudioNode,
analyser: AnalyserNode,
canvas: HTMLCanvasElement,
overlay: HTMLSpanElement
) {
const analyser = context.createAnalyser();
analyser.fftSize = 2048;
analyser.smoothingTimeConstant = 0.8;
node.connect(analyser);
this.canvas = canvas;
this.overlay = overlay;
this.analyser = analyser;

View file

@ -2,17 +2,41 @@ import React, { useCallback, useState } from "react";
import { Renderer, RendererError } from "./Renderer";
import { ShaderError } from "./Shader";
function Visualizer({
audioContext,
audioNode,
}: {
audioContext: AudioContext;
audioNode: AudioNode;
}) {
import { useAppSelector } from "../../hooks";
import { PlayState, musicPlayer } from "../musicplayer/musicPlayerSlice";
function Visualizer() {
const playing = useAppSelector((state) => state.musicPlayer.playing);
const rendererState = useState<Renderer | null>(null);
let renderer = rendererState[0];
const setRenderer = rendererState[1];
const [renderError, setRenderError] = useState<JSX.Element | null>(null);
const visualizer = useCallback(
(visualizer: HTMLDivElement | null) => {
// TODO(tlater): Clean up state management. This is all
// but trivial; there's seemingly no good place to keep
// these big api objects (WebGLRenderingcontext or
// AudioContext).
//
// It's tricky, too, because obviously react expects to be
// in control of the DOM, and be allowed to delete our
// canvas and create a new one.
//
// For the moment, this works, but it's a definite hack.
if (renderer) {
return;
}
// Until we start playing music, there is nothing to render.
if (playing !== PlayState.Playing) {
return;
}
if (musicPlayer.audioAnalyser === undefined) {
throw new Error("MusicPlayer analyser was not set up on time");
}
// If we're rendering an error message, we won't be
// setting up the visualizer.
//
@ -35,12 +59,14 @@ function Visualizer({
);
}
const renderer = new Renderer(
audioContext,
audioNode,
if (renderer === null) {
renderer = new Renderer(
musicPlayer.audioAnalyser,
canvas,
overlay
);
setRenderer(renderer);
}
try {
renderer.initializeScene();
@ -92,7 +118,7 @@ function Visualizer({
}
}
},
[audioContext, audioNode]
[playing, renderer, musicPlayer.audioAnalyser]
);
if (renderError === null) {