X-Git-Url: https://git.cworth.org/git?p=scherzo;a=blobdiff_plain;f=score.c;fp=score.c;h=0a6016bcb706785db4460460a37ebdde235a8563;hp=ecfc2352662194790ec40b39577146fca64c08ee;hb=54d2b1cb13bf52df22c93e58377a00b757644b94;hpb=aae6b7f55870ab7f9cd41f95e22231e66a673aea diff --git a/score.c b/score.c index ecfc235..0a6016b 100644 --- a/score.c +++ b/score.c @@ -25,6 +25,8 @@ #include "score.h" +#define ARRAY_SIZE(arr) ((int) (sizeof(arr) / sizeof(arr[0]))) + typedef struct score_note { score_staff_t *staff; @@ -76,6 +78,9 @@ struct score /* Full width of staff */ int width; + /* the pitch class of the current (diatonic) key */ + pitch_t key; + score_brace_t **braces; int num_braces; int brace_width; @@ -99,6 +104,9 @@ score_create (void *ctx) /* Just to have some nominal width. */ score->width = 1000; + /* Default to C, of course */ + score->key = PITCH_CLASS_LITERAL (C, NATURAL); + score->braces = NULL; score->num_braces = 0; @@ -127,6 +135,12 @@ score_set_width (score_t *score, int width) score->width = width; } +void +score_set_key (score_t *score, pitch_t key) +{ + score->key = key; +} + /* Returns in brace_width the width of the brace */ static void _draw_brace (score_t *score, cairo_t *cr, @@ -255,7 +269,12 @@ _draw_chord (score_t *score, cairo_t *cr, cairo_restore (cr); } -static void +/* Draw 'note' with accidental (if not NATURAL) at its correct + * position on 'staff'. + * + * Returns the width of the drawn glyphs. + */ +static double _draw_note (score_t *score, cairo_t *cr, score_staff_t *staff, score_note_t *note) { @@ -324,33 +343,38 @@ _draw_note (score_t *score, cairo_t *cr, note_glyph[0].x = - (extents.width + ACCIDENTAL_NOTE_SPACING); } - switch (note->duration) { - case SCORE_DURATION_1: - note_glyph[num_glyphs].index = 127; - break; - case SCORE_DURATION_2: - note_glyph[num_glyphs].index = 85; - break; - case SCORE_DURATION_4: - case SCORE_DURATION_8: - case SCORE_DURATION_16: - case SCORE_DURATION_32: - case SCORE_DURATION_64: - case SCORE_DURATION_128: - default: - note_glyph[num_glyphs].index = 84; - } + /* Support duration == 0 to draw accidental only */ + if (note->duration) + { + switch (note->duration) { + case SCORE_DURATION_1: + note_glyph[num_glyphs].index = 127; + break; + case SCORE_DURATION_2: + note_glyph[num_glyphs].index = 85; + break; + case SCORE_DURATION_4: + case SCORE_DURATION_8: + case SCORE_DURATION_16: + case SCORE_DURATION_32: + case SCORE_DURATION_64: + case SCORE_DURATION_128: + default: + note_glyph[num_glyphs].index = 84; + } + + note_glyph[num_glyphs].x = 0; + note_glyph[num_glyphs].y = score->space_height * line; - note_glyph[num_glyphs].x = 0; - note_glyph[num_glyphs].y = score->space_height * line; + num_glyphs++; + } - num_glyphs++; + cairo_glyph_extents (cr, note_glyph, num_glyphs, &extents); if (line < 0 || line > 4) { double offset, width; int i; - cairo_glyph_extents (cr, note_glyph, num_glyphs, &extents); offset = note_glyph[0].x + extents.x_bearing; width = extents.width; @@ -370,6 +394,147 @@ _draw_note (score_t *score, cairo_t *cr, cairo_show_glyphs (cr, note_glyph, num_glyphs); cairo_restore (cr); + + return extents.width; +} + +/* Draw the accidental from 'pitch' only (no notehead) at the correct + * position as if drawing a note at 'pitch'. + * + * Returns the width of the drawn glyph. + */ +static double +_draw_accidental (score_t *score, + cairo_t *cr, + score_staff_t *staff, + pitch_t pitch) +{ + score_note_t note; + + note.staff = staff; + note.pitch = pitch; + + /* A duration of 0 indicates to draw only the accidental. */ + note.duration = 0; + + note.color.r = 0.0; + note.color.g = 0.0; + note.color.b = 0.0; + + return _draw_note (score, cr, staff, ¬e); +} + +static void +_draw_sharps_key_signature (score_t *score, cairo_t *cr, + score_staff_t *staff, int num_sharps) +{ + pitch_t pitch; + double width; + int i; + + /* These octave numbers are correct for treble clef. For bass + * clef, subtract two. + */ + pitch_t sharps_order[] = { + PITCH_LITERAL (F, SHARP, 5), + PITCH_LITERAL (C, SHARP, 5), + PITCH_LITERAL (G, SHARP, 5), + PITCH_LITERAL (D, SHARP, 5), + PITCH_LITERAL (A, SHARP, 4), + PITCH_LITERAL (E, SHARP, 5), + PITCH_LITERAL (B, SHARP, 4) + }; + + for (i = 0; i < num_sharps; i++) { + pitch = sharps_order[i]; + + if (staff->clef == SCORE_CLEF_BASS) + pitch = pitch_lower_by_octaves (pitch, 2); + + width = _draw_accidental (score, cr, staff, pitch); + +#define KEY_SIGNATURE_ACCIDENTAL_SPACING (score->space_height * .15) + cairo_translate (cr, ceil (width + KEY_SIGNATURE_ACCIDENTAL_SPACING), 0); + } +} + +static void +_draw_flats_key_signature (score_t *score, cairo_t *cr, + score_staff_t *staff, int num_flats) +{ + pitch_t pitch; + double width; + int i; + + /* These octave numbers are correct for treble clef. For bass + * clef, subtract two. + */ + pitch_name_t flats_order[] = { + PITCH_LITERAL (B, FLAT, 4), + PITCH_LITERAL (E, FLAT, 5), + PITCH_LITERAL (A, FLAT, 4), + PITCH_LITERAL (D, FLAT, 5), + PITCH_LITERAL (G, FLAT, 4), + PITCH_LITERAL (C, FLAT, 5), + PITCH_LITERAL (F, FLAT, 4), + }; + + for (i = 0; i < num_flats; i++) { + pitch = flats_order[i]; + + if (staff->clef == SCORE_CLEF_BASS) + pitch = pitch_lower_by_octaves (pitch, 2); + + width = _draw_accidental (score, cr, staff, pitch); + + cairo_translate (cr, ceil (width + KEY_SIGNATURE_ACCIDENTAL_SPACING), 0); + } +} + +static void +_draw_key_signature (score_t *score, cairo_t *cr, + score_staff_t *staff, pitch_t key) +{ + int i; + + pitch_t sharp_keys[] = { + PITCH_CLASS_LITERAL (C, NATURAL), + PITCH_CLASS_LITERAL (G, NATURAL), + PITCH_CLASS_LITERAL (D, NATURAL), + PITCH_CLASS_LITERAL (A, NATURAL), + PITCH_CLASS_LITERAL (E, NATURAL), + PITCH_CLASS_LITERAL (B, NATURAL), + PITCH_CLASS_LITERAL (F, SHARP), + PITCH_CLASS_LITERAL (C, SHARP), + }; + + pitch_t flat_keys[] = { + PITCH_CLASS_LITERAL (C, NATURAL), + PITCH_CLASS_LITERAL (F, NATURAL), + PITCH_CLASS_LITERAL (B, FLAT), + PITCH_CLASS_LITERAL (E, FLAT), + PITCH_CLASS_LITERAL (A, FLAT), + PITCH_CLASS_LITERAL (D, FLAT), + PITCH_CLASS_LITERAL (G, FLAT), + PITCH_CLASS_LITERAL (C, FLAT) + }; + + for (i = 0; i < ARRAY_SIZE (sharp_keys); i++) { + if (sharp_keys[i] == key) { + _draw_sharps_key_signature (score, cr, staff, i); + return; + } + } + + for (i = 0; i < ARRAY_SIZE (flat_keys); i++) { + if (flat_keys[i] == key) { + _draw_flats_key_signature (score, cr, staff, i); + return; + } + } + + fprintf (stderr, "Internal error: Not a key: %s\n", pitch_string (key)); + exit (1); } static void @@ -378,6 +543,7 @@ _draw_staff (score_t *score, cairo_t *cr, { int i; cairo_glyph_t clef_glyph; + cairo_text_extents_t clef_extents; cairo_save (cr); @@ -387,6 +553,19 @@ _draw_staff (score_t *score, cairo_t *cr, cairo_set_font_size (cr, score->staff_height); + /* Draw staff lines */ + for (i = 0; i < 5; i++) { + cairo_move_to (cr, 0, i * score->space_height + score->line_width / 2.0); + cairo_rel_line_to (cr, staff_width, 0); + } + + cairo_set_line_width (cr, score->line_width); + + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ + cairo_stroke (cr); + + /* Draw the clef */ + /* XXX: The hard-coded glyph indices here are very ugly. We should * figure out how to lookup glyphs by name from this font. */ switch (staff->clef) { @@ -406,19 +585,15 @@ _draw_staff (score_t *score, cairo_t *cr, cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ cairo_show_glyphs (cr, &clef_glyph, 1); - /* Draw staff lines */ - for (i = 0; i < 5; i++) { - cairo_move_to (cr, 0, i * score->space_height + score->line_width / 2.0); - cairo_rel_line_to (cr, staff_width, 0); - } - - cairo_set_line_width (cr, score->line_width); + /* Make space for clef */ + cairo_glyph_extents (cr, &clef_glyph, 1, &clef_extents); - cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ - cairo_stroke (cr); +#define CLEF_KEY_SIGNATURE_SPACING (score->space_height * .75) + cairo_translate (cr, ceil (clef_extents.width + + CLEF_KEY_SIGNATURE_SPACING), 0); - /* Make space for clef before drawing notes */ - cairo_translate (cr, (int) (4 * score->space_height), 0); + /* Draw the key signature */ + _draw_key_signature (score, cr, staff, score->key); /* Draw chord symbols */ cairo_save (cr);