Browse Source

first working version

mrkvon 1 year ago
commit
557a7e789a
8 changed files with 203 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 56
    0
      README.md
  3. 20
    0
      bin/index.mjs
  4. BIN
      example.png
  5. 84
    0
      lib/Fingerboard.mjs
  6. 10
    0
      lib/chords.mjs
  7. 10
    0
      lib/help.mjs
  8. 22
    0
      package.json

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+node_modules

+ 56
- 0
README.md View File

@@ -0,0 +1,56 @@
1
+# Violin chords
2
+
3
+Generate shapes of chords for violin
4
+
5
+## Instal
6
+
7
+```bash
8
+sudo yarn global add violin-chords
9
+# or
10
+sudo npm install -g violin-chords
11
+```
12
+
13
+## Use
14
+
15
+```bash
16
+violin-chords [note][accidental][chord]
17
+
18
+# e.g.
19
+
20
+violin-chords cmaj7
21
+violin-chords a#6
22
+violin-chords ebmaj7
23
+violin-chords g#+
24
+```
25
+
26
+### notes
27
+
28
+```
29
+c, d, e, f, g, a, b, h
30
+```
31
+We use German naming, i. e. a# === b === hb
32
+
33
+### accidentals (optional)
34
+
35
+```
36
+#, b
37
+```
38
+
39
+### chords
40
+
41
+```
42
+maj, min, +, 0, 6, 7, maj7, min7
43
+```
44
+
45
+## Output
46
+
47
+cmaj7
48
+
49
+![example output](example.png)
50
+
51
+- The numbers (in hex (a === 10, b === 11)) represent interval in semitones above tonic.  E.g. 0: tonic, 4: major third, 7: perfect fifth (dominant), b: major seventh.
52
+- The first line (without dots) represents empty strings. Lines have a semitone
53
+- It's up to the player to choose a comfortable fingering.
54
+  - choose all the numbers if possible, or as many as possible
55
+  - choose the more significant tones. e.g. `b` matters for the character cmaj7 more than `4` or `7`
56
+  - you may want 0 (tonic) at the lowest played string

+ 20
- 0
bin/index.mjs View File

@@ -0,0 +1,20 @@
1
+#!/bin/sh
2
+":" //# comment; exec /usr/bin/env node --experimental-modules "$0" "$@"
3
+
4
+import Fingerboard from '../lib/Fingerboard.mjs';
5
+import help from '../lib/help.mjs';
6
+
7
+(function () {
8
+  const argv = process.argv.slice(2);
9
+
10
+  const fingerboard = new Fingerboard(25);
11
+
12
+  if (argv.length < 1) {
13
+    console.log(help());
14
+    return;
15
+  }
16
+
17
+  fingerboard.highlightChord(argv[0]);
18
+
19
+  console.log(fingerboard.draw());
20
+}());

BIN
example.png View File


+ 84
- 0
lib/Fingerboard.mjs View File

