Pick a pattern's sign in one row, as a song's (#364)
game-maker / gate (push) Successful in 3m52s
game-maker / suite (push) Successful in 15m0s

A pattern had two rows for its meter, Beats per bar and Beat, while a
song has had one row Sign since #363. Both now pick from the same list.

- The list of signs and how a sign becomes a meter moved to time.ts.
  Song and pattern share it.
- A pattern keeps its meter in `sign`. The fields `beats` and `beat`
  are gone, a sign that is not in the list counts as 4/4.
- Create pattern in a song hands the song's sign to the new pattern.
- The four patterns of cbbf160bb7e8 were moved to `sign` by hand
  (three 4/4, one 3/4). The other projects only had 4/4, which stays
  the default.

Tests:
- Unit pattern/embedded: 30 green (1 new, 2 rewritten), the
  counter-test reading `beats` again turns 2 red
- e2e 64 + 65 (melektaus-atx): ALL PASSED, 85 checks
- Gate (melektaus-atx): CHECK EXIT 0, 272 files, 4215 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-09 08:15:14 +02:00
1 parent 0d57a2f3c4
commit 8891f027db
9 files changed
+60 -67

No files matched your search

+2 -3
View File
@@ -311,7 +311,7 @@ describe('a pattern made in a song (#355)', () => {
it('hides only its bus, not its clock, length, instrument or sound FX (#363)', () => {
const { make } = withPatterns();
const own = make();
const keys = ['bus', 'tempo', 'beats', 'beat', 'bars', 'instrument', 'fx'];
const keys = ['bus', 'tempo', 'sign', 'bars', 'instrument', 'fx'];
expect(keys.map((k) => isRowHiddenWhenEmbedded(own, k))).toEqual([
true,
false,
@@ -319,7 +319,6 @@ describe('a pattern made in a song (#355)', () => {
false,
false,
false,
false,
]);
});
@@ -348,7 +347,7 @@ describe('a pattern made in a song (#355)', () => {
expect([patternTempo(copy.props), patternMeter(copy.props).beats]).toEqual([120, 4]);
const other = new CreateAssetCommand('song', 'Other');
f.stack.execute(other);
const theirs = new CreateAssetCommand('pattern', 'Theirs', { bars: 1, beats: 5 }, other.asset.id);
const theirs = new CreateAssetCommand('pattern', 'Theirs', { bars: 1, sign: '5/4' }, other.asset.id);
f.stack.execute(theirs);
edit([
...song().items,
+15 -4
View File
@@ -16,6 +16,7 @@ import {
patternSequence,
patternTempo,
} from '../../src/core/music/pattern';
import { SIGN_OPTIONS } from '../../src/core/music/time';
import { schemaForAsset } from '../../src/core/properties/PropertySchema';
import { makeProject, roundTrip } from '../helpers';
@@ -52,8 +53,7 @@ describe('pattern asset (#333)', () => {
'instrument',
'bars',
'grid',
'beats',
'beat',
'sign',
'tempo',
'fx',
'bus',
@@ -70,11 +70,22 @@ describe('pattern asset (#333)', () => {
it('measures its length in bars of its meter: 3 bars of 6/8 are 4320 ticks', () => {
const f = makePattern();
f.set('bars', 3);
f.set('beats', 6);
f.set('beat', '8');
f.set('sign', '6/8');
expect(patternLength(f.pattern())).toBe(3 * 6 * 240);
});
it('picks its meter from the signs a song has too, 4/4 for any other (#364)', () => {
const f = makePattern();
f.set('sign', '9/8');
expect(patternMeter(f.pattern().props)).toEqual({ beats: 9, unit: 8 });
f.set('sign', '12/8');
expect(patternMeter(f.pattern().props)).toEqual({ beats: 4, unit: 4 });
expect(schemaForAsset(f.project, f.pattern()).find((d) => d.key === 'sign')).toMatchObject({
kind: 'enum',
options: SIGN_OPTIONS,
});
});
it('holds notes to whole ticks, a tick at least, the keyboard and 0 to 1, sorted, ids unique', () => {
const held = heldPatternNotes([
{ id: 'b', tick: 480.4, length: 0.2, note: 64, velocity: 2 },