Em poucas palavras
Um capítulo sobre cadências de um livro de harmonia de conservatório, em duas colunas. Os cinco exemplos musicais são escritos como texto, gravados como partituras, numerados como figuras e postos perto do parágrafo que os cita.
O que você vai compor
O capítulo 4 de Four-Part Harmony, um método para os primeiros anos de conservatório, numa página de 210 × 280 mm em duas colunas de Crimson Pro. A lição trata das cadências, e os seus cinco exemplos são corais a quatro vozes em duas pautas, com os graus em algarismos romanos sob o baixo: as cinco cadências de sol maior, três versos de Nun danket alle Gott e o Salmo 134 de Genebra (o Old Hundredth) completo. Cada um leva um número, de Example 4.1 a 4.5 (Ejemplo na edição espanhola), uma legenda na fonte dos rótulos com o rótulo em vinho, e fica no topo ou no pé da coluna que segue o parágrafo que o cita; o salmo inteiro ocupa as duas colunas no topo da página 63. As harmonizações foram escritas para este método e conferidas quanto a quintas e oitavas paralelas, distância entre as vozes e resolução da sensível.
Esta receita responde a
- Como numero os exemplos musicais e os faço flutuar como figuras num livro em duas colunas?
- Como acrescento uma figura com legenda numerada e a cito no texto (“ver fig. 3.2”)?
- Como decido onde vai uma figura: no alto da página, sobre as duas colunas, exatamente aqui ou na margem?
A 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' }),
];
Ingredientes
- Funcionalidades
- Exemplos musicais numeradosPosicionamento de figurasCitações que posicionam as figurasNotação musicalEstilo de partituraLegendas numeradasFigura e Tabela no seu idiomaTipos de recurso personalizadosEstilo de legendaLinhas de fonte e créditoUma ou duas colunasAberturas desenhadasAtributos de títuloCabeços e fóliosMargens espelhadasCabeços por tipo de páginaBoxesEstilos de parágrafoPaleta de cores semântica
- Tipografia
- Crimson Pro, Playfair Display, Fira Sans Condensed (SIL OFL 1.1)
- Materiais
- Nenhum: todas as imagens são desenhadas em código
Preparo
#1 · Escrever cada coral em 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');
O Humdrum escreve as vozes em colunas separadas por tabulações, da mais grave à mais aguda. Cada pauta é uma coluna (spine) dividida em duas com *^: tenor e baixo na clave de fá, soprano e contralto na de sol, e assim o gravador põe duas vozes por pauta com as hastes separadas (a voz da esquerda para cima, a da direita para baixo). Uma coluna **harm depois da pauta do baixo imprime os graus sob ela; Ib é a primeira inversão e Ic, a quarta e sexta. O script escreve as colunas com espaços e as converte em tabulações, para que nenhum editor estrague o arquivo, e a edição espanhola recebe os graus em maiúsculas (VI, IIb), como os livros de harmonia espanhóis os escrevem.
#2 · Fazer de cada exemplo um recurso do tipo exemplo
A resposta curta monta os cinco recursos. typeId: 'example' usa o tipo Exemplo embutido, que o motor acrescenta aos tipos de recurso do livro quando um recurso o usa, com o nome no idioma do documento (locale: 'es' dá Ejemplo) e numerado por capítulo: o primeiro exemplo do capítulo 4 é o 4.1. Um recurso de partitura fica inteiro e flutua como uma figura: position: 'top' ou 'bottom' ocupa o topo ou o pé da próxima coluna com espaço depois da citação, e span: 'page' estende o Old Hundredth pelas duas colunas. Legendas, créditos e textos alternativos vêm de um segundo arquivo Markdown por edição.
#3 · Dar aos exemplos de coluna uma pauta menor
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 },
];
Os dois corais na largura da página seguem o scoreStyle do documento, com pautas de 5,4 mm e números de compasso no começo de cada sistema. Os exemplos de coluna escolhem o estilo nomeado column: pautas de 4,6 mm, sem números de compasso e espaçamento 0,8, que mantém os cinco compassos do exemplo 4.2 num só sistema em vez de deixar o último sozinho num segundo.
#4 · Iniciar o gravador antes da composição
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 o Verovio, o gravador, a partir do pacote; até ele rodar, cada partitura é diagramada como uma caixa cinza. A continuation faz deste o capítulo 4 de um livro mais longo, e assim os exemplos vão de 4.1 a 4.5 e os fólios começam em 61, numa página ímpar.
#5 · Abrir o capítulo sobre as duas colunas
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) } } },
] },
};
A abertura ocupa as duas colunas (span: 'page' no título de nível um) e tira o antetítulo e o olho dos atributos do título. minHeight rebaixa o texto 46 mm, e as duas colunas começam na mesma linha sob o fio.
A receita 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 = 'en'; // @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),
O script.js montado funciona como está: cole-o como script de módulo em qualquer página ou abra a receita no CodePen. Pasta da receita no GitHub ↗ (abre em uma nova aba)
Variações
#Pôr um exemplo sem número no texto
Um bloco de código mantém no texto uma partitura que o texto não cita, como uma fórmula destacada: sem número nem legenda.
- ::resource{id="ex-cadences"}
+ ```humdrum {style="column" alt="As cinco cadências em sol maior"}
+ **kern **harm **kern
+ …
+ ```#Chamá-los de exemplos musicais
Defina um tipo seu para outro nome e aponte typeId para ele.
- resourceTypes: defaultResourceTypes(LANG),
+ resourceTypes: [...defaultResourceTypes(LANG), { id: 'musex', name: 'Exemplo musical',
+ shortLabel: 'Ex. mus.', captionPrefix: 'Exemplo musical', numberingTemplate: '{h1}.{n}',
+ resetOn: 'h1', counterFormat: 'decimal' }],Erros comuns
Erro comum
::resource{id="…"} só aceita aspas duplas
Uma inserção em bloco só é reconhecida como ::resource{id="…"} com aspas duplas; qualquer outra forma fica no texto como uma linha visível. Figuras exatamente aqui →
- Um recurso de partitura nunca se divide entre colunas ou páginas. Limite os exemplos flutuantes a poucos compassos e ponha uma peça mais longa num bloco de código, que se divide entre sistemas.
- O gravador preenche cada sistema até a largura do seu espaço, e o último mantém a largura natural. Quando um exemplo deixa um compasso sozinho num segundo sistema, reduza o
spacingou o tamanho de pauta do seu estilo.
Créditos
- Receita
- Ignacio Ferro
- Texto
- 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 · domínio público
- Melody of the Old Hundredth (Psalm 134 in the Genevan Psalter, 1551) · Louis Bourgeois (attributed) · domínio público
- Fontes
- Crimson Pro (SIL OFL 1.1) · Playfair Display (SIL OFL 1.1) · Fira Sans Condensed (SIL OFL 1.1)
- Código
- MIT, como o Postext
Editar este texto ↗ (abre em uma nova aba)Pasta da receita no GitHub ↗ (abre em uma nova aba)


