Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e51fdf8216 |
No files matched your search
@@ -7,14 +7,14 @@ The usual way to run it is one Linux AppImage. That file contains the window and
|
||||
## Run the AppImage
|
||||
|
||||
```bash
|
||||
chmod +x Recorder-Tab-Converter-0.2.1.AppImage
|
||||
./Recorder-Tab-Converter-0.2.1.AppImage
|
||||
chmod +x Recorder-Tab-Converter-0.2.2.AppImage
|
||||
./Recorder-Tab-Converter-0.2.2.AppImage
|
||||
```
|
||||
|
||||
If the file will not start because FUSE is missing:
|
||||
|
||||
```bash
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 ./Recorder-Tab-Converter-0.2.1.AppImage
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 ./Recorder-Tab-Converter-0.2.2.AppImage
|
||||
```
|
||||
|
||||
Open **Empty line**, or start from **Twinkle, opening** or **Ode, held note**. Drag any chart from C5 to D7, or drag **Rest** or **Space**, onto the line. Drag a placed item to move it. Choose a duration under it. Remove takes it off. **Shorter**, **Taller**, **Narrower**, **Wider**, and the corner grip change the line box. **Save PNG** downloads the finished row.
|
||||
@@ -30,7 +30,7 @@ From a checkout, one command:
|
||||
That installs Node packages, builds the app, and writes:
|
||||
|
||||
```text
|
||||
dist/Recorder-Tab-Converter-0.2.1.AppImage
|
||||
dist/Recorder-Tab-Converter-0.2.2.AppImage
|
||||
```
|
||||
|
||||
The AppImage is not committed. Keep it under 600MB.
|
||||
@@ -47,6 +47,6 @@ The dev server listens on [http://127.0.0.1:3847](http://127.0.0.1:3847).
|
||||
|
||||
## Fingerings
|
||||
|
||||
Baroque (English) fingerings are the first choice on the [Dolmetsch Online chart for recorders in C](https://dolmetsch.com/textfingeringchart.htm) (Brian Blood). The soprano is treated in concert pitch: it sounds as written, low C is C5, and the chart stops at D7. Double holes 6 and 7, forked F, and the second-octave thumb pinch are included. Alternate fingerings on that chart are not shown.
|
||||
Baroque (English) fingerings are the first choice on the [Dolmetsch Online chart for recorders in C](https://dolmetsch.com/textfingeringchart.htm) (Brian Blood). The soprano is labeled in concert pitch: low C is C5, and the chart stops at D7. Double holes 6 and 7, forked F, and the second-octave thumb pinch are included. Alternate fingerings on that chart are not shown.
|
||||
|
||||
Each chart is drawn foot-up: holes 7 and 6 at the top, then 5 through 1, then the thumb, with the mouthpiece at the bottom. A filled hole is closed. A treble staff under the holes shows the pitch and the length: notehead, stem, flags, dots, and rests.
|
||||
Each chart is drawn foot-up: holes 7 and 6 at the top, then 5 through 1, then the thumb, with the mouthpiece at the bottom. A filled hole is closed. A treble staff under the holes is written one octave below the sounding pitch. Low C, labeled C5, sits on the ledger line below the staff. Sounding C6 is written C5, and sounding C7 is written C6. Length is a notehead, stem, flags, dots, and rests. Labels keep the sounding names.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "recorder-tab-converter",
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"private": true,
|
||||
"description": "Arrange soprano recorder fingering charts by dragging them onto a line.",
|
||||
"main": "electron/main.cjs",
|
||||
|
||||
@@ -334,7 +334,7 @@ export function Editor() {
|
||||
<div>
|
||||
<h2 className="font-heading text-2xl">{title}</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Treble staff, 4/4. Noteheads, stems, flags, and dots show the length. Plus and minus
|
||||
Treble staff written an octave below the sounding pitch, 4/4. Noteheads, stems, flags, and dots show the length. Plus and minus
|
||||
change it. A space is empty room you can still drag. The box is {boxHeight}px tall
|
||||
{boxWidth ? `, ${boxWidth}px wide` : ""}.
|
||||
</p>
|
||||
|
||||
@@ -83,7 +83,7 @@ export function FingeringDiagram({
|
||||
|
||||
const [h1, h2, h3, h4, h5, h6a, h6b, h7a, h7b] = fingering.holes;
|
||||
const value = noteValue(quarters);
|
||||
const label = `${pitch}, ${durationLabel(quarters)} on the treble staff. Foot at the top, mouthpiece at the bottom. ${fingeringProse(fingering)}. ${fingeringMarks(fingering).join(", ")}`;
|
||||
const label = `${pitch}, written one octave lower, ${durationLabel(quarters)} on the treble staff. Foot at the top, mouthpiece at the bottom. ${fingeringProse(fingering)}. ${fingeringMarks(fingering).join(", ")}`;
|
||||
|
||||
return (
|
||||
<svg
|
||||
|
||||
+12
-7
@@ -8,13 +8,15 @@ const RECORDER_VB_H = 188;
|
||||
export const STAFF_SPACE = 4.6;
|
||||
const STEP = STAFF_SPACE / 2;
|
||||
|
||||
const TOP_INDEX = 20;
|
||||
const LOW_STEM_STEPS = 7;
|
||||
const PAD_TOP = 4;
|
||||
const PAD_BOTTOM = 8;
|
||||
/** Highest written note is sounding D7, drawn as D6. */
|
||||
const TOP_INDEX = 13;
|
||||
/** Lowest written note is sounding C5, drawn as C4 on the ledger line below. */
|
||||
const LOW_INDEX = -2;
|
||||
const PAD_TOP = 6;
|
||||
const PAD_BOTTOM = 14;
|
||||
|
||||
const ABOVE = TOP_INDEX * STEP;
|
||||
const BELOW = LOW_STEM_STEPS * STEP - 5 * STEP + PAD_BOTTOM;
|
||||
const BELOW = -LOW_INDEX * STEP + PAD_BOTTOM;
|
||||
|
||||
export const BOTTOM_LINE_Y = RECORDER_VB_H + 3 + PAD_TOP + ABOVE;
|
||||
export const CHART_VB_H = Math.ceil(BOTTOM_LINE_Y + BELOW);
|
||||
@@ -31,7 +33,10 @@ const LETTERS = ["C", "D", "E", "F", "G", "A", "B"] as const;
|
||||
|
||||
export type Accidental = "sharp" | "flat";
|
||||
|
||||
/** Treble staff. Index 0 is the bottom line, E4. C5, the recorder's low C, is 5. */
|
||||
/**
|
||||
* Treble staff, written one octave below sounding pitch.
|
||||
* Index 0 is the bottom line, E4. Sounding C5 is written C4, index -2.
|
||||
*/
|
||||
export function staffPlacement(pitch: string): { index: number; accidental: Accidental | null } | null {
|
||||
const match = /^([A-Ga-g])(#|b|♯|♭)?(-?\d+)$/.exec(pitch.trim());
|
||||
if (!match) return null;
|
||||
@@ -41,7 +46,7 @@ export function staffPlacement(pitch: string): { index: number; accidental: Acci
|
||||
mark === "#" || mark === "♯" ? "sharp" : mark === "b" || mark === "♭" ? "flat" : null;
|
||||
const octave = Number(match[3]);
|
||||
if (!Number.isInteger(octave)) return null;
|
||||
const diatonic = (octave - 4) * 7 + LETTERS.indexOf(letter as (typeof LETTERS)[number]);
|
||||
const diatonic = (octave - 1 - 4) * 7 + LETTERS.indexOf(letter as (typeof LETTERS)[number]);
|
||||
return { index: diatonic - 2, accidental };
|
||||
}
|
||||
|
||||
|
||||
+11
-10
@@ -176,17 +176,18 @@ describe("fingering line", () => {
|
||||
});
|
||||
|
||||
describe("treble staff", () => {
|
||||
it("puts low C in the third space and high D on a ledger line", () => {
|
||||
expect(staffPlacement("C5")).toEqual({ index: 5, accidental: null });
|
||||
expect(staffPlacement("C#5")).toEqual({ index: 5, accidental: "sharp" });
|
||||
expect(staffPlacement("Eb5")).toEqual({ index: 7, accidental: "flat" });
|
||||
expect(staffPlacement("G5")).toEqual({ index: 9, accidental: null });
|
||||
expect(staffPlacement("A5")).toEqual({ index: 10, accidental: null });
|
||||
expect(staffPlacement("D7")).toEqual({ index: 20, accidental: null });
|
||||
it("writes each sounding pitch one octave lower", () => {
|
||||
expect(staffPlacement("C5")).toEqual({ index: -2, accidental: null });
|
||||
expect(staffPlacement("C#5")).toEqual({ index: -2, accidental: "sharp" });
|
||||
expect(staffPlacement("Eb5")).toEqual({ index: 0, accidental: "flat" });
|
||||
expect(staffPlacement("C6")).toEqual({ index: 5, accidental: null });
|
||||
expect(staffPlacement("C7")).toEqual({ index: 12, accidental: null });
|
||||
expect(staffPlacement("D7")).toEqual({ index: 13, accidental: null });
|
||||
expect(ledgerIndexes(-2)).toEqual([-2]);
|
||||
expect(ledgerIndexes(5)).toEqual([]);
|
||||
expect(ledgerIndexes(9)).toEqual([]);
|
||||
expect(ledgerIndexes(10)).toEqual([10]);
|
||||
expect(ledgerIndexes(20)).toEqual([10, 12, 14, 16, 18, 20]);
|
||||
expect(ledgerIndexes(12)).toEqual([10, 12]);
|
||||
expect(ledgerIndexes(13)).toEqual([10, 12]);
|
||||
expect(stemDown(-2)).toBe(false);
|
||||
expect(stemDown(5)).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user