Cut a sound into named clips, as a texture into frames (#357)
A sound could only be cut to one stretch, set on the asset itself. To
play two parts of one file, the file had to be imported twice. Now a
sound is always the whole file, and its clips are the stretches of it,
each with a name and a cut of its own.
- A clip is { id, name, props } on SoundAsset.clips. Its id shares the
generator with asset ids, so a sound field holds a sound or a clip,
as a texture field holds a texture or a frame.
- props are the cut: start, end, the fades and their curves, reverse.
Volume, loop, sound FX and bus stay on the sound for all its clips.
The cut rows left the sound's schema for CLIP_ROWS.
- findSound resolves either id. Play sound, the Sample of an
instrument, a Reverb impulse, song items, the scheduler and the test
sound of the Sound FX tab all go through it.
- Every sound field offers each sound followed by its clips, "Kick",
"Kick / Clip 1". A clip that is gone reads as "<id>?".
- The Sound tab has a strip under the ruler with the clips as bands.
A drag there makes a clip, a click picks one, beside the bands the
sound. Only the picked clip has handles. Reverse, fades and curves
in the header work on it and are greyed out without one.
- Whole file is gone from the header, as the user asked.
- Picking a clip is a selection of its own, like a frame. The
inspector shows its name, its cut rows and Delete clip. Delete
takes it away without asking, as with a frame. Ctrl+Z brings it
back.
- Copies get fresh clip ids: Duplicate, Embed a copy, a song
duplicated with its takes, and the copy a split or paste makes of
an embedded take. An item on a clip plays the same clip of its copy.
- A double-click on a song item playing a clip opens its sound with
the clip picked. The Sound row of an item on an embedded take
offers the take and its clips, which no list shows.
- A new take stays the whole sound. Clips are made by hand, as the
user decided.
- No migration in the code, as the user decided. The one project
with cut sounds was changed over by hand.
Deviation from the issue: deleting a used clip does not warn. A frame
does not warn either, and the issue had promised "like a frame".
Tests:
- Unit sound-clips (new): 11 green, 7 counter-checks each red
- Unit sounds, embedded: adjusted to clips, green
- e2e 34 (melektaus-atx): ALL PASSED, 90 checks (3 new, 12 rewritten)
- Counter-check in the browser: clips left out of the sound fields,
the picker check fails
- Gate (melektaus-atx): CHECK EXIT 0, 271 files, 4194 tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c7fa1d15b0
commit
42a3759d9d
32 files changed
+1230
-363
No files matched your search
@@ -15,6 +15,7 @@ import type { InstrumentAsset } from '../model/Asset';
|
||||
import { graphLookup } from '../model/graphLookup';
|
||||
import { textOf } from '../model/Properties';
|
||||
import { findAssetOf, type Project } from '../model/Project';
|
||||
import { findSound } from '../model/sound';
|
||||
import { playedSoundOf } from '../properties/playedSound';
|
||||
import { resolveAssetProps } from '../properties/resolve';
|
||||
import { volumeOf } from '../properties/sounds';
|
||||
@@ -77,19 +78,18 @@ export function instrumentSourcesOf(project: Project): InstrumentSources {
|
||||
return {
|
||||
lookup: graphLookup(project),
|
||||
sound: (assetId) => {
|
||||
const asset = findAssetOf(project, assetId, 'sound');
|
||||
if (!asset) return null;
|
||||
const ref = findSound(project, assetId);
|
||||
if (!ref) return null;
|
||||
// An asset without a file stays without one, so the Sample can say so.
|
||||
const file = asset.file ? playedSoundOf(project, asset) : '';
|
||||
return { file, volume: volumeOf(resolveAssetProps(project, asset)) };
|
||||
return { file: playedSoundOf(ref), volume: volumeOf(resolveAssetProps(project, ref.asset)) };
|
||||
},
|
||||
soundFx: (assetId) => {
|
||||
const asset = findAssetOf(project, assetId, 'soundfx');
|
||||
return asset ? soundFxBuildFor(asset, project).compiled : null;
|
||||
},
|
||||
key: (assetId) => {
|
||||
const sound = findAssetOf(project, assetId, 'sound');
|
||||
if (sound) return `${playedSoundOf(project, sound)}@${volumeOf(resolveAssetProps(project, sound))}`;
|
||||
const sound = findSound(project, assetId);
|
||||
if (sound) return `${playedSoundOf(sound)}@${volumeOf(resolveAssetProps(project, sound.asset))}`;
|
||||
const fx = findAssetOf(project, assetId, 'soundfx');
|
||||
return fx ? soundFxKey(fx, project) : '';
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user