1 /* chord.c
2  *
3  * functions which manipulate chords
4  * For denemo, a gtk+ frontend to Lilypond, the GNU music typesetter
5  *
6  * (c) 2000-2005  Matthew Hiller, Adam Tee
7  *
8  */
9 
10 #include <math.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdint.h>
14 #include "command/object.h"
15 #include "command/chord.h"
16 #include "core/utils.h"
17 #include "audio/audiointerface.h"
18 #include "command/commandfuncs.h"
19 
20 /* Calculates the height of a notehead */
21 static void
calcheight(gpointer data,gpointer user_data)22 calcheight (gpointer data, gpointer user_data)
23 {
24   note *thenote = (note *) data;
25   gint dclef = GPOINTER_TO_INT (user_data);
26 
27   thenote->y = calculateheight (thenote->mid_c_offset, dclef);
28 }
29 
30 /**
31  *  Changes the position of the chord when a new clef
32  *  is selected
33  *
34  */
35 void
newclefify(DenemoObject * thechord)36 newclefify (DenemoObject * thechord)
37 {
38   if (((chord *) thechord->object)->notes == NULL)
39     return;
40   gint dclef = thechord->clef->type;
41   g_list_foreach (((chord *) thechord->object)->notes, calcheight, GINT_TO_POINTER (dclef));
42   ((chord *) thechord->object)->highesty = calculateheight (((chord *) thechord->object)->highestpitch, dclef);
43   ((chord *) thechord->object)->lowesty = calculateheight (((chord *) thechord->object)->lowestpitch, dclef);
44 }
45 
46 /**
47  *  This function goes through a chord and checks to see how its notes are
48  * laid out, and if it will have to push the display of any notes to the
49  * "wrong" side of the stem as a result
50  * the result is to set the is_reversealigned field of the chord
51  * and the reversealign field of the note* structures that make up the chord.
52  * thechord must have type CHORD on entry.
53  */
54 void
findreversealigns(DenemoObject * thechord)55 findreversealigns (DenemoObject * thechord)
56 {
57   GList *current;
58   GList *previous;
59   note *curnote;
60   note *prevnote;
61 
62   ((chord *) thechord->object)->is_reversealigned = FALSE;
63   if (((chord *) thechord->object)->notes)
64     {
65       if (((chord *) thechord->object)->is_stemup)
66         {
67           /* note clusters are painted left-right from bottom to top */
68           previous = ((chord *) thechord->object)->notes;
69           current = previous->next;
70           prevnote = (note *) previous->data;
71           prevnote->reversealign = FALSE;
72           for (; current; previous = current, prevnote = curnote, current = current->next)
73             {
74               curnote = (note *) current->data;
75               if ((prevnote->mid_c_offset == curnote->mid_c_offset - 1) || (prevnote->mid_c_offset == curnote->mid_c_offset))
76                 {
77                   ((chord *) thechord->object)->is_reversealigned = TRUE;
78                   curnote->reversealign = !prevnote->reversealign;
79                 }
80               else
81                 curnote->reversealign = FALSE;
82             }                   /* End for */
83         }
84       else
85         {
86           /* the stem's down
87            * note clusters are painted right-left from top to bottom */
88           previous = g_list_last (((chord *) thechord->object)->notes);
89           current = previous->prev;
90           prevnote = (note *) previous->data;
91           prevnote->reversealign = FALSE;
92           for (; current; previous = current, prevnote = curnote, current = current->prev)
93             {
94               curnote = (note *) current->data;
95               if ((prevnote->mid_c_offset == curnote->mid_c_offset + 1) || (prevnote->mid_c_offset == curnote->mid_c_offset))
96                 {
97                   curnote->reversealign = !prevnote->reversealign;
98                   ((chord *) thechord->object)->is_reversealigned = TRUE;
99                 }
100               else
101                 curnote->reversealign = FALSE;
102             }                   /* End for */
103         }                       /* End else */
104     }
105   setpixelmin (thechord);
106 }
107 
108 /**
109  * Allocate new chord from the heap
110  * and do the basic initialisation
111  *
112  */
113 DenemoObject *
newchord(gint baseduration,gint numdots,int tied)114 newchord (gint baseduration, gint numdots, int tied)
115 {
116   DenemoObject *thechord = (DenemoObject *) g_malloc0 (sizeof (DenemoObject));
117   chord *newchord = (chord *) g_malloc0 (sizeof (chord));
118   thechord->type = CHORD;
119   thechord->isinvisible = FALSE;
120 
121   newchord->notes = NULL;
122   newchord->dynamics = NULL;
123   newchord->highestpitch = G_MININT;
124   newchord->lowestpitch = G_MAXINT;
125   newchord->baseduration = baseduration;
126   newchord->numdots = numdots;
127   newchord->sum_mid_c_offset = 0;
128   newchord->numnotes = 0;
129   newchord->is_tied = tied;
130   newchord->is_reversealigned = FALSE;
131   newchord->slur_begin_p = FALSE;
132   newchord->slur_end_p = FALSE;
133 
134   newchord->crescendo_begin_p = FALSE;
135   newchord->crescendo_end_p = FALSE;
136   newchord->diminuendo_begin_p = FALSE;
137   newchord->diminuendo_end_p = FALSE;
138   newchord->hasanacc = FALSE;
139   newchord->is_grace = FALSE;
140   newchord->struck_through = FALSE;
141   newchord->has_dynamic = FALSE;
142   newchord->is_syllable = FALSE;
143   newchord->center_lyric = FALSE;
144   newchord->lyric = NULL;
145   newchord->figure = NULL;
146   thechord->object = newchord;
147 
148   set_basic_numticks (thechord);
149 
150   //g_debug ("Chord %d \n", ((chord *) (thechord->object))->baseduration);
151   return thechord;
152 }
153 
154 DenemoObject *
dnm_newchord(gint baseduration,gint numdots,int tied)155 dnm_newchord (gint baseduration, gint numdots, int tied)
156 {
157   return newchord (baseduration, numdots, tied);
158 }
159 
160 /**
161  * Set the invisible flag for the DenemoObject
162  * @param thechord object to make invisible
163  * @return the chord
164  */
165 DenemoObject *
hidechord(DenemoObject * thechord)166 hidechord (DenemoObject * thechord)
167 {
168   thechord->isinvisible = TRUE;
169 
170   return thechord;
171 }
172 
173 
174 /**
175  * compare current note with pitch to be added
176  *
177  * if equal return FALSE(0) else return TRUE (1)
178  */
179 #if 0
180 static gint
181 findcomparefunc (gconstpointer a, gconstpointer b)
182 {
183   const note *anote = (note *) a;
184   const int bnum = GPOINTER_TO_INT (b);
185 
186   if (anote->mid_c_offset == bnum)
187     return 0;                   /* Identical */
188   else
189     return 1;                   /* Not identical */
190 }
191 #endif
192 
193 /**
194  * Compare two notes
195  * used for sorting the currentchords
196  * note list
197  */
198 static gint
insertcomparefunc(gconstpointer a,gconstpointer b)199 insertcomparefunc (gconstpointer a, gconstpointer b)
200 {
201   const note *anote = (note *) a;
202   const note *bnote = (note *) b;
203 
204   return anote->mid_c_offset - bnote->mid_c_offset;
205 }
206 
207 /* modify the pitch of the note of a one note chord.
208    (for multi-note chords delete and add a note, do not use this).
209 FIXME repeated calls to calculateheight
210 */
211 void
modify_note(chord * thechord,gint mid_c_offset,gint enshift,gint dclef)212 modify_note (chord * thechord, gint mid_c_offset, gint enshift, gint dclef)
213 {
214   note *thenote;
215   if(thechord->notes == NULL)
216     {
217         g_critical ("modify_note called with rest");
218         gdk_beep();
219         return;
220     }
221   thenote = (note *) (thechord->notes->data);
222   thenote->mid_c_offset = mid_c_offset;
223   thenote->enshift = enshift;
224   thenote->y = calculateheight (mid_c_offset, dclef);
225   thechord->sum_mid_c_offset = mid_c_offset;    //Damned difficult to track this down - will not work if there are >1 notes in chord
226   thechord->highestpitch = mid_c_offset;
227   thechord->highesty = calculateheight (mid_c_offset, dclef);
228   thechord->lowestpitch = mid_c_offset;
229   thechord->lowesty = calculateheight (mid_c_offset, dclef);
230   if (Denemo.project->last_source == INPUTKEYBOARD)
231     {
232       DenemoStaff *curstaffstruct = (DenemoStaff *) Denemo.project->movement->currentstaff->data;
233       if (Denemo.prefs.immediateplayback)
234         {
235           play_notes (DEFAULT_BACKEND, curstaffstruct->midi_port, curstaffstruct->midi_channel, thechord);
236         }
237     }
238   displayhelper (Denemo.project);
239 }
240 
241 /* Allocate a new note structure initializing the fields
242  * caller must g_free
243  */
244 
245 static note *
new_note(gint mid_c_offset,gint enshift,gint dclef)246 new_note (gint mid_c_offset, gint enshift, gint dclef)
247 {
248   note *newnote;
249   if (enshift > 2)
250     enshift = 2;
251   if (enshift < -2)
252     enshift = -2;
253   newnote = (note *) g_malloc0 (sizeof (note));
254   newnote->mid_c_offset = mid_c_offset;
255   newnote->enshift = enshift;
256   newnote->y = calculateheight (mid_c_offset, dclef);
257   newnote->reversealign = FALSE;
258   newnote->noteheadtype = DENEMO_NORMAL_NOTEHEAD;
259   return newnote;
260 }
261 
262 /**
263  * Add note to the current chord
264  * The note *will* get added if it is
265  * present already
266  * return note added
267  */
268 note *
addtone(DenemoObject * thechord,gint mid_c_offset,gint enshift)269 addtone (DenemoObject * thechord, gint mid_c_offset, gint enshift)
270 { gint dclef = thechord->clef->type;
271   note *newnote = new_note (mid_c_offset, (Denemo.project->movement?Denemo.project->movement->pending_enshift:0) + enshift, dclef);
272   if(Denemo.project->movement) Denemo.project->movement->pending_enshift = 0;
273   ((chord *) thechord->object)->notes = g_list_insert_sorted (((chord *) thechord->object)->notes, newnote, insertcomparefunc);
274   if (mid_c_offset > ((chord *) thechord->object)->highestpitch)
275     {
276       ((chord *) thechord->object)->highestpitch = mid_c_offset;
277       ((chord *) thechord->object)->highesty = calculateheight (mid_c_offset, dclef);
278     }
279   if (mid_c_offset < ((chord *) thechord->object)->lowestpitch)
280     {
281       ((chord *) thechord->object)->lowestpitch = mid_c_offset;
282       ((chord *) thechord->object)->lowesty = calculateheight (mid_c_offset, dclef);
283     }
284   ((chord *) thechord->object)->sum_mid_c_offset += mid_c_offset;
285   ((chord *) thechord->object)->numnotes++;
286 
287   return newnote;
288 }
289 
290 void
dnm_addtone(DenemoObject * thechord,gint mid_c_offset,gint enshift)291 dnm_addtone (DenemoObject * thechord, gint mid_c_offset, gint enshift)
292 {
293   addtone (thechord, mid_c_offset, enshift);
294 }
295 
296 /**
297  * This function finds the node of the closest chord tone to n; in the
298  * case of equally distant chord notes, it'll select the higher notes
299  * of the two
300  * returns NULL in the case of no notes
301  */
302 
303 /* I don't think that I could have quite done this with a g_list_find_custom */
304 
305 static GList *
findclosest(GList * notes,gint n)306 findclosest (GList * notes, gint n)
307 {
308   GList *cur_tnode = notes;
309   GList *next_tnode;
310   note *cur_tone;
311   note *next_tone;
312   gint distance_from_cur;
313   gint distance_from_next;
314 
315   if (!cur_tnode)
316     return NULL;
317   for (next_tnode = cur_tnode->next;; cur_tnode = next_tnode, next_tnode = cur_tnode->next)
318     {
319       cur_tone = (note *) cur_tnode->data;
320       if (n <= cur_tone->mid_c_offset || !next_tnode)
321         /* Aha! We have no other options */
322         return cur_tnode;
323       else
324         {
325           next_tone = (note *) next_tnode->data;
326           if (cur_tone->mid_c_offset < n && n < next_tone->mid_c_offset)
327             {
328               distance_from_cur = n - cur_tone->mid_c_offset;
329               distance_from_next = next_tone->mid_c_offset - n;
330               if (distance_from_cur < distance_from_next)
331                 return cur_tnode;
332               else
333                 return next_tnode;
334             }
335         }
336     }                           /* End for loop */
337 }
338 
339 objnode *
nearestnote(DenemoObject * thechord,gint mid_c_offset)340 nearestnote (DenemoObject * thechord, gint mid_c_offset)
341 {
342   if (thechord && thechord->object && thechord->type == CHORD && ((chord *) thechord->object)->notes)
343     return findclosest (((chord *) thechord->object)->notes, mid_c_offset);
344   else
345     return NULL;
346 }
347 
348 /**
349  * Remove tone from current chord
350  * return TRUE if a note is removed,
351  * false if chord is a rest.
352  */
353 gboolean
removetone(DenemoObject * thechord,gint mid_c_offset)354 removetone (DenemoObject * thechord, gint mid_c_offset)
355 {
356   GList *tnode;                 /* Tone node to remove */
357   note *tone;
358   gint dclef = thechord->clef->type;
359   tnode = findclosest (((chord *) thechord->object)->notes, mid_c_offset);
360   if (tnode)
361     {
362       tone = (note *) tnode->data;
363       if (!tnode->next)         /* That is, we're removing the highest pitch */
364         {
365           if (tnode->prev)
366             {
367               ((chord *) thechord->object)->highestpitch = ((note *) tnode->prev->data)->mid_c_offset;
368               ((chord *) thechord->object)->highesty = calculateheight (((chord *) thechord->object)->highestpitch, dclef);
369             }
370           else
371             {
372               ((chord *) thechord->object)->highestpitch = G_MININT;
373               /* Had to take care of this somewhere - perhaps not needed - when passing through an edit with no notes...
374                  ((chord *) thechord->object)->is_tied = FALSE; */
375             }
376         }
377       if (!tnode->prev)         /* That is, we're removing the lowest pitch */
378         {
379           if (tnode->next)
380             {
381               ((chord *) thechord->object)->lowestpitch = ((note *) tnode->next->data)->mid_c_offset;
382               ((chord *) thechord->object)->lowesty = calculateheight (((chord *) thechord->object)->lowestpitch, dclef);
383             }
384           else
385             ((chord *) thechord->object)->lowestpitch = G_MAXINT;
386         }
387       ((chord *) thechord->object)->sum_mid_c_offset -= tone->mid_c_offset;
388 
389       /* Now that we no longer need any info in tnode or tone,
390        * actually free stuff */
391 
392       g_free (tone);
393       ((chord *) thechord->object)->notes = g_list_remove_link (((chord *) thechord->object)->notes, tnode);
394       g_list_free_1 (tnode);
395     }
396 
397   if (((chord *) thechord->object)->notes==NULL)
398      ((chord *) thechord->object)->highesty = ((chord *) thechord->object)->lowesty = 0;
399   return (gboolean) (intptr_t) tnode;
400 }
401 
402 //void
403 //removeallnotes(DenemoObject * thechord) {
404 //  while(removetone(thechord, 0, 0))
405 //      ;
406 //}
407 
408 /**
409  * Set the accidental of note closest to mid_c_offset in
410  * the currentchord accidental = -2 ... +2 for double flat to double sharp
411  */
412 void
changeenshift(DenemoObject * thechord,gint mid_c_offset,gint accidental)413 changeenshift (DenemoObject * thechord, gint mid_c_offset, gint accidental)
414 {
415   GList *tnode;                 /* note node to inflect */
416   note *tone;
417   tnode = findclosest (((chord *) thechord->object)->notes, mid_c_offset);
418   if (tnode)
419     {
420       tone = (note *) tnode->data;
421       tone->enshift = accidental;
422       displayhelper (Denemo.project);
423     }
424 }
425 
426 
427 /**
428  * Alter the pitch the  note closest to mid_c_offset in
429  * the currentchord by setting
430  * the accidental value
431  * one sharper or flatter subject to a limit of double sharp/flat
432  */
433 void
shiftpitch(DenemoObject * thechord,gint mid_c_offset,gint is_sharpening)434 shiftpitch (DenemoObject * thechord, gint mid_c_offset, gint is_sharpening)
435 {
436   GList *tnode;                 /* Tone node to inflect */
437   note *tone;
438 
439   tnode = findclosest (((chord *) thechord->object)->notes, mid_c_offset);
440   if (tnode)
441     {
442       tone = (note *) tnode->data;
443       if (is_sharpening)
444         tone->enshift = MIN (tone->enshift + 1, 2);
445       else
446         tone->enshift = MAX (tone->enshift - 1, -2);
447     }
448 }
449 
450 
451 /**
452  * Change the duration of the current chord
453  *
454  */
455 void
changedur(DenemoObject * thechord,gint baseduration,gint numdots)456 changedur (DenemoObject * thechord, gint baseduration, gint numdots)
457 {
458   gint current = ((chord *) thechord->object)->baseduration;
459   if (current < 0)
460     {
461       if (((chord *) thechord->object)->directives)
462         {
463           GList *g;
464           for (g = ((chord *) thechord->object)->directives; g; g = g->next)
465             {
466               DenemoDirective *directive = g->data;
467               if (directive->prefix && (0 == (directive->override & DENEMO_OVERRIDE_AFFIX)))
468                 {
469                   free_directive (directive);
470                   ((chord *) thechord->object)->directives = g_list_remove (((chord *) thechord->object)->directives, directive);
471                   break;        //there can only be one prefix replacing the duration, it would be tricky to remove more than one anyway as continuing the loop would be trick...
472                 }
473             }
474         }
475     }
476   ((chord *) thechord->object)->baseduration = baseduration;
477   ((chord *) thechord->object)->numdots = numdots;
478 
479   set_basic_numticks (thechord);
480 }
481 
482 /**
483  *  Set the number of dots on the chord
484  *
485  */
486 void
changenumdots(DenemoObject * thechord,gint number)487 changenumdots (DenemoObject * thechord, gint number)
488 {
489   ((chord *) thechord->object)->numdots = MAX (((chord *) thechord->object)->numdots + number, 0);
490   set_basic_numticks (thechord);
491   displayhelper (Denemo.project);
492   score_status(Denemo.project, TRUE);
493 }
494 
495 
496 
497 static void
freenote(gpointer thenote)498 freenote (gpointer thenote)
499 {
500   if (((note *) thenote)->directives)
501     {
502       free_directives (((note *) thenote)->directives);
503       //g_list_free(thenote->directives);
504     }
505   g_free (thenote);
506 }
507 
508 
509 /**
510  * Free the current chord
511  */
512 void
freechord(DenemoObject * thechord)513 freechord (DenemoObject * thechord)
514 {
515   g_list_foreach (((chord *) thechord->object)->notes, (GFunc) freenote, NULL);
516   g_list_free (((chord *) thechord->object)->notes);
517   g_list_free (((chord *) thechord->object)->dynamics);
518   if (((chord *) thechord->object)->lyric)
519     g_string_free (((chord *) thechord->object)->lyric, FALSE); //FIXME memory leak????
520   /* tone_node does not belong to the chord but belongs instead to the pitch recognition system */
521   if (((chord *) thechord->object)->is_figure && ((chord *) thechord->object)->figure)
522     g_string_free (((chord *) thechord->object)->figure, FALSE);        //FIXME memory leak????
523 
524   if (((chord *) thechord->object)->directives)
525     {
526       free_directives (((chord *) thechord->object)->directives);
527       //g_list_free(((chord *) thechord->object)->directives);
528     }
529 //FIXME we should free thechord->directives too if scripts fail to delete them
530   g_free (thechord);
531 }
532 
533 
534 
535 /**
536  * Clone the current chord
537  * used in the cut/copy/paste routine
538  */
539 DenemoObject *
clone_chord(DenemoObject * thechord)540 clone_chord (DenemoObject * thechord)
541 {
542   DenemoObject *ret = (DenemoObject *) g_malloc0 (sizeof (DenemoObject));
543   GList *curtone;
544   note *newnote;
545   chord *curchord = (chord *) thechord->object;
546   chord *clonedchord = (chord *) g_malloc0 (sizeof (chord));
547   /* I'd use a g_list_copy here, only that won't do the deep copy of
548    * the list data that I'd want it to */
549   memcpy ((DenemoObject *) ret, (DenemoObject *) thechord, sizeof (DenemoObject));
550 
551   ret->object = NULL;
552   ret->directives = NULL;       //currently the only pointers in DenemoObject
553   memcpy ((chord *) clonedchord, curchord, sizeof (chord));
554   clonedchord->directives = NULL;
555   clonedchord->dynamics = NULL;
556   clonedchord->tone_node = NULL;
557   if (curchord->figure)
558     clonedchord->figure = g_string_new (((GString *) curchord->figure)->str);
559   else
560     clonedchord->figure = NULL;
561   clonedchord->is_figure = curchord->is_figure;
562   if (curchord->fakechord)
563     clonedchord->fakechord = g_string_new (((GString *) curchord->fakechord)->str);
564   else
565     clonedchord->fakechord = NULL;
566   clonedchord->is_fakechord = curchord->is_fakechord;
567 
568 
569   clonedchord->lyric = NULL;
570 
571 /*   GList *g = curchord->directives; */
572 /*   for(;g;g=g->next) { */
573 /*     DenemoDirective *directive = (DenemoDirective *)g->data; */
574 /*     if(directive) */
575 /*       clonedchord->directives = g_list_append(clonedchord->directives, clone_directive(directive)); */
576 /*     else */
577 /*       g_warning("A Chord Directive list with NULL directive"); */
578 /*   } */
579 
580   clonedchord->directives = clone_directives (curchord->directives);
581 
582   clonedchord->notes = NULL;
583   for (curtone = ((chord *) thechord->object)->notes; curtone; curtone = curtone->next)
584     {
585       newnote = (note *) g_malloc0 (sizeof (note));
586       note *curnote = (note *) curtone->data;
587       memcpy (newnote, curnote, sizeof (note));
588       newnote->directives = clone_directives (curnote->directives);
589       clonedchord->notes = g_list_append (clonedchord->notes, newnote);
590     }
591   ret->object = (chord *) clonedchord;
592   /*
593      g_debug ("Chord Base dur %d \tCloned Note base dur %d\n",
594      ((chord *) thechord->object)->baseduration,
595      ((chord *) ret->object)->baseduration);
596      */
597   return ret;
598 }
599