问题
I'm currently trying to adjust the volume of a Player component provided by tone.js.
I initiated a new Player and utilized useRef to save a reference of the object in current
.
the Player contains keys like url, loop, volume etc.
Outside of the useEffect I have a few eventhandlers which control the play and stop methods as well as a loop button that toggles the state of loop inside useEffect. They all work.
The problem I am facing is that whenever I want to adjust the volume, useEffect is being called, which leads to a rerender.
I basically want to be able to change the key parameter(value) for volume inside the created Player object. When moving the slider react rerenders but the volume doesn't change.
I just put this out her hoping someone has done this before.
My previous question has been left unanswered and eventually I solved it myself. It seems there are just not many people working with the web audio API I assume.
I believe though in this case I just need someone skilled in react.
There's probably an easy workaround but I just lack the knowledge.
I started learning react a couple of weeks ago.
Here is my code
import React, { useState, useEffect, useRef} from "react";
import "./DrumMachine.css";
import Button from "./Button";
import { Player } from "tone";
import Sound from "./sound.wav";
import Poti from './Poti';
export default function DrumMachine() {
console.log("body")
const [value, setValue ] = useState(0);
const [loop, setLoop] = useState(false);
const playerRef = useRef(null);
playerRef.current = new Player(Sound).toDestination();
useEffect(() => {
console.log("useEffect")
console.log(playerRef.current.volume)
playerRef.current.loop = loop;
playerRef.current.volume = value;
},[loop, value]);
const play = () => {
playerRef.current.start();
};
const stop = () => {
playerRef.current.stop();
}
const volume = (event) => {
console.log(value)
setValue(event.target.value)
}
const loopToggle = () => {
console.log('loop')
setLoop(!loop);
}
const doSt = () => {
console.log('loop')
}
return (
<div className="machine">
<Button onClick={play}>Play</Button>
<Button onClick={stop}>Stop</Button>
<Button onClick={loopToggle}>
{loop ? 'Loop active' : 'Loop disabled'}
</Button>
<Button onClick={doSt}>
{'Do something'}
</Button>
<Button></Button>
<Button></Button>
<Button></Button>
<Button></Button>
<span className='vol-box'>
<label htmlFor='vol' >Volume</label>
<Poti
onChange={volume}
value={value} id='volM'
name='vol'
min='0'
max='1'
step='0.1'
></Poti>
</span>
</div>
);
}
The console.logs are obviously just for testing. I will read the docs meanwhile hoping to find solution.
Thanks for any help.
回答1:
Working fork.
https://codesandbox.io/s/web-audio-api-playground-v2-forked-y1dc6?file=/src/components/DrumMachine.js
Okay, so a couple of things.
The volume min and max were too small to have a big impact. I set it -20 and 20 as the decibels and that was enough to notice a difference.
I declared your player outside of the component and passed it into the component as the ref initial value. This isn't the only way to do this, but it was the easiest to get your example working. This makes the player a singleton.
If you need to have multiple DrumMachine
components. I recommend creating a player in its own component and passing functions to update it down to a controls component. This will allow your controls component to get rerendered on different button presses, but your player will not and you can have as many of them as needed.
来源:https://stackoverflow.com/questions/63595019/how-to-enable-volume-slider-of-player-tone-js-inside-of-useeffect-hookreact