- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我一直在尝试构建类似于 official example 的步进音序器在 Tone.js
文档中。然而,我没有使用 Players
方法播放 MP3 文件,而是想实现 PolySynth
来探索各种声音,例如 Jon Oliver's example。 .
在查看了一些使用不同方法的演示之后——比如 this drum sequencer (使用带有 Players
的 MP3),this step sequencer (使用多个单声道 Synth
)——我设法构建了一个基本的功能音序器。 My working demo in CodeSandbox here .
虽然它按预期工作——即打开/关闭时播放单个音符,按定义的速度循环播放序列,切换播放/暂停模式等——复音合成器的声音在循环几次后开始有点失真,很快就完全静音 .
即使在音频消失后,Sequence
调用似乎仍在继续并且所有其他功能都按预期工作 - 但它变得明显卡顿和滞后。
我怀疑这是我处理 Sequence
调用并无意中使内存过载的方式,尽管我没有看到任何明显的控制台错误。
Transport.scheduleRepeat
:(类似于此 Medium article 导致相同的滞后、静音行为async
调用来处理 Sequence
播放切换:(类似于 this CodeSandbox example 导致相同的延迟、静音行为Array.forEach
或 for
循环手动循环序列:没有 Tone 时无法保持时间同步
时序控制ref
跟踪 Sequence
,同时连续处理先前的节拍:(在 this CodeSandbox example 上修改导致相同的滞后,静音行为。这是当前工作代码中的内容。它是用 React
和 TypeScript
构建的。主要的 App 部分如下所示。 You can view the full working code at this CodeSandbox .
我是 Tone.js
的完全初学者,但相当精通 React
。我们将不胜感激任何指导和见解。
我知道应用程序的数据处理、DOM 处理和其他方面也很笨拙,希望它们不会成为导致此问题的原因。
function App() {
type Loop = {
dispose: any;
};
const [isPlaying, setIsPlaying] = React.useState(false);
const [currentBeat, setCurrentBeat] = React.useState(0);
const [currentBPM, setCurrentBPM] = React.useState(120);
const [noteMatrix, setNoteMatrix] = React.useState(defaultNoteMatrix);
const [currentScale, setCurrentScale] = React.useState(SCALES.major);
const synth = new Tone.PolySynth().toDestination();
const loop = React.useRef<Loop | null>(null);
const noteMap = noteMatrix.map((beat) =>
beat
.map((note, i) => {
return { note: currentScale[i], on: note };
})
.filter((note) => note.on)
.map((item) => item.note)
);
const playSingleNote = (note = "") => synth.triggerAttackRelease(note, "16n");
const tick = (e: React.MouseEvent<HTMLButtonElement>): void => {
const currentBeat = parseFloat(e.currentTarget.dataset.beat!);
const selectedNote = parseFloat(e.currentTarget.dataset.index!);
const isOn = e.currentTarget.dataset.on === "1" ? 0 : 1;
const updatedMatrix = [...noteMatrix];
playSingleNote(currentScale[selectedNote]);
updatedMatrix[currentBeat][selectedNote] = isOn;
setNoteMatrix(updatedMatrix);
};
const reset = useCallback(() => {
setNoteMatrix([
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat],
[...emptyBeat]
]);
setIsPlaying(false);
Tone.Transport.stop();
}, []);
React.useEffect(() => {
Tone.Transport.loop = false;
Tone.Transport.on("stop", () => setCurrentBeat(0));
});
React.useEffect(() => {
Tone.Transport.bpm.value = currentBPM;
}, [currentBPM]);
React.useEffect(() => {
if (loop.current) {
loop.current.dispose();
}
loop.current = new Tone.Sequence(
(time, beat) => {
setCurrentBeat(beat);
synth.triggerAttackRelease(noteMap[beat], "16n", time);
},
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
"16n"
).start(0);
}, [noteMap, isPlaying]);
const togglePlay = React.useCallback(() => {
Tone.context.resume();
Tone.Transport.toggle();
setIsPlaying((isPlaying) => !isPlaying);
}, []);
return (
<>
<GridStyle>
<div className="beat">
{currentScale.map((note, i) => (
<div className="head" key={i}>
{note}
</div>
))}
</div>
{noteMatrix.map((beat, i) => (
<div className="beat" key={i} data-active={currentBeat === i ? 1 : 0}>
{currentScale.map((note, j) => (
<button
className="note"
key={j}
onClick={tick}
data-note={note}
data-on={beat[j]}
data-beat={i}
data-index={j}
/>
))}
</div>
))}
</GridStyle>
<button onClick={togglePlay}>{isPlaying ? "Stop" : "Play"}</button>
<button onClick={reset}>Clear</button>
Scale
<select
name="scale"
id="scale"
defaultValue="major"
onChange={(e) => setCurrentScale(SCALES[e.currentTarget.value])}
>
{["major", "minor", "suspended"].map((scale) => (
<option value={scale} key={scale}>
{scale}
</option>
))}
</select>
BPM
<select
name="bpm"
id="bpm"
onChange={(e) => setCurrentBPM(parseFloat(e.currentTarget.value))}
defaultValue={currentBPM}
>
{[80, 100, 120, 140, 160, 180, 200].map((bpm) => (
<option value={bpm} key={bpm}>
{bpm}
</option>
))}
</select>
</>
);
}
export default App;
最佳答案
synth
对象在每次渲染时创建。您可以将其移出 App
(或 useRef
),一切正常。
另一件事我注意到这里没有依赖列表:
React.useEffect(() => {
Tone.Transport.loop = false;
Tone.Transport.on("stop", () => setCurrentBeat(0));
}, []); // <--
附言多么好的项目!
关于javascript - 将 `Tone.PolySynth()` 与 `Sequence` 方法一起使用时,如何避免音频消失?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73439543/
上下文 我一直在尝试构建类似于 official example 的步进音序器在 Tone.js 文档中。然而,我没有使用 Players 方法播放 MP3 文件,而是想实现 PolySynth 来探
我是一名优秀的程序员,十分优秀!