From 7e2ab4eb3ab1fc670d9a0150996d682e7f55f183 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Thu, 22 Sep 2011 17:22:22 -0700 Subject: [PATCH] Restructure code to manually add staves, braces, and notes. Previously, the program was hard-coded to draw a grand staff, (two staves connected by a brace). Now, at the level of the score library, each staff and any braces are added manually and any set of staves and braces can be drawn, (the only the two-staves-connected-by-one-brace has been tested). There's also functionality for ading notes, though no notes are drawn yet. --- scherzo.c | 9 +++ score.c | 236 +++++++++++++++++++++++++++++++++++++++++++----------- score.h | 101 +++++++++++++++++++++++ 3 files changed, 299 insertions(+), 47 deletions(-) diff --git a/scherzo.c b/scherzo.c index 6a04de9..cccaa20 100644 --- a/scherzo.c +++ b/scherzo.c @@ -111,6 +111,8 @@ main (int argc, char *argv[]) GtkWidget *window; GtkWidget *drawing_area; scherzo_t scherzo; + score_staff_t *treble; + score_staff_t *bass; gtk_init (&argc, &argv); @@ -118,6 +120,13 @@ main (int argc, char *argv[]) scherzo.staff_height = 48; score_set_staff_height (scherzo.score, scherzo.staff_height); + score_add_brace (scherzo.score, 2); + treble = score_add_staff (scherzo.score, SCORE_CLEF_G); + bass = score_add_staff (scherzo.score, SCORE_CLEF_F); + + score_staff_add_note (treble, SCORE_BUILD_NOTE (F, 4, WHOLE)); + score_staff_add_note (bass, SCORE_BUILD_NOTE (F, 3, HALF)); + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); diff --git a/score.c b/score.c index 2900591..fb54b95 100644 --- a/score.c +++ b/score.c @@ -20,6 +20,20 @@ #include "score.h" +struct score_staff +{ + score_clef_t clef; + + score_note_t **notes; + int num_notes; +}; + +typedef struct score_brace +{ + int first_staff; + int num_staves; +} score_brace_t; + struct score { /* Height of a single staff */ @@ -33,16 +47,43 @@ struct score /* Full width of staff */ int width; + + score_brace_t **braces; + int num_braces; + int brace_width; + + score_staff_t **staves; + int num_staves; }; +typedef struct score_note +{ + score_pitch_t pitch; + int octave; + score_duration_t duration; +} score_note_t; + score_t * score_create (void *ctx) { score_t *score; score = talloc (ctx, score_t); + if (score == NULL) + return NULL; + + /* Also sets space_height and line_width */ score_set_staff_height (score, 24); + /* Just to have some nominal width. */ + score->width = 800; + + score->braces = NULL; + score->num_braces = 0; + + score->staves = NULL; + score->num_staves = 0; + return score; } @@ -65,14 +106,49 @@ score_set_width (score_t *score, int width) score->width = width; } -typedef enum score_clef +/* Returns in brace_width the width of the brace */ +static void +_draw_brace (score_t *score, cairo_t *cr, + score_brace_t *brace, int *brace_width) { - SCORE_CLEF_G, - SCORE_CLEF_F -} score_clef_t; + cairo_glyph_t brace_glyph; + cairo_text_extents_t brace_extents; + + cairo_save (cr); + + cairo_select_font_face (cr, "Gonville-Brace", 0, 0); + + /* XXX: This hard-coded glyph index is pretty ugly. We should + * figure out how to lookup the glyph we want, (though, as it + * turns out, this brace font pretty much just has numbered glyph + * names for different sizes, so it wouldn't be all that different + * than just the bare index here). */ + brace_glyph.index = 300; + brace_glyph.x = 0; + brace_glyph.y = score->staff_height * (brace->first_staff + (2 * brace->num_staves - 1) / 2.0) + 1; + + /* XXX: This font size (in conjunction with the glyph selection) + * is a rough guess at best. We should figure out how the brace + * font is intended to be used and actually measure to find the + * correctly-sized glyph. */ + cairo_set_font_size (cr, (score->staff_height * 3) / 3.85); + + cairo_glyph_extents (cr, &brace_glyph, 1, &brace_extents); + + /* Subtract space for brace itself */ + cairo_translate (cr, -brace_extents.x_bearing, 0); + + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ + cairo_show_glyphs (cr, &brace_glyph, 1); + + cairo_restore (cr); + + *brace_width = (int) -brace_extents.x_bearing; +} static void -_draw_staff (score_t *score, cairo_t *cr, score_clef_t clef) +_draw_staff (score_t *score, cairo_t *cr, + score_staff_t *staff, int staff_width) { int i; cairo_glyph_t glyph; @@ -85,7 +161,7 @@ _draw_staff (score_t *score, cairo_t *cr, score_clef_t 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 (clef) { + switch (staff->clef) { case SCORE_CLEF_G: default: glyph.index = 46; @@ -105,12 +181,12 @@ _draw_staff (score_t *score, cairo_t *cr, score_clef_t clef) cairo_rectangle (cr, score->line_width / 2.0, score->line_width / 2.0, - score->width - score->line_width, + staff_width - score->line_width, score->space_height * 4); for (i = 1; i < 4; i++) { cairo_move_to (cr, 0, i * score->space_height + score->line_width / 2.0); - cairo_rel_line_to (cr, score->width, 0); + cairo_rel_line_to (cr, staff_width, 0); } cairo_set_line_width (cr, score->line_width); @@ -121,65 +197,131 @@ _draw_staff (score_t *score, cairo_t *cr, score_clef_t clef) cairo_restore (cr); } -static void -_draw_grand_staff (score_t *score, cairo_t *cr) +void +score_draw (score_t *score, cairo_t *cr) { -#define BRACE_GLYPHS 1 - cairo_glyph_t brace; - cairo_text_extents_t brace_extents; - cairo_save (cr); - - /* Brace test */ - cairo_select_font_face (cr, "Gonville-Brace", 0, 0); - - /* XXX: This hard-coded glyph index is pretty ugly. We should - * figure out how to lookup the glyph we want, (though, as it - * turns out, this brace font pretty much just has numbered glyph - * names for different sizes, so it wouldn't be all that different - * than just the bare index here). */ - brace.index = 300; - brace.x = 0; - brace.y = (score->staff_height * 3) / 2 + 1; + int i; + int staff_width = score->width; - /* XXX: This font size (in conjunction with the glyph selection) - * is a rough guess at best. We should figure out how the brace - * font is intended to be used and actually measure to find the - * correctly-sized glyph. */ - cairo_set_font_size (cr, (score->staff_height * 3) / 3.85); + cairo_save (cr); - cairo_glyph_extents (cr, &brace, 1, &brace_extents); + if (score->num_braces) + { + int brace_width; - cairo_translate (cr, -brace_extents.x_bearing, 0); - score->width += brace_extents.x_bearing; + for (i = 0; i < score->num_braces; i++) + _draw_brace (score, cr, score->braces[i], &brace_width); - cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ - cairo_show_glyphs (cr, &brace, 1); + /* Subtract space for brace itself */ + cairo_translate (cr, brace_width, 0); + staff_width -= brace_width; - cairo_translate (cr, 2, 0); - score->width -= 2; + /* As well as some padding */ + cairo_translate (cr, 2, 0); + staff_width -= 2; + } /* Vertical lines at each end */ cairo_rectangle (cr, score->line_width / 2.0, score->line_width / 2.0, - score->width - score->line_width, + staff_width - score->line_width, score->staff_height * 3); cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */ cairo_set_line_width (cr, score->line_width); cairo_stroke (cr); - /* Top staff */ - _draw_staff (score, cr, SCORE_CLEF_G); - - /* Bottom staff */ - cairo_translate (cr, 0, score->staff_height * 2); - _draw_staff (score, cr, SCORE_CLEF_F); + for (i = 0; i < score->num_staves; i++) { + _draw_staff (score, cr, score->staves[i], staff_width); + cairo_translate (cr, 0, 2 * score->staff_height); + } cairo_restore (cr); } void -score_draw (score_t *score, cairo_t *cr) +score_add_brace (score_t *score, int staves) { - _draw_grand_staff (score, cr); + score_brace_t *brace; + + brace = talloc (score, score_brace_t); + if (brace == NULL) + return; + + brace->first_staff = score->num_staves; + brace->num_staves = staves; + + score->num_braces++; + score->braces = talloc_realloc (score, + score->braces, + score_brace_t*, + score->num_braces); + if (score->braces == NULL) { + score->num_braces = 0; + return; + } + + score->braces[score->num_braces - 1] = brace; + } + +score_staff_t * +score_add_staff (score_t *score, score_clef_t clef) +{ + score_staff_t *staff; + + staff = talloc (score, score_staff_t); + if (staff == NULL) + return NULL; + + staff->clef = clef; + + staff->notes = NULL; + staff->num_notes = 0; + + score->num_staves++; + score->staves = talloc_realloc (score, + score->staves, + score_staff_t*, + score->num_staves); + if (score->staves == NULL) { + score->num_staves = 0; + return NULL; + } + + score->staves[score->num_staves - 1] = staff; + + return staff; +} + +score_note_t * +score_staff_add_note (score_staff_t *staff, + score_pitch_t pitch, + int octave, + score_duration_t duration) +{ + score_note_t *note; + + note = talloc (staff, score_note_t); + if (note == NULL) + return NULL; + + note->pitch = pitch; + note->octave = octave; + note->duration = duration; + + staff->num_notes++; + staff->notes = talloc_realloc (staff, + staff->notes, + score_note_t*, + staff->num_notes); + if (staff->notes == NULL) { + staff->num_notes = 0; + return NULL; + } + + staff->notes[staff->num_notes - 1] = note; + + return note; +} + diff --git a/score.h b/score.h index 02b56e3..25d5583 100644 --- a/score.h +++ b/score.h @@ -25,6 +25,81 @@ #include typedef struct score score_t; +typedef struct score_staff score_staff_t; +typedef struct score_note score_note_t; + +typedef enum score_pitch +{ + SCORE_PITCH_Aff, + SCORE_PITCH_Af, + SCORE_PITCH_A, + SCORE_PITCH_As, + SCORE_PITCH_Ass, + + SCORE_PITCH_Bff, + SCORE_PITCH_Bf, + SCORE_PITCH_B, + SCORE_PITCH_Bs, + SCORE_PITCH_Bss, + + SCORE_PITCH_Cff, + SCORE_PITCH_Cf, + SCORE_PITCH_C, + SCORE_PITCH_Cs, + SCORE_PITCH_Css, + + SCORE_PITCH_Dff, + SCORE_PITCH_Df, + SCORE_PITCH_D, + SCORE_PITCH_Ds, + SCORE_PITCH_Dss, + + SCORE_PITCH_Eff, + SCORE_PITCH_Ef, + SCORE_PITCH_E, + SCORE_PITCH_Es, + SCORE_PITCH_Ess, + + SCORE_PITCH_Fff, + SCORE_PITCH_Ff, + SCORE_PITCH_F, + SCORE_PITCH_Fs, + SCORE_PITCH_Fss, + + SCORE_PITCH_Gff, + SCORE_PITCH_Gf, + SCORE_PITCH_G, + SCORE_PITCH_Gs, + SCORE_PITCH_Gss +} score_pitch_t; + +typedef enum score_duration +{ + SCORE_DURATION_WHOLE = 1, + SCORE_DURATION_1 = 1, + SCORE_DURATION_HALF = 2, + SCORE_DURATION_2 = 2, + SCORE_DURATION_QUARTER = 4, + SCORE_DURATION_4 = 4, + SCORE_DURATION_EIGHTH = 8, + SCORE_DURATION_8 = 8, + SCORE_DURATION_SIXTEENTH = 16, + SCORE_DURATION_16 = 16, + SCORE_DURATION_THIRTYSECOND = 32, + SCORE_DURATION_32 = 32, + SCORE_DURATION_SIXTYFOURTH = 64, + SCORE_DURATION_64 = 64, + SCORE_DURATION_ONEHUNDREDTWENTYEIGHTH = 128, + SCORE_DURATION_128 = 128 +} score_duration_t; + +#define SCORE_BUILD_NOTE(pitch, octave, duration) SCORE_PITCH_##pitch, (octave), SCORE_DURATION_##duration + +typedef enum score_clef +{ + SCORE_CLEF_G, + SCORE_CLEF_F +} score_clef_t; /* Allocate a new, empty score object, (with optional ctx as talloc * owner). If ctx is NULL, the caller should call talloc_free on the @@ -43,6 +118,32 @@ score_set_staff_height (score_t *score, int height); void score_set_width (score_t *score, int width); +/* Add a brace to the score, connecting the given number of staves. + * + * The staves to be connected are those that will next be added to the + * score. */ +void +score_add_brace (score_t *score, int staves); + +/* Add a new staff to the score */ +score_staff_t * +score_add_staff (score_t *score, score_clef_t clef); + +/* Add a note to a staff of the given pitch, octave, and duration. + * + * Octave numbers are ISO octave numbers [0:8], (so Octave 4 is from + * middle C to the B above middle C). + * + * Duration values can be symbolic (SCORE_DURATION_WHOLE, _QUARTER, + * _EIGHTH, etc.) or numerical as simply the denominator (WHOLE=1, + * QUARTER=4, EIGHTH=8, etc.) + */ +score_note_t * +score_staff_add_note (score_staff_t *staff, + score_pitch_t pitch, + int octave, + score_duration_t); + /* Draw the given score_t onto the given cairo_t. * * The caller can call cairo_translate before calling score_draw to -- 2.43.0