Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React, { useRef, useEffect, useMemo, useState } from 'react';
import MockAudioContext from '@site/src/audio/MockAudioContext';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2.js';
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';

// @ts-ignore
import labelImage from '/static/img/logo.png';
// @ts-ignore
import swmLogo from '/static/img/swm-text.png';
import styles from './styles.module.css';
import { VINYL_CONSTANTS as C } from './consts';
import styles from './styles.module.css';
import swmLogo from '/static/img/swm-text.png';

interface SceneRefs {
recordGroup: THREE.Group;
Expand All @@ -31,7 +32,7 @@ export default function VinylPlayer(): React.ReactElement {
const mountRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);

// Animation state ref
// Animation state ref
const animationStateRef = useRef<AnimationState>({
isSliderPressed: false,
targetCoverY: 0,
Expand All @@ -40,11 +41,12 @@ export default function VinylPlayer(): React.ReactElement {
fallVelocity: 0,
});

// Scene object refs
// Scene object refs
const sceneRefsRef = useRef<SceneRefs | null>(null);

const { context, filter, gain } = useMemo(() => {
const ctx = new AudioContext();
const ctx = typeof window !== 'undefined' ? new AudioContext() : MockAudioContext;

const filterNode = ctx.createBiquadFilter();
filterNode.type = 'lowpass';
const gainNode = ctx.createGain();
Expand Down Expand Up @@ -75,7 +77,7 @@ export default function VinylPlayer(): React.ReactElement {
return () => {
context?.close();
};
}, [context]);
}, [context]);

const playSound = () => {
if (!context || !audioBuffer || bufferSourceRef.current) {
Expand Down Expand Up @@ -110,7 +112,7 @@ export default function VinylPlayer(): React.ReactElement {
const state = animationStateRef.current;
state.coverHeight = v;
state.targetCoverY = (v / C.SLIDER_MAX) * C.SLIDER_MAX;

if (!bufferSourceRef.current) {
playSound();
}
Expand All @@ -125,13 +127,13 @@ export default function VinylPlayer(): React.ReactElement {
1000
);
camera.position.set(8, 10, 12);

const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.target.set(0, 2, 0);
Expand All @@ -143,7 +145,7 @@ export default function VinylPlayer(): React.ReactElement {
const initializeLighting = (scene: THREE.Scene) => {
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
scene.add(ambientLight);

const dirLight = new THREE.DirectionalLight(0xffffff, 1.2);
dirLight.position.set(10, 15, 5);
dirLight.castShadow = true;
Expand Down Expand Up @@ -324,7 +326,7 @@ export default function VinylPlayer(): React.ReactElement {
// SWM logo on box
const logoTexture = new THREE.TextureLoader().load(swmLogo);
logoTexture.colorSpace = THREE.SRGBColorSpace;

const logoMaterialFront = new THREE.MeshBasicMaterial({
map: logoTexture,
transparent: true,
Expand All @@ -335,15 +337,15 @@ export default function VinylPlayer(): React.ReactElement {
transparent: true,
side: THREE.FrontSide,
});

const logoWidth = coverWidth * 0.7;
const logoHeight = coverBoxHeight * 0.5;
const logoPlaneGeometry = new THREE.PlaneGeometry(logoWidth, logoHeight);

const logoFront = new THREE.Mesh(logoPlaneGeometry, logoMaterialFront);
logoFront.position.set(0, coverBoxHeight / 2, coverDepth / 2 + 0.01);
coverGroup.add(logoFront);

const logoBack = new THREE.Mesh(logoPlaneGeometry, logoMaterialBack);
logoBack.position.set(0, coverBoxHeight / 2, -coverDepth / 2 - 0.01);
logoBack.rotation.y = Math.PI;
Expand Down Expand Up @@ -450,7 +452,7 @@ export default function VinylPlayer(): React.ReactElement {
}

const MAX_STRING_LENGTH = C.ANCHOR_Y - C.COVER_TOP_Y;

const isOnGround = newCoverY < 0.01;
if (isOnGround && !state.wasOnGround) {
if (bufferSourceRef.current) {
Expand All @@ -477,13 +479,13 @@ export default function VinylPlayer(): React.ReactElement {

const { scene, camera, renderer, controls } = initializeScene(currentMount);
initializeLighting(scene);

const materials = createMaterials();
const recordGroup = createVinylPlayer(scene, materials);
const { coverGroup, string, lineMaterial } = createCoverBox(scene, renderer, materials);

sceneRefsRef.current = { recordGroup, coverGroup, string };

startAnimationLoop(renderer, scene, camera, controls);

const handleResize = () => {
Expand Down Expand Up @@ -546,4 +548,4 @@ export default function VinylPlayer(): React.ReactElement {
</div>
</div>
);
}
}
22 changes: 11 additions & 11 deletions packages/react-native-audio-api/src/core/BaseAudioContext.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { InvalidAccessError, NotSupportedError } from '../errors';
import { IBaseAudioContext } from '../interfaces';
import {
AudioBufferBaseSourceNodeOptions,
ContextState,
PeriodicWaveConstraints,
AudioBufferBaseSourceNodeOptions,
} from '../types';
import { isWorkletsAvailable, workletsModule } from '../utils';
import AnalyserNode from './AnalyserNode';
import AudioBuffer from './AudioBuffer';
import AudioBufferQueueSourceNode from './AudioBufferQueueSourceNode';
import AudioBufferSourceNode from './AudioBufferSourceNode';
import AudioDestinationNode from './AudioDestinationNode';
import OscillatorNode from './OscillatorNode';
import GainNode from './GainNode';
import StereoPannerNode from './StereoPannerNode';
import BiquadFilterNode from './BiquadFilterNode';
import AudioBufferSourceNode from './AudioBufferSourceNode';
import AudioBuffer from './AudioBuffer';
import GainNode from './GainNode';
import OscillatorNode from './OscillatorNode';
import PeriodicWave from './PeriodicWave';
import AnalyserNode from './AnalyserNode';
import AudioBufferQueueSourceNode from './AudioBufferQueueSourceNode';
import StreamerNode from './StreamerNode';
import { InvalidAccessError, NotSupportedError } from '../errors';
import RecorderAdapterNode from './RecorderAdapterNode';
import StereoPannerNode from './StereoPannerNode';
import StreamerNode from './StreamerNode';
import WorkletNode from './WorkletNode';
import { isWorkletsAvailable, workletsModule } from '../utils';

export default class BaseAudioContext {
readonly destination: AudioDestinationNode;
Expand Down
19 changes: 12 additions & 7 deletions packages/react-native-audio-api/src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { AudioEventCallback, AudioEventName } from './events/types';
import {
WindowType,
ContextState,
OscillatorType,
BiquadFilterType,
ChannelCountMode,
ChannelInterpretation,
ContextState,
OscillatorType,
WindowType,
} from './types';
import { AudioEventName, AudioEventCallback } from './events/types';

export type ShareableWorkletCallback = (
audioBuffers: Array<ArrayBuffer>,
channelCount: number
) => void;

export interface IBaseAudioContext {
readonly destination: IAudioDestinationNode;
Expand All @@ -15,11 +20,11 @@ export interface IBaseAudioContext {
readonly currentTime: number;

createRecorderAdapter(): IRecorderAdapterNode;
createWorkletNode: (
shareableWorklet: any,
createWorkletNode(
shareableWorklet: ShareableWorkletCallback,
bufferLength: number,
inputChannelCount: number
) => IWorkletNode;
): IWorkletNode;
createOscillator(): IOscillatorNode;
createGain(): IGainNode;
createStereoPanner(): IStereoPannerNode;
Expand Down
10 changes: 9 additions & 1 deletion packages/react-native-audio-api/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import type { ShareableWorkletCallback } from '../interfaces';

interface SimplifiedWorkletModule {
makeShareableCloneRecursive: (
workletCallback: ShareableWorkletCallback
) => ShareableWorkletCallback;
}

export function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}

export let isWorkletsAvailable = false;
export let workletsModule: any = null;
export let workletsModule: SimplifiedWorkletModule;

try {
workletsModule = require('react-native-worklets');
Expand Down