@@ -0,0 +1,84 @@
1
+import { chords } from './chords.mjs'; // it is weird that we need to provide extension
2
+
3
+class FString { // becase String is taken
4
+  constructor(tune, threads) {
5
+    this.tune = tune % 12;
6
+    this.threads = Array(threads).fill(null);
7
+  }
8
+
9
+  highlightNote(i, symbol) {
10
+    this.threads[i] = symbol;
11
+  }
12
+}
13
+
14
+export default class Fingerboard {
15
+  constructor(threads) {
16
+    this.threads = threads;
17
+    this.iv = new FString(7, threads);
18
+    this.iii = new FString(14, threads);
19
+    this.ii = new FString(21, threads);
20
+    this.i = new FString(28, threads);
21
+  }
22
+
23
+  draw() {
24
+    let fingerboard = 'g d a e\n\n'
25
+    for (let i = 0; i < this.threads; i++) {
26
+      fingerboard += this.drawThread(i) + '\n';
27
+    }
28
+
29
+    return fingerboard;
30
+  }
31
+
32
+  drawThread(i) {
33
+    const empty = (i === 0) ? ' ' : '.';
34
+    const drawPoint = (string, i, empty) => string.threads[i] === null ? empty : string.threads[i];
35
+    return `${drawPoint(this.iv, i, empty)} ${drawPoint(this.iii, i, empty)} ${drawPoint(this.ii, i, empty)} ${drawPoint(this.i, i, empty)}`;
36
+  }
37
+
38
+  highlightNote(base, interval) {
39
+    [this.iv, this.iii, this.ii, this.i].forEach((string) => {
40
+      for (let i = 0; i < this.threads; i++) {
41
+        // console.log(i, string.tune, base, interval)
42
+        if ((i + string.tune) % 12 === (base + interval) % 12) {
43
+          string.highlightNote(i, interval.toString(16))
44
+        }
45
+      }
46
+    });
47
+  }
48
+
49
+  highlightChord(chord) {
50
+    const [base, intervals] = chord2numbers(chord);
51
+    intervals.forEach(interval => this.highlightNote(base, interval));
52
+  }
53
+}
54
+
55
+function note2number(note) {
56
+  const notes = {
57
+    c: 0,
58
+    d: 2,
59
+    e: 4,
60
+    f: 5,
61
+    g: 7,
62
+    a: 9,
63
+    b: 10,
64
+    h: 11,
65
+  }
66
+
67
+  if (note.length === 1) return notes[note];
68
+
69
+  const accidental2number = accidental => {
70
+    if (accidental === '#') return 1;
71
+    if (accidental === 'b') return 11;
72
+    throw new Error('invalid accidental (#, b)');
73
+  }
74
+
75
+  if (note.length === 2) return notes[note[0]] + accidental2number(note[1]) % 12;
76
+  throw new Error('invalid note name (c, d, e, f, g, a, b, h)');
77
+}
78
+
79
+function chord2numbers(chord) {
80
+  const separator = (['#', 'b'].includes(chord[1])) ? 2 : 1;
81
+  const note = chord.slice(0, separator);
82
+  const chordType = chord.slice(separator);
83
+  return [note2number(note), chords[chordType]];
84
+}

+ 10
- 0
lib/chords.mjs View File

@@ -0,0 +1,10 @@
1
+export const chords = {
2
+  'maj': [0, 4, 7],
3
+  'min': [0, 3, 7],
4
+  '+': [0, 4, 8],
5
+  '0': [0, 3, 6, 9],
6
+  '6': [0, 4, 7, 9],
7
+  '7': [0, 4, 7, 10],
8
+  'maj7': [0, 4, 7, 11],
9
+  'min7': [0, 3, 7, 10],
10
+};

+ 10
- 0
lib/help.mjs View File

@@ -0,0 +1,10 @@
1
+export default function help() {
2
+  return `usage:
3
+violin-chords [chord name]
4
+
5
+supported notes: c, d, e, f, g, a, b, h (please note that we use german naming: b is equivalent to a# and hb)
6
+supported accidentals: #, b
7
+supported chords: maj, min, +, 0, 6, 7, maj7, min7
8
+
9
+chord name examples: cmaj, dmin, f#+, abmin7, b0`;
10
+}

+ 22
- 0
package.json View File

@@ -0,0 +1,22 @@
1
+{
2
+  "name": "violin-chords",
3
+  "version": "0.0.1",
4
+  "description": "Shapes of violin chords.",
5
+  "author": "mrkvon <mrkvon@protonmail.com> (https://mrkvon.org)",
6
+  "type": "module",
7
+  "repository": {
8
+    "type": "git",
9
+    "url": "https://git.mrkvon.org/mrkvon/violin-chords.git"
10
+  },
11
+  "homepage": "https://git.mrkvon.org/mrkvon/violin-chords",
12
+  "preferGlobal": true,
13
+  "main": "bin/index.mjs",
14
+  "bin": "bin/index.mjs",
15
+  "license": "MIT",
16
+  "keywords": [
17
+    "violin",
18
+    "chord",
19
+    "chords",
20
+    "music"
21
+  ]
22
+}