/* chordops.c * * functions which manipulate chords * For denemo, a gtk+ frontend to Lilypond, the GNU music typesetter * * (c) 2000-2005 Matthew Hiller, Adam Tee * */ #include #include #include #include "chordops.h" #include "utils.h" /* Calculates the height of a notehead */ static void calcheight (gpointer data, gpointer user_data) { note *thenote = (note *) data; gint dclef = GPOINTER_TO_INT (user_data); thenote->y = calculateheight (thenote->mid_c_offset, dclef); } /** * Changes the position of the chord when a new clef * is selected * */ void newclefify (DenemoObject * thechord, gint dclef) { g_list_foreach (((chord *) thechord->object)->tones, calcheight, GINT_TO_POINTER (dclef)); ((chord *) thechord->object)->highesty = calculateheight (((chord *) thechord->object)->highestpitch, dclef); ((chord *) thechord->object)->lowesty = calculateheight (((chord *) thechord->object)->lowestpitch, dclef); } /** * This function goes through a chord and checks to see how its notes are * laid out, and if it will have to push the display of any tones to the * "wrong" side of the staff as a result */ void findreversealigns (DenemoObject * thechord) { GList *current; GList *previous; note *curnote; note *prevnote; ((chord *) thechord->object)->is_reversealigned = FALSE; if (((chord *) thechord->object)->tones) if (((chord *) thechord->object)->is_stemup) { /* note clusters are painted left-right from bottom to top */ previous = ((chord *) thechord->object)->tones; current = previous->next; prevnote = (note *) previous->data; prevnote->reversealign = FALSE; for (; current; previous = current, prevnote = curnote, current = current->next) { curnote = (note *) current->data; if (prevnote->mid_c_offset == curnote->mid_c_offset - 1) { ((chord *) thechord->object)->is_reversealigned = TRUE; curnote->reversealign = !prevnote->reversealign; } else curnote->reversealign = FALSE; } /* End for */ } else { /* the stem's down * note clusters are painted right-left from top to bottom */ previous = g_list_last (((chord *) thechord->object)->tones); current = previous->prev; prevnote = (note *) previous->data; prevnote->reversealign = FALSE; for (; current; previous = current, prevnote = curnote, current = current->prev) { curnote = (note *) current->data; if (prevnote->mid_c_offset == curnote->mid_c_offset + 1) { curnote->reversealign = !prevnote->reversealign; ((chord *) thechord->object)->is_reversealigned = TRUE; } else curnote->reversealign = FALSE; } /* End for */ } /* End else */ setpixelmin (thechord); } /** * Allocate new chord from the heap * and do the basic initialisation * */ DenemoObject * newchord (gint baseduration, gint numdots, int tied) { DenemoObject *thechord = (DenemoObject *) g_malloc0 (sizeof (DenemoObject)); chord *newchord = (chord *) g_malloc0 (sizeof (chord)); thechord->type = CHORD; thechord->isinvisible = FALSE; newchord->tones = NULL; newchord->dynamics = NULL; newchord->highestpitch = G_MININT; newchord->lowestpitch = G_MAXINT; newchord->baseduration = baseduration; newchord->numdots = numdots; newchord->sum_mid_c_offset = 0; newchord->numtones = 0; newchord->is_tied = tied; newchord->is_reversealigned = FALSE; newchord->slur_begin_p = FALSE; newchord->slur_end_p = FALSE; newchord->crescendo_begin_p = FALSE; newchord->crescendo_end_p = FALSE; newchord->diminuendo_begin_p = FALSE; newchord->diminuendo_end_p = FALSE; newchord->hasanacc = FALSE; newchord->is_grace = FALSE; newchord->struck_through = FALSE; newchord->has_dynamic = FALSE; newchord->is_highlighted = FALSE; newchord->is_syllable = FALSE; newchord->center_lyric = FALSE; newchord->lyric = NULL; newchord->figure = NULL; newchord->ornamentlist = NULL; thechord->object = newchord; set_basic_numticks (thechord); #ifdef DEBUG printf ("Chord %d \n", ((chord *) (thechord->object))->baseduration); #endif return thechord; } /** * Set the invisible flag for the DenemoObject * @param thechord object to make invisible * @return the chord */ DenemoObject * hidechord (DenemoObject * thechord) { thechord->isinvisible = TRUE; return thechord; } /** * adds an ornament to the given note * @param obj the DenemoObject to add the ornament to. * @param orn the ornament type to add */ void addornament (DenemoObject * obj, Ornament orn) { if (obj && obj->type == CHORD && ((chord *) obj->object)->tones) ((chord *) obj->object)->ornamentlist = insert_ornament_list (orn, ((chord *) obj->object)->ornamentlist); } /** * compare current note with pitch to be added * * if equal return FALSE(0) else return TRUE (1) */ static gint findcomparefunc (gconstpointer a, gconstpointer b) { const note *anote = (note *) a; const int bnum = GPOINTER_TO_INT (b); if (anote->mid_c_offset == bnum) return 0; /* Identical */ else return 1; /* Not identical */ } /** * Compare two notes * used for sorting the currentchords * note list */ static gint insertcomparefunc (gconstpointer a, gconstpointer b) { const note *anote = (note *) a; const note *bnote = (note *) b; return anote->mid_c_offset - bnote->mid_c_offset; } /** * Add tone to the current chord * The tone will not get added if it is * present already */ void addtone (DenemoObject * thechord, gint mid_c_offset, gint enshift, gint dclef) { note *newnote; if (!g_list_find_custom (((chord *) thechord->object)->tones, GINT_TO_POINTER (mid_c_offset), findcomparefunc)) { /* A-ha! The note isn't already in the chord */ newnote = (note *) g_malloc0 (sizeof (note)); newnote->mid_c_offset = mid_c_offset; newnote->enshift = enshift; newnote->reversealign = FALSE; newnote->y = calculateheight (mid_c_offset, dclef); newnote->noteheadtype = DENEMO_NORMAL_NOTEHEAD; ((chord *) thechord->object)->tones = g_list_insert_sorted (((chord *) thechord->object)->tones, newnote, insertcomparefunc); if (mid_c_offset > ((chord *) thechord->object)->highestpitch) { ((chord *) thechord->object)->highestpitch = mid_c_offset; ((chord *) thechord->object)->highesty = calculateheight (mid_c_offset, dclef); } if (mid_c_offset < ((chord *) thechord->object)->lowestpitch) { ((chord *) thechord->object)->lowestpitch = mid_c_offset; ((chord *) thechord->object)->lowesty = calculateheight (mid_c_offset, dclef); } ((chord *) thechord->object)->sum_mid_c_offset += mid_c_offset; ((chord *) thechord->object)->numtones++; } } /** * This function finds the node of the closest chord tone to n; in the * case of equally distant chord tones, it'll select the higher tones * of the two */ /* I don't think that I could have quite done this with a g_list_find_custom */ GList * findclosest (GList * tones, gint n) { GList *cur_tnode = tones; GList *next_tnode; note *cur_tone; note *next_tone; gint distance_from_cur; gint distance_from_next; if (!cur_tnode) return NULL; for (next_tnode = cur_tnode->next;; cur_tnode = next_tnode, next_tnode = cur_tnode->next) { cur_tone = (note *) cur_tnode->data; if (n <= cur_tone->mid_c_offset || !next_tnode) /* Aha! We have no other options */ return cur_tnode; else { next_tone = (note *) next_tnode->data; if (cur_tone->mid_c_offset < n && n < next_tone->mid_c_offset) { distance_from_cur = n - cur_tone->mid_c_offset; distance_from_next = next_tone->mid_c_offset - n; if (distance_from_cur < distance_from_next) return cur_tnode; else return next_tnode; } } } /* End for loop */ } /** * Remove tone from current chord * */ void removetone (DenemoObject * thechord, gint mid_c_offset, gint dclef) { GList *tnode; /* Tone node to remove */ note *tone; tnode = findclosest (((chord *) thechord->object)->tones, mid_c_offset); if (tnode) { tone = (note *) tnode->data; if (!tnode->next) /* That is, we're removing the highest pitch */ if (tnode->prev) { ((chord *) thechord->object)->highestpitch = ((note *) tnode->prev->data)->mid_c_offset; ((chord *) thechord->object)->highesty = calculateheight (((chord *) thechord->object)->highestpitch, dclef); } else { ((chord *) thechord->object)->highestpitch = G_MININT; /* Had to take care of this somewhere */ ((chord *) thechord->object)->is_tied = FALSE; } if (!tnode->prev) /* That is, we're removing the lowest pitch */ if (tnode->next) { ((chord *) thechord->object)->lowestpitch = ((note *) tnode->next->data)->mid_c_offset; ((chord *) thechord->object)->lowesty = calculateheight (((chord *) thechord->object)->lowestpitch, dclef); } else ((chord *) thechord->object)->lowestpitch = G_MAXINT; ((chord *) thechord->object)->sum_mid_c_offset -= tone->mid_c_offset; /* Now that we no longer need any info in tnode or tone, * actually free stuff */ g_free (tone); ((chord *) thechord->object)->tones = g_list_remove_link (((chord *) thechord->object)->tones, tnode); g_list_free_1 (tnode); } } /** * Alter the pitch of the currentchord by setting * the accidental value */ void shiftpitch (DenemoObject * thechord, gint mid_c_offset, gint is_sharpening) { GList *tnode; /* Tone node to inflect */ note *tone; tnode = findclosest (((chord *) thechord->object)->tones, mid_c_offset); if (tnode) { tone = (note *) tnode->data; if (is_sharpening) tone->enshift = MIN (tone->enshift + 1, 2); else tone->enshift = MAX (tone->enshift - 1, -2); } } /** * Change the duration of the current chord * */ void changedur (DenemoObject * thechord, gint baseduration, gint numdots) { ((chord *) thechord->object)->baseduration = baseduration; ((chord *) thechord->object)->numdots = numdots; set_basic_numticks (thechord); } /** * Set the number of dots on the chord * */ void changenumdots (DenemoObject * thechord, gint number) { ((chord *) thechord->object)->numdots = MAX (((chord *) thechord->object)->numdots + number, 0); set_basic_numticks (thechord); } /** * Free the current chord */ void freechord (DenemoObject * thechord) { g_list_foreach (((chord *) thechord->object)->tones, freeit, NULL); g_list_free (((chord *) thechord->object)->tones); g_free (thechord); } /** * Clone the current chord * used in the cut/copy/paste routine */ DenemoObject * clone_chord (DenemoObject * thechord) { DenemoObject *ret = (DenemoObject *) g_malloc0 (sizeof (DenemoObject)); GList *curtone; note *newnote; chord *clonedchord = (chord *) g_malloc0 (sizeof (chord)); /* I'd use a g_list_copy here, only that won't do the deep copy of * the list data that I'd want it to */ memcpy ((DenemoObject *) ret, (DenemoObject *) thechord, sizeof (DenemoObject)); ret->object = NULL; /* This has to be done as the object union has been removed. * A gpointer doesn't know the type it is so have to explictly * copy the object */ memcpy ((chord *) clonedchord, (chord *) thechord->object, sizeof (chord)); if (!((chord *) thechord->object)->dynamics) clonedchord->dynamics = NULL; else clonedchord->dynamics = ((chord *) thechord->object)->dynamics; clonedchord->tones = NULL; for (curtone = ((chord *) thechord->object)->tones; curtone; curtone = curtone->next) { newnote = (note *) g_malloc0 (sizeof (note)); memcpy (newnote, (note *) curtone->data, sizeof (note)); clonedchord->tones = g_list_append (clonedchord->tones, newnote); } ret->object = (chord *) clonedchord; /*#ifdef DEBUG g_print ("Chord Base dur %d \tCloned Note base dur %d\n", ((chord *) thechord->object)->baseduration, ((chord *) ret->object)->baseduration); #endif */ return ret; }