Put a sound in a room, a hall or a cave with a Reverb node (#328)
A sound FX had no space around it. The Reverb node folds the sound with an impulse response: one it makes itself, or a sound asset as the answer of a real space. It works on a voice and on a bus. - Type: Room, Hall, Cave and Plate have fixed numbers. Custom shows Decay (up to 10 s), Damping and Pre-delay. Sound shows the pick of a sound asset and Pre-delay. The rows are derived ports, the sound pick an idle param, so they only appear for their type. - The made impulse response is seeded noise per side, 60 dB down at Decay, through a low-pass closing from 20 kHz toward 500 Hz as far as Damping says. It is made once per rate, decay and damping, the last 16 are kept. - A sound as impulse response: the sound FX compilers now get the project, so a node can turn a sound asset into its file. The build key holds that file, so a cut or swapped sound builds again. The instrument compiler does the same for a Reverb inside an instrument. - The library loads that file with the voice, a sequence or before a bus builds its chain. A missing file leaves the reverb silent. - Mix blends dry and reverb and is the only number that moves while the sound plays, as the issue said. The voice rings for pre-delay plus decay, or as long as its sound. - The open point from #325 about sends on project buses is #351, not part of this. Tests: - Unit reverb (new) 4, soundfx 57 (4 new), sound-library 104 (2 new), green. - Counter-checks, each turns one test red: the impulse file not loaded with the voice, the tail without the length of the sound. - e2e 34 (melektaus-atx): ALL PASSED, 81 checks (1 new). Chrome offline: silent during the pre-delay, 35.9 dB fall over 0.6 s of a 1 s decay (36 expected), the dry half at 0.25 for mix 0.5. A sound with one sample at 0.2 s gives the impulse back 0.25 s later and nowhere else. - Gate (melektaus-atx) over the end of this series #318 to #328: CHECK EXIT 0, 267 files, 4134 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
071f194326
commit
d349251a01
14 files changed
+544
-47
No files matched your search
@@ -82,10 +82,11 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
sampleRate: () => soundLibrary.liveContext().sampleRate,
|
||||
/**
|
||||
* The nodes a stage of a voice becomes, in any context – an offline one renders what it does.
|
||||
* `at` is when its voice starts, which a Flanger's sweep counts from (#341).
|
||||
* `at` is when its voice starts, which a Flanger's sweep counts from (#341), `impulse` the
|
||||
* sound a Reverb that takes one has (#328).
|
||||
*/
|
||||
stageNodes: (context: BaseAudioContext, stage: FxStage, at?: number) =>
|
||||
stageNodes(context, stage, undefined, at),
|
||||
stageNodes: (context: BaseAudioContext, stage: FxStage, at?: number, impulse?: AudioBuffer) =>
|
||||
stageNodes(context, stage, undefined, at, impulse),
|
||||
/** The gain in dB a Saturation's auto-gain gives at a drive (#323). */
|
||||
saturationAutoGain,
|
||||
/** What drives the two shares of a Mix "Both at center", in any context (unstarted). */
|
||||
|
||||
@@ -311,7 +311,7 @@ export class GraphCanvas extends Widget {
|
||||
: asset.type === 'particles'
|
||||
? particleLibrary.buildFor(asset).problems
|
||||
: asset.type === 'soundfx'
|
||||
? soundFxBuildFor(asset).problems
|
||||
? soundFxBuildFor(asset, this.state.project).problems
|
||||
: asset.type === 'instrument'
|
||||
? instrumentCheck(asset, instrumentSourcesOf(this.state.project)).problems
|
||||
: asset.type === 'song'
|
||||
|
||||
@@ -100,7 +100,7 @@ export class SoundFxEditor extends AssetTabEditor<SoundFxAsset, GraphCanvas> {
|
||||
play(): void {
|
||||
const asset = this.asset();
|
||||
const sound = findAssetOf(this.state.project, this.previewSoundId, 'sound');
|
||||
const plan = asset && soundFxBuildFor(asset).compiled;
|
||||
const plan = asset && soundFxBuildFor(asset, this.state.project).compiled;
|
||||
if (!sound?.file || !plan) return;
|
||||
const props = resolveAssetProps(this.state.project, sound);
|
||||
const fx = { plan, params: { ...this.values } };
|
||||
@@ -123,7 +123,7 @@ export class SoundFxEditor extends AssetTabEditor<SoundFxAsset, GraphCanvas> {
|
||||
}
|
||||
|
||||
protected override infoText(asset: SoundFxAsset): string {
|
||||
const build = soundFxBuildFor(asset);
|
||||
const build = soundFxBuildFor(asset, this.state.project);
|
||||
if (!build.compiled) return build.reason;
|
||||
return `${build.effects} ${build.effects === 1 ? 'effect' : 'effects'}`;
|
||||
}
|
||||
@@ -133,7 +133,9 @@ export class SoundFxEditor extends AssetTabEditor<SoundFxAsset, GraphCanvas> {
|
||||
const sound = findAssetOf(this.state.project, this.previewSoundId, 'sound');
|
||||
this.soundPicker.setText(sound?.name ?? 'No sound');
|
||||
this.soundPicker.setEnabled(Boolean(asset && sound));
|
||||
this.playButton.setEnabled(Boolean(asset && sound?.file && soundFxBuildFor(asset).compiled));
|
||||
this.playButton.setEnabled(
|
||||
Boolean(asset && sound?.file && soundFxBuildFor(asset, this.state.project).compiled),
|
||||
);
|
||||
this.stopButton.setEnabled(Boolean(asset));
|
||||
this.syncParams(asset);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user