commit a44300eb8e15cea892c5542df86fdf7a4109a7f8
parent 65edd010d82cf93469ea6be773528e7ee4744017
Author: nibo <nibo@relim.de>
Date: Mon, 2 Dec 2024 19:39:07 +0100
parser: add chord diagrams
Diffstat:
6 files changed, 307 insertions(+), 5 deletions(-)
diff --git a/chord_diagram.c b/chord_diagram.c
@@ -9,7 +9,7 @@
#include "chord_diagram.h"
#include "diagrams.h"
-bool
+static bool
text_show(
pdfio_stream_t *stream,
const char *text,
@@ -503,6 +503,15 @@ string_diagram_draw(
return true;
}
+static struct KeyboardDiagram *
+keyboard_diagram_new(void)
+{
+ struct KeyboardDiagram *d = emalloc(sizeof(struct KeyboardDiagram));
+ d->name = NULL;
+ memset(d->keys, -2, sizeof(d->keys));
+ return d;
+}
+
static void
keyboard_diagram_free(struct KeyboardDiagram *d)
{
@@ -510,6 +519,20 @@ keyboard_diagram_free(struct KeyboardDiagram *d)
free(d);
}
+struct ChordDiagram *
+chord_diagram_new(bool is_string_instrument)
+{
+ struct ChordDiagram *d = emalloc(sizeof(struct ChordDiagram));
+ if (is_string_instrument) {
+ d->is_string_instrument = true;
+ d->u.sd = string_diagram_new();
+ } else {
+ d->is_string_instrument = false;
+ d->u.kd = keyboard_diagram_new();
+ }
+ return d;
+}
+
void
chord_diagram_free(struct ChordDiagram *d)
{
@@ -591,3 +614,34 @@ chord_diagram_draw(
}
return true;
}
+
+#ifdef DEBUG
+void
+debug_chord_diagram_print(struct ChordDiagram *diagram)
+{
+ int i;
+ printf("---- CHORD DIAGRAM BEGIN ----\n");
+ if (diagram->is_string_instrument) {
+ printf("name: %s\n", diagram->u.sd->name);
+ printf("base-fret: %d\n", diagram->u.sd->base_fret);
+ printf("frets: ");
+ for (i = 0; i<12; i++) {
+ printf("%d ", diagram->u.sd->frets[i]);
+ }
+ printf("\n");
+ printf("fingers: ");
+ for (i = 0; i<12; i++) {
+ printf("%d ", diagram->u.sd->fingers[i]);
+ }
+ printf("\n");
+ } else {
+ printf("name: %s\n", diagram->u.kd->name);
+ printf("keys: ");
+ for (i = 0; i<24; i++) {
+ printf("%d ", diagram->u.kd->keys[i]);
+ }
+ printf("\n");
+ }
+ printf("---- CHORD DIAGRAM END ------\n");
+}
+#endif /* DEBUG */
diff --git a/chord_diagram.h b/chord_diagram.h
@@ -31,9 +31,9 @@ struct ChordDiagram {
} u;
};
+struct ChordDiagram *chord_diagram_new(bool is_string_instrument);
void chord_diagram_free(struct ChordDiagram *d);
void chord_diagrams_free(struct ChordDiagram **diagrams);
bool chord_diagram_draw(pdfio_stream_t *stream, struct ChordDiagram *diagram, double x, double y, double width);
struct ChordDiagram **chord_diagrams_create(struct Config *config, struct ChoChord ***chords);
-
-bool text_show( pdfio_stream_t *stream, const char *text, double x, double y);
+void debug_chord_diagram_print(struct ChordDiagram *diagram);
diff --git a/chordpro.c b/chordpro.c
@@ -11,6 +11,7 @@
#include "chordpro.h"
#include "config.h"
#include "util.h"
+#include "chord_diagram.h"
static const char *font_families[] = { "normal", "sans", "serif", "monospace", "empty" };
static const char *font_styles[] = { "normal", "oblique", "italic", "empty" };
@@ -2629,6 +2630,218 @@ cho_image_tag_parse(struct Attr **attrs)
return image;
}
+static
+int8_t
+char_to_positive_int(char c)
+{
+ if (c > '0' && c <= '9') {
+ return c - 48;
+ }
+ return -1;
+}
+
+static struct ChordDiagram *
+cho_chord_diagram_parse(const char *str)
+{
+ struct ChordDiagram *diagram = NULL;
+ enum ChordDiagramState state = CDS_NAME;
+ enum ChordDiagramContent current_content = CDC_EMPTY;
+ enum ChordDiagramContent future_content = CDC_EMPTY;
+ char c;
+ char name[20];
+ char option[10];
+ char key[3];
+ int n = 0;
+ int o = 0;
+ int k = 0;
+ int f = 0;
+ int i;
+ int fret_count = 0;
+ int finger_count = 0;
+ int8_t number = -2;
+ long l;
+ for (i = 0; str[i] != 0; i++) {
+ c = str[i];
+ // printf("c '%c' state '%d'\n", c, state);
+ switch (state) {
+ case CDS_NAME:
+ if (is_whitespace(c)) {
+ if (n == 0) {
+ break;
+ }
+ name[n] = 0;
+ state = CDS_OPTION_NAME;
+ break;
+ }
+ if (n > 18) {
+ cho_log(LOG_ERR, "Chord name in chord diagram is too long.");
+ return NULL;
+ }
+ name[n] = c;
+ n++;
+ break;
+ case CDS_OPTION_NAME:
+ CDS_OPTION_NAME_LABEL:
+ if (is_whitespace(c)) {
+ if (o == 0) {
+ break;
+ }
+ option[o] = 0;
+ if (!strcmp(option, "base-fret")) {
+ state = CDS_BASE_FRET;
+ future_content = CDC_STRING;
+ } else
+ if (!strcmp(option, "frets")) {
+ state = CDS_FRETS;
+ future_content = CDC_STRING;
+ } else
+ if (!strcmp(option, "fingers")) {
+ state = CDS_FINGERS;
+ future_content = CDC_STRING;
+ } else
+ if (!strcmp(option, "keys")) {
+ state = CDS_KEYS;
+ future_content = CDC_KEYBOARD;
+ } else {
+ cho_log(LOG_ERR, "Invalid option '%s' in define directive.", option);
+ return NULL;
+ }
+ memset(option, 0, o);
+ o = 0;
+ if (current_content == CDC_EMPTY) {
+ current_content = future_content;
+ switch (future_content) {
+ case CDC_STRING:
+ diagram = chord_diagram_new(true);
+ diagram->u.sd->name = strdup(name);
+ break;
+ case CDC_KEYBOARD:
+ diagram = chord_diagram_new(false);
+ diagram->u.kd->name = strdup(name);
+ break;
+ }
+ }
+ break;
+ }
+ if (o > 8) {
+ cho_log(LOG_ERR, "Option in chord diagram is too long.");
+ return NULL;
+ }
+ option[o] = c;
+ o++;
+ break;
+ case CDS_BASE_FRET:
+ if (is_whitespace(c)) {
+ if (diagram->u.sd->base_fret == -2) {
+ break;
+ }
+ state = CDS_OPTION_NAME;
+ break;
+ }
+ number = char_to_positive_int(c);
+ if (number == -1) {
+ cho_log(LOG_ERR, "Invalid base-fret value '%c' in chord diagram.", c);
+ return NULL;
+ }
+ diagram->u.sd->base_fret = number;
+ state = CDS_OPTION_NAME;
+ break;
+ case CDS_FRETS:
+ if (is_whitespace(c)) {
+ break;
+ }
+ if (isalpha(c)) {
+ f = 0;
+ state = CDS_OPTION_NAME;
+ goto CDS_OPTION_NAME_LABEL;
+ }
+ number = char_to_positive_int(c);
+ if (number == -1) {
+ cho_log(LOG_ERR, "Invalid frets value '%c' in chord diagram.", c);
+ return NULL;
+ }
+ if (f > 11) {
+ cho_log(LOG_ERR, "Too many fret values in chord diagram.");
+ return NULL;
+ }
+ diagram->u.sd->frets[f] = number;
+ f++;
+ fret_count++;
+ break;
+ case CDS_FINGERS:
+ if (is_whitespace(c)) {
+ break;
+ }
+ if (isalpha(c)) {
+ f = 0;
+ state = CDS_OPTION_NAME;
+ goto CDS_OPTION_NAME_LABEL;
+ }
+ number = char_to_positive_int(c);
+ if (number == -1) {
+ cho_log(LOG_ERR, "Invalid fingers value '%c' in chord diagram.", c);
+ return NULL;
+ }
+ if (f > 11) {
+ cho_log(LOG_ERR, "Too many finger values in chord diagram.");
+ return NULL;
+ }
+ diagram->u.sd->fingers[f] = number;
+ f++;
+ finger_count++;
+ break;
+ case CDS_KEYS:
+ if (is_whitespace(c)) {
+ if (k == 0) {
+ break;
+ }
+ key[k] = 0;
+ k = 0;
+ l = str_to_number(key);
+ if (l == -1) {
+ cho_log(LOG_ERR, "Invalid number in keys in chord diagram.");
+ return NULL;
+ }
+ if (f > 23) {
+ cho_log(LOG_ERR, "Too many key values in chord diagram.");
+ return NULL;
+ }
+ diagram->u.kd->keys[f] = (int8_t)l;
+ f++;
+ break;
+ }
+ if (k > 1) {
+ cho_log(LOG_ERR, "Too high key value in chord diagram. '%d'", k);
+ printf("key '%s'\n", key);
+ return NULL;
+ }
+ key[k] = c;
+ k++;
+ break;
+ }
+ }
+ if (current_content == CDC_KEYBOARD) {
+ key[k] = 0;
+ if (strlen(key) > 0) {
+ l = str_to_number(key);
+ if (l == -1) {
+ cho_log(LOG_ERR, "Invalid number in keys in chord diagram.");
+ return NULL;
+ }
+ if (f > 23) {
+ cho_log(LOG_ERR, "Too many key values in chord diagram.");
+ return NULL;
+ }
+ diagram->u.kd->keys[f] = (int8_t)l;
+ }
+ }
+ if (current_content == CDC_STRING && fret_count != finger_count) {
+ cho_log(LOG_ERR, "The number of frets (%d) and fingers (%d) in the chord diagram must be equal.", fret_count, finger_count);
+ return NULL;
+ }
+ return diagram;
+}
+
static struct ChoText *
cho_text_new(void)
{
@@ -3412,6 +3625,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config)
struct ChoChord *tmp_chord;
struct ChoSection *chorus;
struct ChoImage *image;
+ struct ChordDiagram *diagram;
while (feof(fp) == 0) {
read = fread(&buf, 1, 1, fp);
if (read == 1) {
@@ -3716,7 +3930,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config)
th--;
break;
case DEFINE:
- cho_log(LOG_INFO, "Guys, we have a define here.");
+ cho_log(LOG_WARN, "Ignoring chord directive '%s' because it has no value.", directive_name);
break;
}
break;
@@ -4059,7 +4273,9 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config)
th++;
break;
case DEFINE:
- cho_log(LOG_INFO, "Guys, we have a define with a value here.");
+ diagram = cho_chord_diagram_parse(directive_value);
+ debug_chord_diagram_print(diagram);
+ chord_diagram_free(diagram);
break;
}
break;
diff --git a/chordpro.h b/chordpro.h
@@ -12,6 +12,21 @@
#define URL_MAX_LEN 2000
#define FONT_NAME_MAX 100
+enum ChordDiagramState {
+ CDS_NAME,
+ CDS_OPTION_NAME,
+ CDS_BASE_FRET,
+ CDS_FRETS,
+ CDS_FINGERS,
+ CDS_KEYS
+};
+
+enum ChordDiagramContent {
+ CDC_EMPTY,
+ CDC_STRING,
+ CDC_KEYBOARD
+};
+
enum MetadataDirective {
TITLE,
SUBTITLE
diff --git a/util.c b/util.c
@@ -7,6 +7,7 @@
#include <sys/stat.h>
#include <errno.h>
#include <assert.h>
+#include <limits.h>
#include "util.h"
void *
@@ -240,6 +241,21 @@ strs_free(char **strs)
free(strs);
}
+long
+str_to_number(const char *str)
+{
+ long n;
+ char *endptr;
+ n = strtol(str, &endptr, 10);
+ if (str == endptr) {
+ return -1;
+ }
+ if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE) {
+ return -1;
+ }
+ return n;
+}
+
enum FileType
file_type(const char *path)
{
diff --git a/util.h b/util.h
@@ -50,6 +50,7 @@ void strs_free(char **strs);
bool strs_has(char **strs, const char *str);
void strs_add(char ***strs, const char *str);
int str_compare(const char *a, const char *b);
+long str_to_number(const char *str);
enum FileType file_type(const char *path);
char *file_extension_replace_or_add(const char *old, const char *extension);