1import type { Chapter } from '../../ui/types.ts'
2import type { Params } from '../../engine/types.ts'
3import driver from '../membrane/driver.m?raw'
4import script from './drum.m?raw'
5import text from './text.md?raw'
6import {
7 CentsPanels,
8 STRIKE_CONTROL,
9 StrikePanels,
10 strikeParams,
11 strikeSummary,
12 type CentsOut,
13 type StrikeOut,
14} from '../membrane/panels.tsx'
15import { fmtCents } from '../common/panels.tsx'
17export const ch04: Chapter = {
18 id: 'square',
19 nav: 'A square drum',
20 part: 'Drums',
21 title: 'A square drum',
22 blurb:
23 'The same scheme in two dimensions: an inharmonic set of partials, a stability limit of 1/√2, and a pitch error that depends on the direction a wave travels.',
24 text,
25 labs: [
26 {
27 id: 'strike',
28 title: 'Striking the drum',
29 caption:
30 'The fundamental is 150 Hz. Listen to the exact drum first, then to the grid; move the strike to the centre and hear three quarters of the modes disappear.',
31 driver,
32 script,
33 controls: [
34 { kind: 'slider', key: 'N', label: 'grid intervals N', min: 0, max: 0, values: [24, 32, 48, 64] },
35 { kind: 'slider', key: 'lam', label: 'Courant number λ', min: 0, max: 0, values: [0.3, 0.5, 0.7] },
36 STRIKE_CONTROL,
37 ],
38 defaults: { N: 48, lam: 0.7, at: 'off' },
39 derive: (p: Params) => strikeParams(p, 'fd2'),
40 editorOpen: true,
41 render: (d: StrikeOut) => <StrikePanels d={d} />,
42 summary: strikeSummary,
43 },
44 {
45 id: 'cents',
46 title: 'Pitch error in every direction',
47 caption:
48 'Modes at three wavelengths, at directions from nearly along an edge to nearly along the other. Markers are measured; lines are the dispersion relation at the same modes.',
49 driver,
50 script,
51 controls: [{ kind: 'slider', key: 'N', label: 'grid intervals N', min: 0, max: 0, values: [32, 64] }],
52 defaults: { N: 64 },
53 derive: (p: Params) => {
54 const N = Number(p.N)
55 return { mode: 'cents', N, lams: [0.3, 0.5, Math.SQRT1_2], rs: [N / 8, N / 4, N / 2], nang: 9 }
56 },
57 render: (d: CentsOut) => <CentsPanels d={d} />,
58 summary: (d: CentsOut) => [{ label: 'measured minus predicted, largest', value: fmtCents(d.agree) }],
59 },
60 ],
61}