En poques paraules
Un capítol sobre les cadències d'un llibre d'harmonia de conservatori, a dues columnes. Els cinc exemples musicals s'escriuen com a text, es graven com a partitures, es numeren com les figures i van al costat del paràgraf que els cita.
Què compondràs
El capítol 4 d'Armonía a cuatro voces, un mètode per als primers cursos del conservatori, en una pàgina de 210 × 280 mm a dues columnes de Crimson Pro. La lliçó tracta de les cadències, i els seus cinc exemples són corals a quatre veus en dos pentagrames, amb els graus en xifres romanes sota el baix: les cinc cadències de sol major, tres versos de Nun danket alle Gott i el Salm 134 de Ginebra sencer. Cadascun porta el seu número, d'Ejemplo 4.1 a 4.5 (Example a l'edició anglesa), un peu en la lletra de rètols amb l'etiqueta en granat, i es col·loca al cap o al peu de la columna que segueix el paràgraf que el cita; el salm sencer ocupa les dues columnes al cap de la pàgina 63. Les harmonitzacions es van escriure per a aquest mètode i es van revisar a la recerca de quintes i octaves paral·leles, distàncies entre veus i resolució de la sensible.
Aquesta recepta respon a
- Com numero els exemples musicals i els faig flotar com figures en un llibre a dues columnes?
- Com afegeixo una figura amb peu numerat i la cito en el text («vegeu la fig. 3.2»)?
- Com decideixo on va una figura: al capdamunt de la pàgina, a l'amplada de les dues columnes, just aquí o al marge?
La resposta curta
// In the Markdown, :ref{id="ex-old-hundredth" style="full"} prints 'Example 4.4' ('Ejemplo
// 4.4' in Spanish) and ::resource{id="…"} anchors the float after the paragraph that cites it.
const example = (id, source, placement, styleId) => {
const [caption, note, altText] = CAPTIONS[id];
return { id, typeId: 'example', kind: 'score', caption, note, altText, placement,
score: { format: 'humdrum', source, styleId }, createdAt: 0, updatedAt: 0 };
};
// The **kern sources are in #region art-scores; captions in content.captions.<lang>.md.
const resources = () => [
example('ex-cadences', CADENCES, { position: 'bottom' }, 'column'),
example('ex-nun-danket', NUN_DANKET, { position: 'top' }, 'column'),
example('ex-nun-danket-3', NUN_DANKET_3, { position: 'auto' }, 'column'),
// Across both columns in the document's own score style: the whole tune in two systems.
example('ex-old-hundredth', OLD_HUNDREDTH, { position: 'top', span: 'page' }),
example('ex-deceptive', DECEPTIVE, { position: 'auto', span: 'page' }),
];
Ingredients
- Funcions
- Exemples musicals numeratsCol·locació de figuresCites que col·loquen les figuresNotació musicalEstil de partituraPeus numeratsFigura i Taula en la teva llenguaTipus de recurs propisEstil dels peusLínies de font i crèditUna o dues columnesObertures dissenyadesAtributs de títolCapçaleres i folisMarges simètricsCapçaleres segons el tipus de pàginaRequadresEstils de paràgrafPaleta de color semàntica
- Tipografia
- Crimson Pro, Playfair Display, Fira Sans Condensed (SIL OFL 1.1)
- Recursos
- Cap: totes les imatges es dibuixen en codi
Elaboració
#1 · Escriu cada coral en Humdrum **kern
// Each example lists its spines as Humdrum wants them, lowest first: the bass staff split into
// tenor and bass, the **harm spine (printed under that staff), the treble staff split into
// soprano and alto. Writing them with spaces keeps the pen free of tab characters.
const kern = (meter, body) => [
'**kern **harm **kern', '*clefF4 * *clefG2', '*k[f#] * *k[f#]', `*M${meter} * *M${meter}`,
'*^ * *^', ...body.trim().split('\n'), '== == == == ==', '*- *- *- *- *-',
].map((row) => row.trim().split(/ +/)
// Spanish harmony books write every degree in capitals: vi is VI, iib is IIb.
.map((cell) => (LANG === 'es' && /^(?:i{1,3}|iv|vi{0,2})[bc7]?$/.test(cell)
? cell.replace(/^[iv]+/, (degree) => degree.toUpperCase()) : cell))
.join('\t')).join('\n');
Humdrum escriu les veus en columnes separades per tabuladors, de la més greu a la més aguda. Cada pentagrama és una columna (spine) dividida en dues amb *^: tenor i baix al pentagrama de fa, soprano i contralt al de sol, de manera que el gravador posa dues veus per pentagrama amb les pliques separades (la veu de l'esquerra cap amunt, la de la dreta cap avall). Una columna **harm després del pentagrama del baix hi imprimeix els graus a sota; Ib és la primera inversió i Ic, la quarta i sexta. El guió escriu les columnes amb espais i les converteix en tabuladors, així cap editor no pot espatllar el fitxer, i l'edició castellana rep els graus en majúscules (VI, IIb), com els escriuen els llibres d'harmonia en castellà.
#2 · Fes de cada exemple un recurs de tipus exemple
La resposta curta construeix els cinc recursos. typeId: 'example' pren el tipus Exemple integrat, que el motor afegeix als tipus de recurs del llibre quan un recurs l'usa, amb el nom en la llengua del document (locale: 'es' dona Ejemplo) i numerat per capítol, de manera que el primer exemple del capítol 4 és el 4.1. Un recurs de partitura es manté sencer i flota com una figura: position: 'top' o 'bottom' pren el cap o el peu de la columna següent amb lloc després de la cita, i span: 'page' estén el Salm 134 per les dues columnes. Els peus, els crèdits i els textos alternatius surten d'un segon fitxer Markdown per edició.
#3 · Dona als exemples de columna un pentagrama més petit
const scoreStyle = {
staffSize: mm(5.4), musicFont: 'Bravura', barNumbers: 'system', color: col('ink'),
textFont: TEXT, // the Roman numerals under the bass, in the text face
marginTop: pt(4), marginBottom: pt(2),
};
const scoreStyles = [ // rastral 4.6 mm: four staves of a chorale fit a column in two systems
{ id: 'column', name: t({ en: 'Column examples', es: 'Ejemplos de columna' }),
staffSize: mm(4.6), barNumbers: 'none', spacing: 0.8 },
];
Els dos corals a l'amplada de la pàgina fan servir l'scoreStyle del document, amb pentagrames de 5,4 mm i números de compàs al principi de cada sistema. Els exemples de columna trien l'estil amb nom column: pentagrames de 4,6 mm, sense números de compàs i espaiat 0,8, que manté els cinc compassos de l'exemple 4.2 en un sol sistema en lloc de deixar l'últim sol en un segon.
#4 · Engega el gravador abans de compondre
await initMusicEngine(); // until it runs, every score is laid out as a grey box
const continuation = { pageNumbering: { startAt: 61 }, // odd: the opener is a recto
headings: { h1: 3, h2: 0, h3: 0, h4: 0, h5: 0, h6: 0 } }; // the next # is chapter 4
const doc = await buildDocumentWithFonts({ markdown, resources: resources(), continuation },
config, kitFonts(FONTS));
showPages(doc, { title: t({ en: 'Numbered chorale examples in a harmony method',
es: 'Corales numerados en un método de armonía' }) });
initMusicEngine() carrega Verovio, el gravador, des del paquet; fins que s'engega, cada partitura es compon com una capsa grisa. La continuation fa d'aquest el capítol 4 d'un llibre més llarg, així que els exemples es numeren del 4.1 al 4.5 i els folis comencen al 61, en pàgina senar.
#5 · Obre el capítol sobre les dues columnes
const label = { fontFamily: LABEL, fontWeight: 600, textTransform: 'uppercase' };
const opener = {
enabled: true, minHeight: mm(46),
slot: { elements: [
{ kind: 'text', id: 'kicker', content: '{attr.kicker}', ...label, fontSize: pt(9),
letterSpacing: pt(1.8), color: col('accent'), align: 'left',
placement: { anchor: { to: 'container', edge: 'top-left' },
offset: { x: mm(0), y: mm(6) } } },
{ kind: 'text', id: 'title', content: '{titleText}', fontFamily: DISPLAY, fontSize: pt(36),
lineHeight: 1.05, fontWeight: 400, color: col('ink'), align: 'left',
placement: { anchor: { to: '#kicker', edge: 'below' }, offset: { x: mm(0), y: mm(3) },
size: { width: mm(150), height: 'auto' } } },
{ kind: 'text', id: 'deck', content: '{attr.deck}', fontFamily: TEXT, fontSize: pt(13),
lineHeight: 1.25, italic: true, color: col('muted'), align: 'left',
placement: { anchor: { to: '#title', edge: 'below' }, offset: { x: mm(0), y: mm(3) },
size: { width: mm(120), height: 'auto' } } },
{ kind: 'rule', id: 'rule', direction: 'horizontal', thickness: pt(1.5), color: col('accent'),
placement: { anchor: { to: '#deck', edge: 'below' }, offset: { x: mm(0), y: mm(5) },
size: { width: mm(18) } } },
] },
};
L'obertura ocupa les dues columnes (span: 'page' al títol de nivell u) i pren l'avanttítol i l'entradeta dels atributs del títol. minHeight enfonsa el text 46 mm, de manera que les dues columnes comencen a la mateixa línia sota el filet.
La recepta completa
// ═══ Postext Cookbook · Nº 166 · Numbered chorale examples in a harmony method ══
// https://postext.dev/en/cookbook/conservatory-method-chorales
// Code: MIT · Text and four-part settings: original (CC BY 4.0) · Melodies: public domain
// Fonts: Crimson Pro, Playfair Display, Fira Sans Condensed (OFL 1.1) · Needs postext ≥ 1.54.0
import {
buildDocumentWithFonts, renderPageToCanvas, defaultResourceTypes, initMusicEngine,
} from 'https://esm.sh/postext';
const LANG = 'es'; // @lang: the language of the sample document ('en' | 'es')
const RECIPE = 'conservatory-method-chorales';
// ─── 1 · Design ─────────────────────────────────────────────────────────────
// A claret accent on warm paper tones, every colour linked by id.
const palette = { ink: '#1f1a17', accent: '#7a2531', tint: '#f5eee6', rule: '#cdbfb2',
muted: '#6a5f57', paper: '#ffffff' };
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = [
...Object.entries(palette).map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } })),
{ id: 'main-color', name: 'accent (defaults)', value: { hex: palette.accent, model: 'hex' } },
];
const [TEXT, DISPLAY, LABEL] = ['Crimson Pro', 'Playfair Display', 'Fira Sans Condensed'];
const OUTER = 16; // outer margin in mm: running heads and folios align to it
const LEAD = 14.2; // body leading in pt
// #region answer: score resources typed 'example', floated and numbered like figures
// In the Markdown, :ref{id="ex-old-hundredth" style="full"} prints 'Example 4.4' ('Ejemplo
// 4.4' in Spanish) and ::resource{id="…"} anchors the float after the paragraph that cites it.
const example = (id, source, placement, styleId) => {
const [caption, note, altText] = CAPTIONS[id];
return { id, typeId: 'example', kind: 'score', caption, note, altText, placement,
score: { format: 'humdrum', source, styleId }, createdAt: 0, updatedAt: 0 };
};
// The **kern sources are in #region art-scores; captions in content.captions.<lang>.md.
const resources = () => [
example('ex-cadences', CADENCES, { position: 'bottom' }, 'column'),
example('ex-nun-danket', NUN_DANKET, { position: 'top' }, 'column'),
example('ex-nun-danket-3', NUN_DANKET_3, { position: 'auto' }, 'column'),
// Across both columns in the document's own score style: the whole tune in two systems.
example('ex-old-hundredth', OLD_HUNDREDTH, { position: 'top', span: 'page' }),
example('ex-deceptive', DECEPTIVE, { position: 'auto', span: 'page' }),
];
// #endregion
// #region scores: one score style for the page-wide chorale, a smaller one for the columns
const scoreStyle = {
staffSize: mm(5.4), musicFont: 'Bravura', barNumbers: 'system', color: col('ink'),
textFont: TEXT, // the Roman numerals under the bass, in the text face
marginTop: pt(4), marginBottom: pt(2),
L'script.js compost funciona tal com és: enganxa'l com a script de mòdul en qualsevol pàgina o obre la recepta a CodePen. Carpeta de la recepta a GitHub ↗ (s'obre en una pestanya nova)
Variants
#Posa un exemple sense número al text
Un bloc de codi amb la partitura la deixa al text sense que el text la citi, com una fórmula destacada: sense número ni peu.
- ::resource{id="ex-cadences"}
+ ```humdrum {style="column" alt="Les cinc cadències en sol major"}
+ **kern **harm **kern
+ …
+ ```#Anomena'ls exemples musicals
Defineix un tipus propi per a un altre nom i apunta-hi typeId.
- resourceTypes: defaultResourceTypes(LANG),
+ resourceTypes: [...defaultResourceTypes(LANG), { id: 'musex', name: 'Exemple musical',
+ shortLabel: 'Ex. mus.', captionPrefix: 'Exemple musical', numberingTemplate: '{h1}.{n}',
+ resetOn: 'h1', counterFormat: 'decimal' }],Errors freqüents
Error freqüent
::resource{id="…"} només admet cometes dobles
Una inserció de bloc només es reconeix com a ::resource{id="…"} amb cometes dobles; qualsevol altra forma es queda al text com una línia visible. Figures just aquí →
- Un recurs de partitura no es parteix mai entre columnes ni entre pàgines. Limita els exemples flotants a uns quants compassos i posa les peces més llargues en un bloc de codi, que es parteix entre sistemes.
- El gravador omple cada sistema fins a l'amplada del seu buit, i l'últim sistema conserva la seva amplada natural. Si un exemple deixa un compàs sol en un segon sistema, abaixa l'
spacingo la mida de pentagrama del seu estil.
Crèdits
- Recepta
- Ignacio Ferro
- Text
- Chapter text and the four-part settings of every example · Ignacio Ferro · CC BY 4.0
- Melody of Nun danket alle Gott (Praxis pietatis melica, 1647), read from the soprano of Bach's setting BWV 386 · Johann Crüger · domini públic
- Melody of the Old Hundredth (Psalm 134 in the Genevan Psalter, 1551) · Louis Bourgeois (attributed) · domini públic
- Fonts
- Crimson Pro (SIL OFL 1.1) · Playfair Display (SIL OFL 1.1) · Fira Sans Condensed (SIL OFL 1.1)
- Codi
- MIT, com Postext
Editar aquest text ↗ (s'obre en una pestanya nova)Carpeta de la recepta a GitHub ↗ (s'obre en una pestanya nova)


