باختصار
فصل عن القفلات من كتاب هارموني لطلاب المعهد الموسيقي، في عمودين. أمثلته الموسيقية الخمسة تُكتب نصًّا، وتُرسم نوتةً حقيقية، وتُرقَّم ويُكتب لها تعليق كما للأشكال، وتوضع قرب الفقرة التي تذكرها.
ما الذي ستنضده
الفصل الرابع من Four-Part Harmony، وهو منهج للسنوات الأولى في المعهد الموسيقي، على صفحة بمقاس 210 × 280 mm في عمودين بخط Crimson Pro. موضوع الدرس القفلات، وأمثلته الخمسة كورالات رباعية الأصوات على مدرجين، تحت صوت الباص أرقام رومانية للدرجات: القفلات الخمس في سلّم صول الكبير، وثلاثة أسطر من Nun danket alle Gott، والمزمور 134 من مزامير جنيف (Old Hundredth) كاملًا. لكل مثال رقمه من Example 4.1 إلى 4.5 (Ejemplo في الطبعة الإسبانية)، وتعليق بخط العناوين الصغيرة بلون عنابي، ويوضع في رأس العمود أو أسفله بعد الفقرة التي تحيل إليه؛ أما المزمور كله فيمتد على العمودين في رأس الصفحة 63. كُتب التوزيع الرباعي لهذا المنهج، وفُحص من الخامسات والأوكتافات المتوازية، ومن المسافات بين الأصوات، ومن حلّ الحسّاس.
تجيب هذه الوصفة عن
- كيف أرقّم الأمثلة الموسيقية وأجعلها تطفو كالأشكال في كتاب من عمودين؟
- كيف أضيف شكلًا بتعليق مرقّم وأحيل إليه في النص («انظر الشكل 3.2»)؟
- كيف أتحكم في موضع الشكل: أعلى الصفحة، أو بعرض العمودين، أو هنا تمامًا، أو في الهامش؟
الجواب المختصر
// 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' }),
];
المكونات
- الميزات
- أمثلة موسيقية مرقّمةموضع الأشكالاستشهادات تضع الأشكالالتدوين الموسيقينمط المدوّنة الموسيقيةتعليقات مرقّمة«شكل» و«جدول» بلغتكأنواع موارد مخصّصةنمط التعليقسطور المصدر والحقوقعمود واحد أو عمودانصفحات افتتاح مصمَّمةسمات العنوانالترويسات وأرقام الصفحاتهوامش متناظرةالترويسات بحسب دور الصفحةإطارات التنبيهأنماط الفقراتلوحة ألوان دلالية
- الخطوط
- Crimson Pro, Playfair Display, Fira Sans Condensed (SIL OFL 1.1)
- الأصول
- لا شيء: كل صورة مرسومة بالشيفرة
طريقة التحضير
#1 · اكتب كل كورال بصيغة 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 الأصوات أعمدةً تفصل بينها علامات الجدولة، من الأخفض إلى الأعلى. كل مدرج عمود واحد (spine) يقسمه *^ إلى اثنين: التينور والباص على مدرج مفتاح فا، والسوبرانو والألتو على مدرج مفتاح صول، فيكتب المحرّك صوتين على كل مدرج بسيقان متباعدة (الصوت الأيسر إلى أعلى، والأيمن إلى أسفل). وعمود **harm بعد مدرج الباص يطبع الدرجات تحته؛ Ib هو الانقلاب الأول وIc هو تآلف الرابعة والسادسة. تكتب الشيفرة الأعمدة بمسافات ثم تحوّلها إلى علامات جدولة، فلا يفسد أي محرر الملف، وتنال الطبعة الإسبانية درجاتها بحروف كبيرة (VI و IIb) كما تكتبها كتب الهارموني الإسبانية.
#2 · اجعل كل مثال موردًا من نوع example
يبني الجواب المختصر الموارد الخمسة. typeId: 'example' يأخذ نوع «مثال» المدمج، الذي يضيفه المحرّك إلى أنواع موارد الكتاب حين يستعمله مورد، باسم بلغة المستند (locale: 'es' يعطي Ejemplo) وبترقيم لكل فصل، فيكون أول مثال في الفصل الرابع 4.1. ومورد النوتة يبقى كاملًا ويطفو كما يطفو الشكل: position: 'top' أو 'bottom' يأخذ رأس أول عمود فيه متسع بعد الإحالة أو أسفله، وspan: 'page' يمدّ Old Hundredth على العمودين. وتأتي التعليقات والمصادر والنصوص البديلة من ملف Markdown ثانٍ لكل طبعة.
#3 · أعطِ أمثلة العمود مدرجًا أصغر
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 },
];
يتبع الكورالان الممتدان على عرض الصفحة scoreStyle المستند، بمدرجات ارتفاعها 5.4 mm وأرقام للمازورات في أول كل سطر. أما أمثلة العمود فتختار النمط المسمّى column: مدرجات 4.6 mm بلا أرقام مازورات وتباعد 0.8، فتبقى المازورات الخمس في المثال 4.2 على سطر واحد بدل أن تُترك الأخيرة وحدها في سطر ثانٍ.
#4 · شغّل محرّك النوتة قبل التنضيد
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() محرّك النوتة Verovio من الحزمة؛ وإلى أن يعمل تُنضَّد كل نوتة صندوقًا رماديًا. وتجعل continuation هذا الفصلَ الرابع من كتاب أطول، فتُرقَّم الأمثلة من 4.1 إلى 4.5 وتبدأ أرقام الصفحات من 61 على صفحة فردية.
#5 · افتتح الفصل على العمودين
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) } } },
] },
};
يمتد الافتتاح على العمودين (span: 'page' على عنوان المستوى الأول) ويأخذ العنوان الفرعي والمقدمة من سمات العنوان. وتُنزل minHeight النص 46 mm، فيبدأ العمودان على السطر نفسه تحت الخط.
الوصفة كاملة
// ═══ 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),
يعمل ملف script.js المجمّع كما هو: الصقه في سكربت الوحدة (module) لأي صفحة، أو افتح الوصفة على CodePen. مجلد الوصفة على GitHub ↗ (يفتح في تبويب جديد)
تنويعات
#ضع مثالًا بلا رقم في النص
كتلة الشيفرة تُبقي في النص نوتةً لا يحيل إليها النص، كمعادلة مستقلة: بلا رقم ولا تعليق.
- ::resource{id="ex-cadences"}
+ ```humdrum {style="column" alt="القفلات الخمس في صول الكبير"}
+ **kern **harm **kern
+ …
+ ```#سمّها أمثلة موسيقية
عرّف نوعًا خاصًا باسم آخر ووجّه typeId إليه.
- resourceTypes: defaultResourceTypes(LANG),
+ resourceTypes: [...defaultResourceTypes(LANG), { id: 'musex', name: 'مثال موسيقي',
+ shortLabel: 'مثال', captionPrefix: 'مثال موسيقي', numberingTemplate: '{h1}.{n}',
+ resetOn: 'h1', counterFormat: 'decimal' }],أخطاء شائعة
خطأ شائع
::resource{id="…"} لا يقبل إلا علامتي الاقتباس المزدوجتين
لا يُتعرَّف على التضمين الكتلي إلا بالصيغة ::resource{id="…"} بعلامتي اقتباس مزدوجتين؛ وأي صيغة أخرى تبقى في النص سطرًا ظاهرًا. أشكال في هذا الموضع بالضبط →
- مورد النوتة لا ينقسم أبدًا بين عمودين أو صفحتين. اجعل الأمثلة الطافية بضع مازورات، وضع القطعة الأطول في كتلة شيفرة، فهي تنقسم بين السطور.
- يملأ المحرّك كل سطر نوتة حتى عرض موضعه، ويبقى السطر الأخير بعرضه الطبيعي. وإذا ترك مثال مازورة وحدها في سطر ثانٍ، فاخفض
spacingأو حجم المدرج في نمطه.
الحقوق
- الوصفة
- Ignacio Ferro
- النص
- 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 · ملك عام
- Melody of the Old Hundredth (Psalm 134 in the Genevan Psalter, 1551) · Louis Bourgeois (attributed) · ملك عام
- الخطوط
- Crimson Pro (SIL OFL 1.1) · Playfair Display (SIL OFL 1.1) · Fira Sans Condensed (SIL OFL 1.1)
- الشيفرة
- MIT، مثل Postext
حرّر هذا الشرح ↗ (يفتح في تبويب جديد)مجلد الوصفة على GitHub ↗ (يفتح في تبويب جديد)


