@hymnbook/abc 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,13 +8,20 @@ import {
8
8
  TuneObject,
9
9
  VoiceItem,
10
10
  VoiceItemNote
11
- } from "./abcTypes";
11
+ } from "./types";
12
12
  import { validate } from "./validation";
13
+ import { addInfoFieldsToMelody } from "./deparser";
13
14
 
14
15
  // See also https://abcnotation.com/examples
15
16
 
16
-
17
- export const parse = (abc: string): AbcSong | undefined => {
17
+ /**
18
+ * Generates an AbcSong object from an ABC notation string.
19
+ * This function does some extra processing to handle slurs and lyrics properly.
20
+ * If you do not want this, use `convertStringToAbcTune` directly.
21
+ *
22
+ * @param abc
23
+ */
24
+ export const parse = (abc: string): AbcSong => {
18
25
  // Remove comments
19
26
  abc = abc
20
27
  .replace(/%.*/g, "")
@@ -24,9 +31,6 @@ export const parse = (abc: string): AbcSong | undefined => {
24
31
  extractInfoFields(abc, song);
25
32
 
26
33
  const tuneObject = convertStringToAbcTune(abc);
27
- if (tuneObject === undefined) {
28
- return undefined;
29
- }
30
34
 
31
35
  // Get the first staff only (thus in case of multiple instrument play, only take the first instrument)
32
36
  song.clef = tuneObject.lines!![0].staff!![0].clef || song.clef;
@@ -118,6 +122,12 @@ const processSlursForLine = (line: VoiceItem[]) => {
118
122
  });
119
123
  };
120
124
 
125
+ /**
126
+ * Get an info/header field from an ABC notation string.
127
+ * @param abc
128
+ * @param field
129
+ * @param _default
130
+ */
121
131
  export const getField = (abc: string, field: string, _default?: string): (string | undefined) => {
122
132
  const result = abc.match(new RegExp("(^|\n) *\t*" + field + ":(.*)?"));
123
133
  if (result == null || result.length !== 3 || result[2] == null) {
@@ -126,6 +136,11 @@ export const getField = (abc: string, field: string, _default?: string): (string
126
136
  return result[2].trim();
127
137
  };
128
138
 
139
+ /**
140
+ * Extract info/header fields from an ABC notation string into an AbcSong object and return the remaining melody string.
141
+ * @param abc
142
+ * @param song
143
+ */
129
144
  export const extractInfoFields = (abc: string, song: AbcSong): string => {
130
145
  song.area = getField(abc, "A");
131
146
  song.book = getField(abc, "B");
@@ -152,6 +167,7 @@ export const extractInfoFields = (abc: string, song: AbcSong): string => {
152
167
  song.voice = getField(abc, "V");
153
168
  song.referenceNumber = getField(abc, "X", "1");
154
169
  song.transcription = getField(abc, "Z");
170
+
155
171
  return abc
156
172
  .replace(/%.*\n/g, "")
157
173
  .replace(/(^|\n) *\t*[ABCDFGHIKLMmNOPQRrSsTUVXZ]:.*/g, "")
@@ -160,7 +176,12 @@ export const extractInfoFields = (abc: string, song: AbcSong): string => {
160
176
  .replace(/\n*$/g, "");
161
177
  };
162
178
 
163
- export const extractNotesAndLyrics = (abc: string): NoteGroupInterface => {
179
+ /**
180
+ * Extract notes and lyrics from an ABC notation string as a single line for each.
181
+ * Note that you must first remove info/header fields before using this function.
182
+ * @param abc
183
+ */
184
+ export const squashNotesAndLyrics = (abc: string): NoteGroupInterface => {
164
185
  const notes: string[] = [];
165
186
  const lyrics: string[] = [];
166
187
  abc.split("\n")
@@ -176,40 +197,13 @@ export const extractNotesAndLyrics = (abc: string): NoteGroupInterface => {
176
197
  return {
177
198
  notes: notes.join(" "),
178
199
  lyrics: lyrics.join(" ")
179
- } as NoteGroupInterface;
180
- };
181
-
182
- export const addInfoFieldsToMelody = (song: AbcSong, abc: string): string => {
183
- let result = "";
184
- // See for following order of fields: https://abcnotation.com/wiki/abc:standard:v2.1#description_of_information_fields
185
- result += song.referenceNumber === undefined ? "" : "X:" + song.referenceNumber + "\n";
186
- result += song.title === undefined ? "" : "T:" + song.title + "\n";
187
- result += song.area === undefined ? "" : "A:" + song.area + "\n";
188
- result += song.book === undefined ? "" : "B:" + song.book + "\n";
189
- result += song.composer === undefined ? "" : "C:" + song.composer + "\n";
190
- result += song.discography === undefined ? "" : "D:" + song.discography + "\n";
191
- result += song.fileUrl === undefined ? "" : "F:" + song.fileUrl + "\n";
192
- result += song.group === undefined ? "" : "G:" + song.group + "\n";
193
- result += song.history === undefined ? "" : "H:" + song.history + "\n";
194
- result += song.instruction === undefined ? "" : "I:" + song.instruction + "\n";
195
- result += song.key === undefined ? "" : "K:" + song.key + "\n";
196
- result += song.unitNoteLength === undefined ? "" : "L:" + song.unitNoteLength + "\n";
197
- result += song.meter === undefined ? "" : "M:" + song.meter + "\n";
198
- result += song.macro === undefined ? "" : "m:" + song.macro + "\n";
199
- result += song.notes === undefined ? "" : "N:" + song.notes + "\n";
200
- result += song.origin === undefined ? "" : "O:" + song.origin + "\n";
201
- result += song.parts === undefined ? "" : "P:" + song.parts + "\n";
202
- result += song.tempo === undefined ? "" : "Q:" + song.tempo + "\n";
203
- result += song.rhythm === undefined ? "" : "R:" + song.rhythm + "\n";
204
- result += song.remark === undefined ? "" : "r:" + song.remark + "\n";
205
- result += song.source === undefined ? "" : "S:" + song.source + "\n";
206
- result += song.symbolLine === undefined ? "" : "s:" + song.symbolLine + "\n";
207
- result += song.userDefined === undefined ? "" : "U:" + song.userDefined + "\n";
208
- result += song.voice === undefined ? "" : "V:" + song.voice + "\n";
209
- result += song.transcription === undefined ? "" : "Z:" + song.transcription + "\n";
210
- return result + abc;
200
+ };
211
201
  };
212
202
 
203
+ /**
204
+ * Converts and validates an ABC notation string into a TuneObject.
205
+ * @param abc
206
+ */
213
207
  export const convertStringToAbcTune = (abc: string): TuneObject => {
214
208
  const objectArray: TuneObjectArray = ABCJS.parseOnly(abc);
215
209
  // Convert types
@@ -218,6 +212,9 @@ export const convertStringToAbcTune = (abc: string): TuneObject => {
218
212
  validate(object != null, "Tune object may not be null");
219
213
  validate(object.length > 0, "Tune object may not be empty");
220
214
  validate(object[0].lines != null, "Tune object lines may not be null");
215
+
216
+ object[0].lines = object[0].lines.filter(it => it.staff); // Remove empty lines without staff
217
+
221
218
  validate(object[0].lines.length > 0, "Tune object lines are empty");
222
219
  validate(object[0].lines[0].staff != null, "Staffs may not be null");
223
220
  validate(object[0].lines[0].staff!!.length > 0, "Staffs are empty");
@@ -246,8 +243,17 @@ const processAbcLyrics = (object: Array<TuneObject>) => {
246
243
  );
247
244
  };
248
245
 
249
- // Match each lyric line with each melody line
250
- export const combineMelodyAndLyrics = (melody: string, lyrics: string): string => {
246
+ /**
247
+ * Combine multi line lyrics line with a multi line melody into a single ABC notation string.
248
+ * @param melody
249
+ * @param lyrics
250
+ * @param options
251
+ */
252
+ export const combineMelodyAndLyrics = (
253
+ melody: string,
254
+ lyrics: string,
255
+ options: { trimLines?: boolean } = { trimLines: false },
256
+ ): string => {
251
257
  const song = new AbcSong();
252
258
  const rawMelody = extractInfoFields(melody, song);
253
259
 
@@ -255,6 +261,13 @@ export const combineMelodyAndLyrics = (melody: string, lyrics: string): string =
255
261
  .replaceAll(/\n+/g, "\n")
256
262
  .trim()
257
263
  .split("\n")
264
+ .map(it => it.trim())
265
+ .map(it => options.trimLines
266
+ ? it
267
+ .replaceAll(/(^y+|y+$)*/gi, "")
268
+ .replaceAll(/ *y* *(\|+]*) *y* */gi, " $1 ")
269
+ .trim()
270
+ : it);
258
271
  const lyricLines = lyrics
259
272
  .replaceAll(/\n+/g, "\n")
260
273
  .trim()
@@ -12,7 +12,7 @@ export type AbcType =
12
12
  | "bar_thick_thin"
13
13
  | "bar_right_repeat"
14
14
  | "bar_left_repeat"
15
- | "bar_double_repeat";
15
+ | "bar_dbl_repeat";
16
16
  export type AbcElementType = "note" | "bar";
17
17
  export type Clef =
18
18
  "treble"
@@ -99,6 +99,7 @@ export interface VoiceItemBar {
99
99
  type: AbcType;
100
100
  startChar: number;
101
101
  endChar: number;
102
+ startEnding?: string; // Labels the repeat section
102
103
  }
103
104
 
104
105
  export interface NoteProperties {
@@ -107,6 +108,8 @@ export interface NoteProperties {
107
108
  lyric?: AbcLyric[];
108
109
  chord?: AbcChord[];
109
110
  rest?: AbcRest;
111
+ startBeam?: boolean;
112
+ endBeam?: boolean;
110
113
  }
111
114
 
112
115
  export interface VoiceItemNote extends NoteProperties {
@@ -308,7 +311,7 @@ export class AbcSong {
308
311
  };
309
312
  }
310
313
 
311
- export interface NoteGroupInterface {
312
- notes: string,
314
+ export type NoteGroupInterface = {
315
+ notes: string
313
316
  lyrics: string
314
317
  }