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