File Coverage

blib/lib/Music/Abc/DT.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Music::Abc::DT;
2              
3 1     1   27494 use 5.01400;
  1         5  
  1         46  
4 1     1   9 use strict;
  1         2  
  1         45  
5 1     1   6 use warnings FATAL => 'all';
  1         6  
  1         73  
6              
7             BEGIN {
8              
9 1     1   1134 use Data::Dumper;
  1         24979  
  1         87  
10 1     1   453 use Readonly;
  0            
  0            
11             use feature 'state'; #state variables are enabled
12             use Exporter 'import'; # gives you Exporter's import() method directly
13             use POSIX ();
14             use File::Temp ();
15             use List::MoreUtils qw{any};
16              
17             our $VERSION = '0.01';
18              
19             our %EXPORT_TAGS = (
20             'all' => [
21             qw( _broken_rhythm _head_par _length_header_dump _meter_calc _pscom_to_abc _slur_dump
22             _vover_to_abc _tuplet_to_abc _get_transformation _get_note_rest_bar_actuators
23             _get_null_info_clef_actuators _bar_dump _deco_dump _step_dump _get_chord_notes
24             _diatonic_interval _get_alter _get_chromatic_info _get_generic_info _get_ps
25             _get_specifier_from_generic_chromatic _interval_from_generic_and_chromatic _notes_to_chromatic
26             _notes_to_generic _notes_to_interval _convert_staff_distance_to_interval $brhythm @blen
27             $deco_tb %state_name )
28             ]
29             );
30              
31             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
32              
33             # If you are only exporting function names it is recommended to omit the ampersand, as the
34             # implementation is faster this way.
35             our @EXPORT =
36             qw( &dt &dt_string &toabc &get_meter &get_length &get_wmeasure &get_gchords &get_key &get_time
37             &get_time_ql &is_major_triad &is_minor_triad &is_dominant_seventh &get_chord_step &get_fifth
38             &get_third &get_seventh &root &find_consecutive_notes_in_measure &get_pitch_class
39             &get_pitch_name $c_voice $sym %voice_struct);
40              
41             use vars
42             qw( $deco_tb $in_grace $brhythm $gbr @blen $micro_tb $c_voice %voice_struct $c_tune $c_sym_ix
43             $c_abc $sym $c_bar %sym_name %state_name %info_name %STEPREF @key_shift @key_tonic $ly_st @clef_type
44             $toabc_called_outside $toabc_called_inside $GLOBAL $IMPLICIT_VOICE $QUARTER_LENGTH $FIRST_MEASURE);
45              
46             Readonly our $GLOBAL => 'global'; # identifies data that is applied to the entire score (voice independent)
47             Readonly our $IMPLICIT_VOICE => 0; # default voice
48             Readonly our $QUARTER_LENGTH => 384; # default value for quarter length (abcm2ps)
49             Readonly our $FIRST_MEASURE => 1; # default value for the first measure
50              
51             use constant { # info type
52             ABC_T_NULL => 0,
53             ABC_T_INFO => 1, # (first character of text gives the info type)
54             ABC_T_PSCOM => 2,
55             ABC_T_CLEF => 3,
56             ABC_T_NOTE => 4,
57             ABC_T_REST => 5,
58             ABC_T_BAR => 6,
59             ABC_T_EOLN => 7,
60             ABC_T_MREST => 8, # multi-measure rest
61             ABC_T_MREP => 9, # measure repeat
62             ABC_T_V_OVER => 10, # voice overlay
63             ABC_T_TUPLET => 11,
64             };
65              
66             use constant { # symbol state in file/tune
67             ABC_S_GLOBAL => 0, # global
68             ABC_S_HEAD => 1, # in header (after X:)
69             ABC_S_TUNE => 2, # in tune (after K:)
70             ABC_S_EMBED => 3 # embedded header (between [..])
71             };
72              
73              
74             use constant { # info flags
75             ABC_F_ERROR => 0x0001, # error around this symbol
76             ABC_F_INVIS => 0x0002, # invisible symbol
77             ABC_F_SPACE => 0x0004, # space before a note
78             ABC_F_STEMLESS => 0x0008, # note with no stem
79             ABC_F_LYRIC_START => 0x0010, # may start a lyric here
80             ABC_F_GRACE => 0x0020, # grace note
81             ABC_F_GR_END => 0x0040, # end of grace note sequence
82             ABC_F_SAPPO => 0x0080 # short appoggiatura
83             };
84              
85             use constant { # key mode
86             MAJOR => 7,
87             MINOR => 8,
88             BAGPIPE => 9 # bagpipe when >= 8
89             };
90              
91             use constant { # clef type
92             TREBLE => 0,
93             ALTO => 1,
94             BASS => 2,
95             PERC => 3
96             };
97              
98             use constant { # voice overlay
99             V_OVER_V => 0, # &
100             V_OVER_S => 1, # (&
101             V_OVER_E => 2 # &)
102             };
103              
104             # key signatures
105             use constant KEY_NAMES => qw(ionian dorian phrygian lydian mixolydian aeolian locrian major minor HP Hp);
106              
107             use constant { NONE => 'none' };
108             use constant { MAXVOICE => 32 }; # max number of voices
109             use constant { BASE_LEN => 1536 }; # basic note length (semibreve or whole note - same as MIDI)
110             use constant { DEFAULT_METER => '4/4' };
111             use constant { DEFAULT_LENGTH => '1/8' };
112              
113             use constant { # accidentals
114             A_NULL => 0, # none
115             A_SH => 1, # sharp
116             A_NT => 2, # natural
117             A_FT => 3, # flat
118             A_DS => 4, # double sharp
119             A_DF => 5 # double flat
120             };
121              
122              
123             use constant { # bar types
124             B_BAR => 1, # |
125             B_OBRA => 2, # [
126             B_CBRA => 3, # ]
127             B_COL => 4 # :
128             };
129              
130             use constant { # slur/tie types (3 bits)
131             SL_ABOVE => 0x01,
132             SL_BELOW => 0x02,
133             SL_AUTO => 0x03,
134             SL_DOTTED => 0x04 # (modifier bit)
135             };
136              
137             our ( $in_grace, $brhythm, $gbr, $ly_st, $c_voice, %voice_struct );
138             our ( @blen, $micro_tb, $deco_tb );
139             our ( $c_tune, $c_sym_ix, $c_abc, $toabc_called_outside, $toabc_called_inside );
140              
141             our %sym_name = (
142             # the extra () around the constants are there to fool the auto quoting
143             (ABC_T_NULL) => 'null',
144             (ABC_T_INFO) => 'info',
145             (ABC_T_PSCOM) => 'pscom',
146             (ABC_T_CLEF) => 'clef',
147             (ABC_T_NOTE) => 'note',
148             (ABC_T_REST) => 'rest',
149             (ABC_T_BAR) => 'bar',
150             (ABC_T_EOLN) => 'eoln',
151             (ABC_T_MREST) => 'mrest',
152             (ABC_T_MREP) => 'mrep',
153             (ABC_T_V_OVER) => 'vover',
154             (ABC_T_TUPLET) => 'tuplet',
155             );
156             our %info_name = (
157             'K' => 'key',
158             'L' => 'length',
159             'M' => 'meter',
160             'Q' => 'tempo',
161             'V' => 'voice',
162             'w' => 'lyrics',
163             'W' => 'lyrics',
164             );
165             our %state_name = (
166             (ABC_S_GLOBAL) => 'in_global',
167             (ABC_S_HEAD) => 'in_header',
168             (ABC_S_TUNE) => 'in_tune',
169             (ABC_S_EMBED) => 'in_line',
170             );
171              
172             our @key_tonic = qw(F C G D A E B);
173             our @key_shift = (1, 3, 5, 0, 2, 4, 6, 1, 4); # [7 + 2]
174             our @clef_type = qw(treble alto bass perc);
175              
176             }
177              
178              
179             # Processes abc tunes;
180             # Receives the filename of an abc tune
181             # Receives a set of expressions (functions) defining the processing and associated values for each element
182             sub dt {
183             my ( $abcfile, %abch ) = @_;
184             my $abc_struct = eval `aux-abc2perl $abcfile`;
185              
186             my $return = _dt_processing( $abc_struct, %abch );
187             return $return;
188             }
189              
190             # Works in a similar way of dt but takes input from a string instead of a file name
191             sub dt_string {
192             my ( $string, %abch ) = @_;
193              
194             my $tmp_abcfile = File::Temp->new( SUFFIX => '.abc' );
195             print {$tmp_abcfile} $string;
196              
197             my $abc_struct = eval `aux-abc2perl $tmp_abcfile`;
198              
199             my $return = _dt_processing( $abc_struct, %abch );
200              
201             return $return;
202             }
203              
204             # Returns a list of consecutive note structures belonging to the same measure
205             #
206             # A single undef is placed in the list at any point there is a discontinuity (such as if there is a
207             # rest between two pitches), unless the `no_undef` parameter is True.
208             #
209             # How to determine consecutive pitches is a little tricky and there are many options: The
210             # `$args->{skip_unisons}` parameter uses the midi-note value (ps) to determine unisons, so enharmonic
211             # transitions (F# -> Gb) are also skipped if `$args->{skip_unisons}` is true. Music21 believes that
212             # this is the most common usage. However, because of this, you cannot completely be sure that the
213             # find_consecutive_notes_in_measure() - find_consecutive_notes_in_measure({$args->{skip_unisons} =>
214             # 1}) will give you the number of P1s (Perfect First) in the piece, because there could be d2's
215             # (Diminished Second) in there as well.
216             sub find_consecutive_notes_in_measure {
217             my $args = shift;
218              
219             my $return_list = [];
220             my $n_symbols = scalar( @{ $c_tune->{symbols} } ) - 1;
221             my $last_start = 0;
222             my $last_end = -1;
223             my $last_was_undef = 0;
224             my $c_sym_offset = 0;
225             my $last_note;
226              
227             if ( $args->{skip_octaves} ) { $args->{skip_unisons} = 1; } # implied
228              
229             for my $ix ( $c_sym_ix .. $n_symbols ) {
230             my $c_sym = $c_tune->{symbols}->[$ix];
231              
232             # stops searching if it reaches the end of the measure
233             last if $c_sym->{type} == ABC_T_BAR;
234              
235             if ( not $last_was_undef
236             and not $args->{skip_gaps}
237             and $c_sym_offset > $last_end
238             and not $args->{no_undef} )
239             {
240             push @{ $return_list }, undef;
241             $last_was_undef = 1;
242             }
243              
244             # if it's a single note
245             if ( $c_sym->{type} == ABC_T_NOTE and $c_sym->{info}->{nhd} == 0 ) {
246             _check_consecutive_note(
247             {
248             return_list => $return_list,
249             main_args => $args,
250             c_sym_offset => $c_sym_offset,
251             c_sym => $c_sym,
252             last_start => $last_start,
253             last_end => $last_end,
254             last_was_undef => $last_was_undef,
255             last_note => $last_note
256             }
257             );
258             }
259             # it's a chord
260             elsif ( $c_sym->{type} == ABC_T_NOTE and $c_sym->{info}->{nhd} > 0 ) {
261             _check_consecutive_chord(
262             {
263             return_list => $return_list,
264             main_args => $args,
265             c_sym_offset => $c_sym_offset,
266             c_sym => $c_sym,
267             last_start => $last_start,
268             last_end => $last_end,
269             last_was_undef => $last_was_undef,
270             last_note => $last_note
271             }
272             );
273             }
274             # it's a rest
275             elsif ( not $args->{skip_rests}
276             and $c_sym->{type} == ABC_T_REST
277             and not $last_was_undef
278             and not $args->{no_undef} )
279             {
280             push @{$return_list}, undef;
281             $last_was_undef = 1;
282             $last_note = undef;
283             }
284             elsif ( $args->{skip_rests} and $c_sym->{type} == ABC_T_REST ) {
285             $last_end = $c_sym_offset + $c_sym->{info}->{dur};
286             }
287              
288             # increases the time offset
289             if ( $c_sym->{info}->{dur} ) { $c_sym_offset += $c_sym->{info}->{dur} }
290             }
291              
292             # removes the last-added element
293             if ($last_was_undef) { pop @{$return_list} }
294              
295             return @{$return_list};
296             }
297              
298             # Dumps a note's guitar/accompaniment chords
299             sub get_gchords {
300             my $sym;
301             if ( not @_ ) { $sym = $Music::Abc::DT::sym; }
302             else { $sym = shift; }
303              
304             return "$sym->{text}\n";
305             #FIXME return undef if not a note|rest|bar
306             }
307              
308             # Dumps the current voice's key
309             sub get_key {
310             return $voice_struct{$c_voice}{key}{text} || undef;
311             }
312              
313             # Dumps the current voice's length
314             sub get_length {
315             return $voice_struct{$c_voice}{length} || undef;
316             }
317              
318             # Dumps the current voice's meter
319             sub get_meter {
320             return $voice_struct{$c_voice}{meter}{text} || undef;
321             }
322              
323             # Dumps the current voice's time elapsed until the current symbol (time offset)
324             sub get_time {
325             return $voice_struct{$c_voice}{time};
326             # return $sym->{info}->{time};
327             #FIXME return undef if not in_tune or in_line
328             }
329              
330             # Dumps the current voice's elapsed time until the current symbol (time offset) in quarter lengths (ql)
331             sub get_time_ql {
332             # return $sym->{info}->{time} / $QUARTER_LENGTH;
333             return $voice_struct{$c_voice}{time} / $QUARTER_LENGTH;
334             #FIXME return undef if not in_tune or in_line
335             }
336              
337             # Dumps the current voice's wmeasure
338             sub get_wmeasure {
339             return $voice_struct{$c_voice}{meter}{wmeasure};
340             }
341              
342             # Default function for the processor
343             # Dumps a symbol's ABC
344             sub toabc {
345             # Returns the context of the current subroutine call
346             my ( $package, $filename, $line ) = caller;
347              
348             # set to true if it has been called outside of the module
349             $toabc_called_outside = $package ne 'Music::Abc::DT';
350             $toabc_called_inside = $package eq 'Music::Abc::DT';
351              
352             my $sym;
353             if ( not @_ ) { $sym = $Music::Abc::DT::sym; }
354             else { $sym = shift; }
355              
356             my ( $new_abc, $c, $nl_new ) = ( q{}, q{}, 0 );
357              
358             $c = $c_abc eq q{} ? "\n"
359             : substr $c_abc, length($c_abc) - 1, 1; # last character
360             # if ( $c_abc eq q{} ) { $c = "\n"; }
361             # else { $c = substr $c_abc, length($c_abc) - 1, 1; } # last character
362              
363             # put space when one is found
364             if ( $sym->{flags} & ABC_F_SPACE ) { $new_abc .= q{ } }
365              
366             # if the last symbol was inside a grace note block
367             if ( $in_grace
368             && ( $sym->{type} != ABC_T_NOTE || !( $sym->{flags} & ABC_F_GRACE ) ) )
369             {
370             $in_grace = 0; # out of grace note state
371             $brhythm = $gbr;
372             $new_abc .= '}'; # close grace notes
373             }
374              
375             given ($sym->{type}) { # symbol type
376             when (ABC_T_INFO ) { ($new_abc, $nl_new) = _info_to_abc($new_abc, $sym, $c, $nl_new) } # type: info
377             when ([ABC_T_PSCOM, ABC_T_NULL]) { ($new_abc, $nl_new) = _pscom_to_abc($new_abc, $sym, $c) } # type: pscom
378             when (ABC_T_NOTE ) { $new_abc = _pre_note_to_abc($new_abc, $sym); continue } # type: note
379             when ([ABC_T_NOTE,ABC_T_REST] ) { $new_abc = _note_to_abc($new_abc, $sym) } # type: note | rest
380             when (ABC_T_BAR ) { $new_abc = _bar_to_abc($new_abc, $sym, $c) } # type: bar
381             when (ABC_T_CLEF ) { return $new_abc } # type: clef
382             when (ABC_T_EOLN ) { ($new_abc, $nl_new) = _eoln_to_abc($new_abc, $sym, $c, $nl_new) } # type: eoln
383             when (ABC_T_MREST ) { $new_abc .= sprintf 'Z%d', $sym->{info}->{len} } # type: mrest
384             when (ABC_T_MREP ) { foreach (0..$sym->{info}->{len}-1) { $new_abc .= q{/} } } # type: mrep
385             when (ABC_T_V_OVER ) { $new_abc = _vover_to_abc($new_abc, $sym) } # type: v_over
386             when (ABC_T_TUPLET ) { $new_abc = _tuplet_to_abc($new_abc, $sym) } # type: tuplet
387             }
388              
389             if ( $sym->{comment} ne q{} ) {
390             if ( $new_abc ne q{} ) { $new_abc .= "\t" }
391             $new_abc .= "%$sym->{comment}";
392             $nl_new = 1;
393             }
394             if ( $nl_new || !ref( $c_tune->{symbols}->[ $c_sym_ix + 1 ] ) ) {
395             $new_abc .= "\n";
396             # _lyrics_dump( $new_abc, $sym );
397             }
398              
399             return $new_abc;
400             }
401              
402             ########################################### PRIVATE FUNCTIONS ######################################33
403              
404             # Adds a note/chord to the list of consecutive notes if it meets the criteria
405             sub _add_consecutive_note {
406             my $args = shift;
407             my $c_sym_offset = $args->{c_sym_offset};
408             my $c_sym = $args->{c_sym};
409             my $return_list = $args->{return_list};
410             my $last_start = $args->{last_start};
411             my $last_end = $args->{last_end};
412             my $last_was_undef = $args->{last_was_undef};
413             my $last_note = $args->{last_note};
414              
415             if ( $args->{main_args}->{get_overlaps} or $c_sym_offset >= $last_end ) {
416             if ( $c_sym_offset >= $last_end ) { # is not an overlap...
417             $last_start = $c_sym_offset;
418             $last_end = $c_sym->{info}->{dur} ? $last_start + $c_sym->{info}->{dur}
419             : $last_start;
420             $last_was_undef = 0;
421             $last_note = $c_sym;
422             }
423             # else do not update anything for overlaps
424              
425             push @{$return_list}, $c_sym;
426             }
427              
428             return;
429             }
430              
431             # Checks if a chord meets the criteria to be added to a list of consecutive notes
432             sub _check_consecutive_chord {
433             my $args = shift;
434             my $main_args = $args->{main_args};
435             my $c_sym_offset = $args->{c_sym_offset};
436             my $c_sym = $args->{c_sym};
437             my $return_list = $args->{return_list};
438             my $last_start = $args->{last_start};
439             my $last_end = $args->{last_end};
440             my $last_was_undef = $args->{last_was_undef};
441             my $last_note = $args->{last_note};
442              
443             if ( $main_args->{skip_chords}
444             and not $last_was_undef
445             and not $main_args->{no_undef} )
446             {
447             push @{$return_list}, undef;
448             $last_was_undef = 1;
449             $last_note = undef;
450             }
451              
452             # if we have a chord
453             else {
454             if ( $main_args->{skip_unisons}
455             and ( $last_note and $last_note->{info}->{nhd} > 0 )
456             and _get_ps($c_sym) == _get_ps($last_note) )
457             { # pass
458             } else {
459             _add_consecutive_note(
460             {
461             return_list => $return_list,
462             main_args => $main_args,
463             c_sym_offset => $c_sym_offset,
464             c_sym => $c_sym,
465             last_start => $last_start,
466             last_end => $last_end,
467             last_was_undef => $last_was_undef,
468             last_note => $last_note
469             }
470             );
471             }
472             }
473              
474             return;
475             }
476              
477             # Checks if a note meets the criteria to be added to a list of consecutive notes
478             sub _check_consecutive_note {
479             my $args = shift;
480             my $main_args = $args->{main_args};
481             my $c_sym_offset = $args->{c_sym_offset};
482             my $c_sym = $args->{c_sym};
483             my $return_list = $args->{return_list};
484             my $last_start = $args->{last_start};
485             my $last_end = $args->{last_end};
486             my $last_was_undef = $args->{last_was_undef};
487             my $last_note = $args->{last_note};
488              
489             if (
490             not $main_args->{skip_unisons}
491             or ( $last_note and $last_note->{info}->{nhd} > 0 )
492             or not $last_note
493             or get_pitch_class($c_sym) != get_pitch_class($last_note)
494             or ( not $main_args->{skip_octaves}
495             and _get_ps($c_sym) != _get_ps($last_note) )
496             )
497             {
498             _add_consecutive_note(
499             {
500             return_list => $return_list,
501             main_args => $main_args,
502             c_sym_offset => $c_sym_offset,
503             c_sym => $c_sym,
504             last_start => $last_start,
505             last_end => $last_end,
506             last_was_undef => $last_was_undef,
507             last_note => $last_note
508             }
509             );
510             }
511              
512             return;
513             }
514              
515             # -- dumps the bar symbol without decorations or guitar chords
516             sub _bar_dump {
517             my ( $new_abc, $sym, $c ) = @_;
518              
519             if ( $sym->{info}->{dotted} ) { $new_abc .= q{.} }
520              
521             if ( !$sym->{info}->{repeat_bar} || $c ne q{|} ) {
522             my($t, $v) = ($sym->{info}->{type}, 0);
523              
524             while ($t) {
525             #NOTE this instruction replaced the next: $v <<= 4;
526             $v = $v * ( 2**4 ); # left shift
527             $v |= ( $t & 0x0f );
528             $t >>= 4;
529             }
530             while ($v) {
531             $new_abc .= qw(? | [ ] : ? ? ?)[$v & 0x07];
532             $v >>= 4;
533             }
534             }
535              
536             if ( $sym->{info}->{repeat_bar} ) {
537             # it has only one character and it is a digit
538             if ( $sym->{text} =~ /^\d$/xms ) {
539             $new_abc .= $sym->{text}; # repeat
540             } else {
541             $new_abc .= sprintf '"%s"', $sym->{text};
542             }
543             } elsif ( $sym->{info}->{type} == B_OBRA ) {
544             $new_abc .= ']';
545             }
546              
547             return $new_abc;
548             }
549              
550             # -- return abc for bar symbol
551             sub _bar_to_abc {
552             my ( $new_abc, $sym, $c ) = @_;
553             #FIXME PARSER should store the spaces that exist before a bar ('flags' => ABC_F_SPACE; it's always 0)
554              
555             if ( $sym->{info}->{dc}->{n} ) {
556             $new_abc = _deco_dump( $sym->{info}->{dc}, $new_abc );
557             }
558              
559             if ( $sym->{text} ne q{} && !$sym->{info}->{repeat_bar} ) {
560             $new_abc = _gchord_dump( $new_abc, $sym->{text} );
561             }
562              
563             $new_abc = _bar_dump( $new_abc, $sym, $c );
564              
565             return $new_abc;
566             }
567              
568              
569             # -- change length when broken rhythm --
570             sub _broken_rhythm {
571             my $len = shift;
572              
573             given ($brhythm) {
574             when (-3) { $len *= 8; }
575             when (-2) { $len *= 4; }
576             when (-1) { $len *= 2; }
577             when (0 ) { return $len; }
578             when (1 ) { $len = $len * 2 / 3; }
579             when (2 ) { $len = $len * 4 / 7; }
580             when (3 ) { $len = $len * 8 / 15; }
581             }
582             if ( $len % 24 != 0 ) { $len = ( $len + 12 ) / 24 * 24 }
583             return $len;
584             }
585              
586             # -- dumps the broken rhythm symbol
587             sub _broken_rhythm_dump {
588             my $new_abc = shift;
589              
590             $brhythm = -$sym->{info}->{brhythm};
591             if ( $brhythm != 0 ) {
592             my ( $c, $n );
593             if ( ( $n = $brhythm ) < 0 ) {
594             $n = -$n;
595             $c = '>';
596             } else {
597             $c = '<';
598             }
599             while ( --$n >= 0 ) { $new_abc .= $c }
600             }
601              
602             return $new_abc;
603             }
604              
605             # -- dumps a chord's ties
606             sub _chord_tie {
607             my ( $new_abc, $all_tie ) = @_;
608              
609             if ($all_tie) {
610             if ( $all_tie & SL_DOTTED ) { $new_abc .= q{.} }
611             $new_abc .= q{-};
612             given ($all_tie) {
613             when (SL_ABOVE) { $new_abc .= q{'}; }
614             when (SL_BELOW) { $new_abc .= q{,}; }
615             }
616             }
617              
618             return $new_abc;
619             }
620              
621             # -- dumps a chords's notes, slurs, ties, ...
622             sub _chord_to_abc {
623             my ( $sym, $new_abc, $all_tie ) = @_;
624             my $len;
625              
626             # for each note in the symbol / chord(if nhd>0)
627             for my $i ( 0 .. $sym->{info}->{nhd} ) {
628              
629             # the $i'th note of the chord has decorations
630             if ( $sym->{info}->{decs}->[$i] ) {
631             my ( $i1, $i2, $deco );
632              
633             $i1 = $sym->{info}->{decs}->[$i] >> 3;
634             $i2 = $i1 + ( $sym->{info}->{decs}->[$i] & 0x07 );
635             for ( ; $i1 < $i2 ; $i1++ ) {
636             $deco = $sym->{info}->{dc}->t->[$i1];
637              
638             # prints single decoration character
639             if ( $deco < 128 ) {
640             if ($deco) { $new_abc .= chr $deco }
641             }
642             # prints the decoration name enclosed in !!
643             else { $new_abc .= sprintf '!%s!', $deco_tb->{ $deco - 128 } }
644             }
645             }
646              
647             # start slur
648             # sl1: slur start per head
649             if ( $sym->{info}->{sl1}->[$i] ) {
650             $new_abc = _slur_dump( $new_abc, $sym->{info}->{sl1}->[$i] );
651             }
652              
653             # lens: note lengths
654             $len = _broken_rhythm( $sym->{info}->{lens}->[$i] );
655              
656             # chlen: chord length
657             if ( $sym->{info}->{chlen} ) {
658             $len = $len * BASE_LEN / $sym->{info}->{chlen};
659             }
660              
661             $new_abc = _note_dump(
662             $new_abc,
663             $sym->{info}->{pits}->[$i],
664             $sym->{info}->{accs}->[$i],
665             $len,
666             $sym->{flags} & ABC_F_STEMLESS
667             );
668              
669             # prints tie for individual notes only
670             # ti1: flag to start tie here;
671             if ( $sym->{info}->{ti1}->[$i] && $sym->{info}->{ti1}->[$i] != $all_tie ) {
672             if ( $sym->{info}->{ti1}->[$i] & SL_DOTTED ) { $new_abc .= q{.} }
673             $new_abc .= q{-};
674             given ( $sym->{info}->{ti1}->[$i] ) { # tie direction
675             when (SL_ABOVE) { $new_abc .= q{'}; }
676             when (SL_BELOW) { $new_abc .= q{,}; }
677             }
678             }
679              
680             # end slur
681             # sl2: number of slur end per head
682             for ( $len = $sym->{info}->{sl2}->[$i] ; --$len >= 0 ; ) {
683             $new_abc .= ')';
684             }
685             }
686              
687             return $new_abc;
688             }
689              
690              
691             # -- dump a clef definition --
692             sub _clef_dump {
693             my($abc, $sym) = @_;
694             my($clef, $clef_line);
695              
696             if (($clef = $sym->{info}->{type}) >= 0) { # clef is defined
697             $clef_line = $sym->{info}->{line};
698              
699             given ($clef) {
700             when (TREBLE) { continue }
701             when ( [ PERC, TREBLE ] ) { if ( $clef_line == 2 ) { $clef_line = 0 } }
702             when (ALTO) { if ( $clef_line == 3 ) { $clef_line = 0 } }
703             when (BASS) { if ( $clef_line == 4 ) { $clef_line = 0 } }
704             }
705              
706             #name
707             if ( $sym->{info}->{name} ne q{} ) {
708             $abc .= " clef=\"$sym->{info}->{name}\"";
709             }
710             #invis
711             elsif ( $clef_line == 0 ) {
712             $abc .= ' clef=' . ( $sym->{info}->{invis} ? NONE : $clef_type[$clef] );
713             }
714             #clef
715             else { $abc .= ' clef=' . $clef_type[$clef] . $clef_line }
716              
717             #octave
718             if ( $sym->{info}->{octave} != 0 ) {
719             $abc .= ( $sym->{info}->{octave} > 0 ? q{+} : q{-} ) . '8';
720             }
721             }
722             #stafflines
723             if ( $sym->{info}->{stafflines} >= 0 ) {
724             $abc .= " stafflines=$sym->{info}->{stafflines}";
725             }
726             #staffscale
727             if ( $sym->{info}->{staffscale} != 0 ) {
728             $abc .= ' staffscale=' . sprintf '%.2f', $sym->{info}->{staffscale};
729             }
730              
731             return $abc;
732             }
733              
734             # -- dump the decorations --
735             sub _deco_dump {
736             my ( $dc, $abc ) = @_;
737             my ( $deco, $i );
738              
739             for my $i ( 0 .. $dc->{n} - 1 ) {
740             next if ( $i >= $dc->{h} && $i < $dc->{s} ); # skip the head decorations
741             $deco = $dc->{t}->[$i];
742             if ( $deco < 128 ) { # prints single decoration character
743             if ($deco) { $abc .= chr $deco }
744             }
745             else { # prints the decoration name enclosed in !!
746             $abc .= sprintf '!%s!', $deco_tb->{ $deco - 128 };
747             }
748             }
749             return $abc;
750             }
751              
752             sub _dt_processing {
753             my ( $abc_struct, %abch ) = @_;
754              
755             my $return = q{};
756             my $tunes = $abc_struct->{tunes};
757              
758             $deco_tb = $abc_struct->{deco_tb};
759              
760             foreach my $tune ( keys %{$tunes} ) { # tune
761             $in_grace = 0; # in grace note (state)
762             $brhythm = 0; # broken rhythm (state)
763             $gbr = 0; # (state)
764             @blen = (0) x MAXVOICE; # base length array
765             $micro_tb = $tunes->{$tune}->{micro_tb}; # micro tones table
766             $c_voice = $IMPLICIT_VOICE; # current voice
767             $c_tune = $tunes->{$tune}; # current tune
768             $c_sym_ix = 0; # current symbol index
769             $c_abc = q{}; # current abc
770             %voice_struct = (); # voice structure which stores each voice's stuff
771             my $n_symbols = scalar( @{ $c_tune->{symbols} } ) - 1;
772              
773             #initialize voice stuff
774             _initialize();
775              
776             # set the duration of all notes/rests/mrests - this is needed for tuplets
777             _set_durations( \$tunes, $tune );
778              
779             _set_tuplet_time_and_bars( \$tunes, $tune );
780              
781             for ( 0 .. $n_symbols ) { # tune symbols
782             $c_sym_ix = $_;
783             $sym = $c_tune->{symbols}->[$c_sym_ix];
784             $toabc_called_outside = 0;
785             $toabc_called_inside = 0;
786              
787             _update_score_variables(\$tunes, $tune, $sym);
788              
789             my $proc = _get_transformation( \%abch, $sym );
790             $c_abc .= $proc->() || q{};
791              
792             _update_time_offset();
793              
794             # calls toabc in order to update global variables only if it has not already been called in
795             # this iteration (either by being the default function or by being explicitily called inside one
796             # of the subroutines of the handler)
797             my $toabc_not_called = !$toabc_called_outside && !$toabc_called_inside;
798             if ($toabc_not_called) { toabc() }
799             }
800              
801             $return = $abch{'-end'} ? &{ $abch{'-end'} } : $c_abc;
802             }
803              
804             return $return;
805             }
806              
807             # -- dumps a chord's end symbol and updates the base length
808             sub _end_chord {
809             my ( $sym, $new_abc ) = @_;
810              
811             if ( $sym->{info}->{nhd} > 0 ) { # the current symbol is a chord
812             $new_abc .= ']'; # ends chord
813             if ( $sym->{info}->{chlen} ) { # chlen: chord length
814             $blen[$c_voice] = BASE_LEN;
815              
816             # prints the chord length
817             $new_abc = _length_dump( $new_abc, $sym->{info}->{chlen} );
818             }
819             }
820              
821             return $new_abc;
822             }
823              
824              
825             # -- returns the abc for the end of line
826             sub _eoln_to_abc {
827             my($new_abc, $sym, $c, $nl_new) = @_;
828              
829             # tclabc.c => "FIXME:pb when info after line continuation"
830             given ( $sym->{info}->{type} ) {
831             when (1) { $new_abc .= q{\\}; continue } # continuation
832             when ( [ 0, 1 ] ) { if ( $c ne "\n" ) { $nl_new = 1 } } # normal
833             when (2) { $new_abc .= q{!} } # abc2win line break
834             }
835              
836             return ( $new_abc, $nl_new );
837             }
838              
839             # -- dump the guitar chords / annotations --
840             sub _gchord_dump {
841             my($abc, $s) = @_;
842             my $q;
843              
844             while (($q = index $s, "\n") != -1) { # appends all guitar chords except the last one
845             $abc .= sprintf '"%.*s"', $q, $s;
846             $s = substr $s, $q+1, length($s)-$q;
847             }
848             $abc .= "\"$s\""; # appends the last guitar chord
849              
850             return $abc;
851             }
852              
853             # -- searches for a note's chord related actuators
854             sub _get_chord_actuator {
855             my ( $abch, $sym, $proc ) = @_;
856             my %abch = %{$abch};
857              
858             # it is a chord
859             if ( $sym->{info}->{nhd} > 0 ) {
860             $proc = $abch{'chord'} || $proc;
861              
862             if ( is_major_triad($sym) ) {
863             $proc = $abch{'major_triad'} || $proc;
864             }
865             if ( is_minor_triad($sym) ) {
866             $proc = $abch{'minor_triad'} || $proc;
867             }
868             if ( is_dominant_seventh($sym) ) {
869             $proc = $abch{'dominant_seventh'} || $proc;
870             }
871             }
872              
873             return $proc;
874             }
875              
876             # -- get the actuators that have a decoration --
877             sub _get_deco_actuators {
878             my ( $abch, $sym, $proc ) = @_;
879             my %abch = %{$abch};
880             my $type = $sym->{type};
881             my $bar = $type == ABC_T_BAR;
882              
883             if ( $sym->{info}->{dc}->{n} ) { # n is the whole number of decorations
884             $proc = $abch{'deco'} || $proc;
885              
886             # note::deco is more specific than deco alone
887             $proc = $abch{"$sym_name{$type}::deco"} || $proc;
888              
889             # the actual bar is more specific
890             if ($bar) {
891             $proc = $abch{ _bar_dump( q{}, $sym, q{} ) . '::deco' } || $proc;
892             }
893              
894             my $dc = _deco_dump( $sym->{info}->{dc}, q{} );
895             #FIXME é possivel existir mais que uma deco por sym, logo a pesquisa no abch nao pode estar tal como está
896             # the actual decoration is more specific
897             $proc = $abch{$dc} || $proc;
898              
899             # note::!f! is more specific than !f! alone
900             $proc = $abch{"$sym_name{$type}::$dc"} || $proc;
901              
902             # the actual bar with that actual deco is more specific
903             if ($bar) {
904             $proc = $abch{ _bar_dump( q{}, $sym, q{} ) . "::$dc" } || $proc;
905             }
906             }
907              
908             return $proc;
909             }
910              
911             # -- searches for a note/rest/bar's gchord/accompaniment chord actuators
912             sub _get_gchord_actuator {
913             my ( $abch, $sym, $proc ) = @_;
914             my %abch = %{$abch};
915             my $type = $sym->{type};
916             my $element = $type == ABC_T_NOTE ? 'note'
917             : $type == ABC_T_REST ? 'rest'
918             : 'bar';
919              
920             # it has at least one accompaniment chord (or guitar chord)
921             if ($sym->{text}) {
922             $proc = $abch{'gchord'} || $proc;
923              
924             # bar::gchord
925             $proc = $abch{ $element . 'gchord' } || $proc;
926              
927             my $gchord = $sym->{text};
928             # Multiple chords per element can be notated writing two or more consecutive
929             # chords before the same element, or using the separating characters ; or \n
930             $gchord =~ tr/;/\n/;
931             my @gchords = split m/\n/xms, $gchord;
932              
933             # stops the search after the first match; the first gchords have priority
934             # eg: 'gchord::F'
935             foreach my $gc (@gchords) {
936             $proc = $abch{ "gchord::$gc" } || $proc;
937             last if $abch{ "gchord::$gc" };
938             }
939              
940             # stops the search after the first match; the first gchords have priority
941             # eg: 'bar::gchord::F'
942             foreach my $gc (@gchords) {
943             $proc = $abch{ $element . "::gchord::$gc" } || $proc;
944             last if $abch{ $element . "::gchord::$gc" };
945             }
946             }
947              
948             return $proc;
949             }
950              
951             sub _get_info {
952             my $sym = shift;
953              
954             given ( substr $sym->{text}, 0, 1 ) {
955             when ('V') { # Voice
956             _get_voice($sym);
957             }
958             when ('K') { # Key (K)
959             _get_key($sym);
960             }
961             when ('Q') { # Tempo (Q)
962             # $voice_struct{$c_voice}{tempo} = substr _tempo_header_dump( q{}, $sym ), 2;
963             }
964             when ('M') { # Meter (M)
965             _get_meter($sym);
966             }
967             when ('L') { # Length (L)
968             _get_length($sym);
969             }
970             }
971              
972             return;
973             }
974              
975             # -- updates the current voice's key info
976             sub _get_key {
977             my $sym = shift;
978             my $c_key;
979              
980             if ( $sym->{info}->{empty} ) {
981             if ( $sym->{info}->{empty} == 2 ) { $c_key = NONE }
982             } else {
983             # extracts only the Key's note and mode, ignores explicit accidentals
984             $c_key = _key_calc($sym);
985             }
986              
987             _update_key($c_key);
988              
989             return;
990             }
991              
992             sub _update_key {
993             my $c_key = shift;
994             my $v = $sym->{state} == ABC_S_HEAD ? $GLOBAL : $c_voice;
995              
996             $voice_struct{$v}{key}{text} = $c_key;
997             $voice_struct{$v}{key}{sf} = $sym->{info}->{sf};
998             $voice_struct{$v}{key}{exp} = $sym->{info}->{exp};
999             $voice_struct{$v}{key}{nacc} = $sym->{info}->{nacc};
1000             $voice_struct{$v}{key}{pits} = $sym->{info}->{pits};
1001             $voice_struct{$v}{key}{accs} = $sym->{info}->{accs};
1002              
1003             return;
1004             }
1005              
1006             # -- updates the current voice's length info
1007             sub _get_length {
1008             my $sym = shift;
1009             my $length = substr _length_header_dump( q{}, $sym ), 2;
1010              
1011             given ( $sym->{state} ) {
1012             when (ABC_S_GLOBAL) {
1013             #FIXME: keep the values and apply to all tunes??
1014             }
1015             when ( ABC_S_HEAD ) {
1016             $voice_struct{$GLOBAL}{length} = $length;
1017             continue;
1018             }
1019             when ( [ ABC_S_HEAD, ABC_S_TUNE ] ) {
1020             $voice_struct{$c_voice}{length} = $length;
1021             }
1022             }
1023              
1024             return;
1025             }
1026              
1027             # -- updates the current voice's meter info
1028             sub _get_meter {
1029             my $sym = shift;
1030             my $meter_text = 'M:' . _meter_calc($sym);
1031              
1032             given ( $sym->{state} ) {
1033             when (ABC_S_GLOBAL) {
1034             #FIXME: keep the values and apply to all tunes??
1035             }
1036             when ( ABC_S_HEAD ) {
1037             $voice_struct{$GLOBAL}{meter}{text} = $meter_text;
1038             $voice_struct{$GLOBAL}{meter}{wmeasure} = $sym->{info}->{wmeasure};
1039             continue;
1040             }
1041             when ( [ ABC_S_HEAD, ABC_S_TUNE ] ) {
1042             $voice_struct{$c_voice}{meter}{text} = $meter_text;
1043             $voice_struct{$c_voice}{meter}{wmeasure} = $sym->{info}->{wmeasure};
1044             }
1045             }
1046              
1047             return;
1048             }
1049              
1050             # -- searches for note, rest and bar actuators
1051             # -- it also gets decoration related actuators
1052             sub _get_note_rest_bar_actuators {
1053             my ( $abch, $sym, $proc ) = @_;
1054             my %abch = %{$abch};
1055             my $type = $sym->{type};
1056             my ( $note, $bar ) = ( $type == ABC_T_NOTE, $type == ABC_T_BAR );
1057             my $voice_id = $voice_struct{$c_voice}{id};
1058             my $voice_name = $voice_struct{$c_voice}{name};
1059              
1060             $proc = $abch{ $sym_name{$type} } || $proc;
1061              
1062             #searches for actuators of the like: V:1::note or V:Tenor::rest
1063             if ($voice_name) {
1064             $proc = $abch{ "V:$voice_name" . "::$sym_name{$type}" } || $proc;
1065             }
1066             if ($voice_id) {
1067             $proc = $abch{"V:$voice_id" . "::$sym_name{$type}"} || $proc;
1068             }
1069              
1070             if ($note) {
1071             # searches for chord related actuators
1072             $proc = _get_chord_actuator( $abch, $sym, $proc );
1073              
1074             my $pitch = _pitch_dump( $sym->{info}->{pits}->[0], $sym->{info}->{accs}->[0] );
1075             # removes the octave
1076             $pitch =~ tr/,'/ /;
1077              
1078             #searches for actuators of the like: note::c
1079             $proc = $abch{"$sym_name{$type}" . "::$pitch"} || $proc;
1080              
1081             #searches for actuators of the like: V:1::note::c or V:Tenor::note::^F
1082             if ($voice_name) {
1083             $proc = $abch{"V:$voice_name" . "::$sym_name{$type}" . "::$pitch"} || $proc;
1084             }
1085             if ($voice_id) {
1086             $proc = $abch{"V:$voice_id" . "::$sym_name{$type}" . "::$pitch"} || $proc;
1087             }
1088             }
1089              
1090             # the actual bar is more specific: :|
1091             if ($bar) { $proc = $abch{ _bar_dump( q{}, $sym, q{} ) } || $proc; }
1092              
1093             # searches for an actuator corresponding to a note/rest/bar with an accompaniment chord
1094             # gchords are more specific than the previous; equivalent to decorations although in this implementation it's less specific
1095             $proc = _get_gchord_actuator( $abch, $sym, $proc );
1096              
1097             # searches for an actuator corresponding to a note/rest/bar with a decoration
1098             # decorations are more specific than the previous; equivalent to gchords although in this implementation it's more specific
1099             $proc = _get_deco_actuators( $abch, $sym, $proc );
1100              
1101             return $proc;
1102             }
1103              
1104             # -- searches for null, info and clef actuators
1105             # -- these three symbol's types have been separated from the others
1106             # -- because they are the only types that can be conjugated with
1107             # -- the state actuator
1108             sub _get_null_info_clef_actuators {
1109             my ( $abch, $sym, $proc ) = @_;
1110             my %abch = %{$abch};
1111             my $type = $sym->{type};
1112             my $state = $sym->{state};
1113             my $info_type = substr $sym->{text}, 0, 1;
1114             my $info = $type == ABC_T_INFO;
1115              
1116             $proc = $abch{ $sym_name{$type} } || $proc;
1117             $proc = $abch{ $state_name{$state} . "::$sym_name{$type}" } || $proc;
1118              
1119             if ($info) {
1120             $proc = $abch{"$info_type:"} || $proc;
1121             $proc = $abch{ $state_name{$state} . "::$info_type:" } || $proc;
1122              
1123             if ( $info_type eq 'V' ) {
1124             my $voice_id = $sym->{info}->{id};
1125             my $voice_name = $sym->{info}->{fname} || $voice_struct{$c_voice}{name};
1126              
1127             if ($voice_name) { $proc = $abch{"$info_type:$voice_name"} || $proc; }
1128             $proc = $abch{"$info_type:$voice_id"} || $proc;
1129             if ($voice_name) {
1130             $proc = $abch{ $state_name{$state} . "::$info_type:$voice_name" } || $proc;
1131             }
1132             $proc = $abch{ $state_name{$state} . "::$info_type:$voice_id" } || $proc;
1133             } elsif ( $info_type eq 'M' ) {
1134             $proc = $abch{ "$info_type:" . _meter_calc($sym) } || $proc;
1135             $proc = $abch{ $state_name{$state} . "::$info_type:" . _meter_calc($sym) } || $proc;
1136             }
1137             }
1138              
1139             return $proc;
1140             }
1141              
1142             # -- gets pscom actuators (abcMIDI's, PageFormat's, other)
1143             sub _get_pscom_actuators {
1144             my ( $abch, $sym, $proc ) = @_;
1145             my %abch = %{$abch};
1146             my $type = $sym->{type};
1147              
1148             my $text = $sym->{text};
1149             if ( $text ne q{} ) { $text = substr $text, 2 } # removes '%%' from text
1150              
1151             # pscom
1152             $proc = $abch{ $sym_name{$type} } || $proc;
1153              
1154             # MIDI is more specific than pscom
1155             if ( $text =~ /^MIDI.*/xms ) {
1156             $proc = $abch{'MIDI'} || $proc; # || $abch{'midi'}
1157              
1158             # MIDI::abcMIDI_command is more specific than MIDI
1159             if ( $text =~ /^MIDI\s+(\w+).*/xms ) {
1160             $proc = $abch{"MIDI::$1"} || $proc;
1161             }
1162              
1163             #TODO add PageFormats (see last pages from abcplus)
1164             } else {
1165             $proc = $abch{'FORMAT'} || $proc;
1166              
1167             if ( $text =~ /^(staves|score)/xms ) {
1168             $proc = $abch{$1} || $proc;
1169             }
1170             }
1171              
1172             return $proc;
1173             }
1174              
1175             # -- gets the transformation to be applied according to an abc symbol/element
1176             # -- searches for an actuator that matches the abc symbol passed in as argument
1177             # -- the most specific actuator is the one chosen
1178             sub _get_transformation {
1179             my ( $abch, $sym ) = @_;
1180             my %abch = %{$abch};
1181             my $type = $sym->{type};
1182             my $state = $sym->{state};
1183             my $proc = q{};
1184              
1185             # the second most general actuator is the state, ex: in_header
1186             $proc = $abch{ $state_name{$state} } || $proc;
1187              
1188             # searches for actuators
1189             if ( $type == ABC_T_PSCOM ) {
1190             # searches for pscom actuators
1191             $proc = _get_pscom_actuators( $abch, $sym, $proc );
1192             } elsif ( $type == ABC_T_NOTE
1193             || $type == ABC_T_REST
1194             || $type == ABC_T_BAR )
1195             {
1196             # searches for note, rest or bar actuators
1197             $proc = _get_note_rest_bar_actuators( $abch, $sym, $proc );
1198             } elsif ( $type == ABC_T_NULL
1199             || $type == ABC_T_INFO
1200             || $type == ABC_T_CLEF )
1201             {
1202             # searches for nul, info or clef actuators
1203             $proc = _get_null_info_clef_actuators( $abch, $sym, $proc );
1204             } else {
1205             # searches for the remaining actuators ( eoln, mrest, mrep, v_over, tuplet )
1206             $proc = $abch{ $sym_name{$type} } || $proc;
1207             }
1208              
1209             # if no actuator was found, it tries to apply the -default function
1210             # and if it doesn't exist either, it applies the identity function - toabc()
1211             $proc ||= $abch{'-default'} || \&toabc;
1212              
1213             return $proc;
1214             }
1215              
1216             # -- Updates the current voice and some info related to it --
1217             sub _get_voice {
1218             my $sym = shift;
1219              
1220             if ( $sym->{state} == ABC_S_TUNE || $sym->{state} == ABC_S_EMBED ) {
1221             $c_voice = $sym->{info}->{voice};
1222              
1223             #set voice stuff if not already set
1224             #TODO check abcm2ps-7.3.4/parse.c:2817 (do_tune)
1225             $voice_struct{$c_voice}{id} ||= $sym->{info}->{id};
1226             $voice_struct{$c_voice}{name} ||= $sym->{info}->{fname} || q{};
1227             $voice_struct{$c_voice}{time} ||= 0;
1228             $voice_struct{$c_voice}{meter}{text} ||= $voice_struct{$GLOBAL}{meter}{text} || 'M:' . DEFAULT_METER;
1229             $voice_struct{$c_voice}{meter}{wmeasure} ||= $voice_struct{$GLOBAL}{meter}{wmeasure} || BASE_LEN;
1230             $voice_struct{$c_voice}{length} ||= $voice_struct{$GLOBAL}{length} || 'L:' . DEFAULT_LENGTH;
1231             $voice_struct{$c_voice}{key}{text} ||= $voice_struct{$GLOBAL}{key}{text};
1232             $voice_struct{$c_voice}{key}{sf} ||= $voice_struct{$GLOBAL}{key}{sf};
1233             $voice_struct{$c_voice}{key}{exp} ||= $voice_struct{$GLOBAL}{key}{exp};
1234             $voice_struct{$c_voice}{key}{nacc} ||= $voice_struct{$GLOBAL}{key}{nacc};
1235             $voice_struct{$c_voice}{key}{pits} ||= $voice_struct{$GLOBAL}{key}{pits};
1236             $voice_struct{$c_voice}{key}{accs} ||= $voice_struct{$GLOBAL}{key}{accs};
1237             }
1238              
1239             return;
1240             }
1241              
1242              
1243             # -- dump a header --
1244             sub _header_dump {
1245             my ( $abc, $sym ) = @_;
1246              
1247             given (substr $sym->{text}, 0, 1) { # info type (first character)
1248             when ('K' ) { $abc = _key_header_dump($abc, $sym) } # Key
1249             when ('L' ) { $abc = _length_header_dump($abc, $sym) } # Length
1250             when ('M' ) { $abc = _meter_header_dump($abc, $sym) } # Meter
1251             when ('Q' ) { $abc = _tempo_header_dump($abc, $sym) } # Tempo
1252             when ('V' ) { $abc = _voice_header_dump($abc, $sym) } # Voice
1253             when (['d','s']) { $abc .= q{%}; continue } # 's': decoration line # tclabc.c => "FIXME: already in notes"
1254             default { $abc .= $sym->{text}; }
1255             }
1256              
1257             return $abc;
1258             }
1259              
1260             # -- return a 'up' / 'down' / auto' parameter value --
1261             sub _head_par {
1262             my $v = shift;
1263             return 'down' if ($v < 0);
1264             return 'auto' if ($v == 2);
1265             return 'up';
1266             }
1267              
1268             # -- returns the abc for the info field and the new line flag
1269             sub _info_to_abc {
1270             my ($new_abc, $sym, $c, $nl_new) = @_;
1271              
1272             if ($sym->{state} == ABC_S_EMBED) { $new_abc .= '[' }
1273             elsif ($c ne "\n") { $new_abc .= "\\\n";
1274             # _lyrics_dump($new_abc, $sym);
1275             }
1276             $new_abc = _header_dump($new_abc, $sym);
1277             if ($sym->{state} == ABC_S_EMBED) { $new_abc .= ']' }
1278             else { $nl_new = 1; }
1279              
1280             return ($new_abc, $nl_new);
1281             }
1282              
1283             # Initializes voice variables
1284             sub _initialize {
1285              
1286             $voice_struct{$c_voice}{id} = q{};
1287             $voice_struct{$c_voice}{name} = q{};
1288             $voice_struct{$c_voice}{meter}{text} = 'M:' . DEFAULT_METER;
1289             $voice_struct{$c_voice}{meter}{wmeasure} = BASE_LEN;
1290             $voice_struct{$c_voice}{length} = 'L:' . DEFAULT_LENGTH;
1291             $voice_struct{$c_voice}{time} = 0;
1292             $voice_struct{$c_voice}{key}{text} = q{};
1293              
1294             return;
1295             }
1296              
1297             # -- calculates key note and mode
1298             sub _key_calc {
1299             my $sym = shift;
1300             my $abc = q{};
1301              
1302             # calculates Key
1303             if ( $sym->{info}->{mode} < BAGPIPE ) {
1304             # ion dor phr lyd mix aeo loc
1305             # 7 C# D# E# F# G# A# B#
1306             # 6 F# G# A# B C# D# E#
1307             # 5 B C# D# E F# G# A#
1308             # 4 E F# G# A B C# D#
1309             # 3 A B C# D E F# G#
1310             # 2 D E F# G A B C#
1311             # 1 G A B C D E F#
1312             # 0 C D E F G A B
1313             # -1 F G A Bb C D E
1314             # -2 Bb C D Eb F G A
1315             # -3 Eb F G Ab Bb C D
1316             # -4 Ab Bb C Db Eb F G
1317             # -5 Db Eb F Gb Ab Bb C
1318             # -6 Gb Ab Bb Cb Db Eb F
1319             # -7 Cb Db Eb Fb Gb Ab Bb
1320              
1321             my $i = $sym->{info}->{sf} + $key_shift[ $sym->{info}->{mode} ];
1322             $abc .= $key_tonic[ ( $i + 7 ) % 7 ];
1323             if ( $i < 0 ) { $abc .= 'b' }
1324             elsif ( $i >= 7 ) { $abc .= q{#} }
1325             }
1326              
1327             # if it is a mode other than major it appends the first 3 characters of its name (mixolydian => mix)
1328             if ( $sym->{info}->{mode} != MAJOR ) {
1329             $abc .= substr( (KEY_NAMES)[ $sym->{info}->{mode} ], 0, 3 );
1330             }
1331              
1332             return $abc;
1333             }
1334              
1335             # -- dump the header key
1336             sub _key_header_dump {
1337             my($abc, $sym) = @_;
1338              
1339             $abc .= 'K:';
1340             if ( $sym->{info}->{empty} ) {
1341             if ( $sym->{info}->{empty} == 2 ) { $abc .= NONE }
1342             }
1343             else {
1344             # calculates key note and mode
1345             $abc .= _key_calc($sym);
1346              
1347             # prints explicit accidentals
1348             if ( $sym->{info}->{nacc} != 0 ) { # number of explicit accidentals
1349             if ( $sym->{info}->{exp} ) { $abc .= ' exp '; } # Explicit accidentals
1350             else { $abc .= q{ }; }
1351             if ( $sym->{info}->{nacc} < 0 ) { $abc = NONE; } # No accidental
1352             else {
1353             for ( 0 .. $sym->{info}->{nacc} - 1 ) {
1354             $abc = _note_dump(
1355             $abc,
1356             $sym->{info}->{pits}->[$_],
1357             $sym->{info}->{accs}->[$_],
1358             (
1359             $blen[$c_voice] != 0
1360             ? $blen[$c_voice]
1361             : BASE_LEN / 8
1362             ),
1363             0
1364             );
1365             }
1366             }
1367             }
1368             }
1369              
1370             # tclabc.c => "FIXME: only if forced?"
1371             # prints the key's clef if it exists
1372             if ( ref( $c_tune->{symbols}->[ $c_sym_ix + 1 ] )
1373             && $c_tune->{symbols}->[ $c_sym_ix + 1 ]->{type} == ABC_T_CLEF )
1374             {
1375             $abc = _clef_dump( $abc, $c_tune->{symbols}->[ $c_sym_ix + 1 ] );
1376             }
1377              
1378             return $abc;
1379             }
1380              
1381             # -- dump the note/rest length --
1382             sub _length_dump {
1383             my($abc, $len) = @_;
1384             my $div = 0;
1385              
1386             if ( $blen[$c_voice] == 0 ) { $blen[$c_voice] = BASE_LEN / 8 }
1387              
1388             while(1) {
1389             if (($len % $blen[$c_voice]) == 0) {
1390             $len /= $blen[$c_voice];
1391             if ($len != 1) { $abc .= $len }
1392             last;
1393             }
1394             $len *= 2;
1395             $div++;
1396             }
1397              
1398             while ( --$div >= 0 ) { $abc .= q{/} }
1399              
1400             return $abc;
1401             }
1402              
1403             # -- dump header length dump
1404             sub _length_header_dump {
1405             my ( $abc, $sym ) = @_;
1406              
1407             # assigns base length
1408             if ( $sym->{state} == ABC_S_GLOBAL || $sym->{state} == ABC_S_HEAD ) {
1409              
1410             # assigns base length to all voices
1411             foreach ( reverse 0 .. MAXVOICE- 1 ) {
1412             $blen[$_] = $sym->{info}->{base_length};
1413             }
1414             } else {
1415              
1416             # assigns base length to current voice
1417             $blen[$c_voice] = $sym->{info}->{base_length};
1418             }
1419             $abc .= sprintf 'L:1/%d', BASE_LEN / $blen[$c_voice]; # prints length
1420              
1421             return $abc;
1422             }
1423              
1424             # -- dump the lyrics --
1425             # sub _lyrics_dump {
1426             # my($abc,$as2) = @_;
1427             # my($as,$as1);
1428             # my $s;
1429             # my($i,$maxly);
1430             #
1431             # # count the number of lyric lines
1432             # # return if (not defined($as1 = $ly_st));
1433             # return;
1434             # #TODO verificar se isto é mesmo necessario. se sim terminar. é preciso ver a struct sym e lyrics que
1435             # #estao no tclabc.h (linhas 17 e 12)
1436             # }
1437              
1438             # -- calculates meter info
1439             sub _meter_calc {
1440             my $sym = shift;
1441             my $abc = q{};
1442              
1443             # iterates through each meter element
1444             # nmeter: number of meter elements
1445             if ($sym->{info}->{nmeter} == 0) { $abc .= NONE; }
1446             else { # prints meter elements
1447             foreach my $i (0..$sym->{info}->{nmeter}-1) {
1448             if ( $i > 0 # if there's more than one element
1449             && $sym->{info}->{meter}->[$i]->{top} =~ /^\d.*/xms # if top starts with a number
1450             && substr( $abc, length($abc) - 1, 1 ) =~ /\d/xms ) # if last character is a number
1451             {
1452             $abc .= q{ }; # adds a space
1453             }
1454             $abc .= sprintf '%.8s', $sym->{info}->{meter}->[$i]->{top}; # truncates top to 8 characters
1455              
1456             if ( $sym->{info}->{meter}->[$i]->{bot} ne q{} ) {
1457             # truncates bottom to 2 characters
1458             $abc .= sprintf '/%.2s', $sym->{info}->{meter}->[$i]->{bot};
1459             }
1460             }
1461             }
1462              
1463             return $abc;
1464             }
1465              
1466             # -- dump meter
1467             sub _meter_header_dump {
1468             my($abc, $sym) = @_;
1469              
1470             #FIXME TCLABC o expdur nao é tratado aqui logo coisas como: M:C|=2/1 nao aparecem
1471             $abc .= 'M:';
1472              
1473             # prints Meter info
1474             $abc .= _meter_calc($sym);
1475              
1476             # assigns base length
1477             if ($blen[$c_voice] == 0) { # base length is not defined
1478             my $ulen;
1479             if ( $sym->{info}->{wmeasure} >= BASE_LEN * 3 / 4
1480             || $sym->{info}->{wmeasure} == 0 )
1481             {
1482             $ulen = BASE_LEN / 8;
1483             } else {
1484             $ulen = BASE_LEN / 16;
1485             }
1486              
1487             # assigns base length
1488             if ( $sym->{state} == ABC_S_GLOBAL || $sym->{state} == ABC_S_HEAD ) {
1489              
1490             # assigns base length to all voices
1491             foreach ( reverse 0 .. MAXVOICE- 1 ) { $blen[$_] = $ulen }
1492             } else {
1493             $blen[$c_voice] = $ulen; # assigns base length to current voice
1494             }
1495             }
1496              
1497             return $abc;
1498             }
1499              
1500             # -- dump a note --
1501             sub _note_dump {
1502             my ( $abc, $pitch, $acc, $len, $nostem ) = @_;
1503              
1504             # Note Pitch and Accidentals
1505             $abc = _pitch_dump( $pitch, $acc, $abc );
1506              
1507             # Note Length
1508             if ($nostem) { $abc .= '0' } #stem
1509              
1510             return _length_dump( $abc, $len );
1511             }
1512              
1513             # -- returns the abc for rest and note and elements related to them (chord [], slurs (), ties -)
1514             sub _note_to_abc {
1515             my($new_abc, $sym) = @_;
1516              
1517             # if there are slurs starting here; != 0
1518             if ( $sym->{info}->{slur_st} ) {
1519             $new_abc = _slur_dump( $new_abc, $sym->{info}->{slur_st} );
1520             }
1521             if ( $sym->{text} ne q{} ) {
1522             $new_abc = _gchord_dump( $new_abc, $sym->{text} ); # guitar chord
1523             }
1524             if ( $sym->{info}->{dc}->{n} ) {
1525             $new_abc = _deco_dump( $sym->{info}->{dc}, $new_abc );
1526             }
1527              
1528             # NOTE replaced bitwise operator (|)
1529             $brhythm ||= $sym->{info}->{brhythm};
1530              
1531             if ($sym->{type} == ABC_T_NOTE) { # the current symbol is a note
1532             my ( $all_tie, $blen_sav ) = ( 0, $blen[$c_voice] );
1533              
1534             # updates base length if the current symbol is grace note
1535             if ( $sym->{flags} & ABC_F_GRACE ) { $blen[$c_voice] = BASE_LEN / 4 }
1536              
1537             # start chord
1538             ( $new_abc, $all_tie ) = _start_chord( $sym, $new_abc, $all_tie );
1539              
1540             # prints chord's notes, slurs, ties, etc
1541             $new_abc = _chord_to_abc( $sym, $new_abc, $all_tie );
1542              
1543             # end chord
1544             $new_abc = _end_chord( $sym, $new_abc );
1545              
1546             # prints tie for chord
1547             $new_abc = _chord_tie( $new_abc, $all_tie );
1548              
1549             # restores the current voice's base length
1550             $blen[$c_voice] = $blen_sav;
1551             } else {
1552              
1553             # rests and additional spacings
1554             $new_abc = _rest_to_abc( $sym, $new_abc );
1555             }
1556              
1557             #end slurs
1558             foreach ( 0 .. $sym->{info}->{slur_end} - 1 ) { $new_abc .= ')' }
1559              
1560             # dumps broken rhythm symbol
1561             $new_abc = _broken_rhythm_dump($new_abc);
1562              
1563             return $new_abc;
1564             }
1565              
1566             # -- dumps a note's accidentals, microtones and pitch --
1567             sub _pitch_dump {
1568             my ( $pits, $acc, $abc ) = @_;
1569              
1570             # Note Accidentals
1571             given ( $acc & 0x07 ) {
1572             when (A_DS) { $abc .= q{^}; continue; }
1573             when ( [ A_SH, A_DS ] ) { $abc .= q{^}; }
1574             when (A_DF) { $abc .= '_'; continue; }
1575             when ( [ A_FT, A_DF ] ) { $abc .= '_'; }
1576             when (A_NT) { $abc .= q{=}; }
1577             }
1578              
1579             # Note Microtones
1580             $acc >>= 3;
1581             if ($acc) {
1582             my ($n,$d);
1583              
1584             $n = $micro_tb->[$acc] >> 8;
1585             $d = $micro_tb->[$acc] & 0xff;
1586             if ( $n != 0 ) { $abc .= ( $n + 1 ) }
1587             if ($d != 0) {
1588             $abc .= q{/};
1589             if ( $d != 1 ) { $abc .= ( $d + 1 ) }
1590             }
1591             }
1592              
1593             # Note Step and Octave
1594             $abc .= _step_dump($pits);
1595              
1596             return $abc;
1597             }
1598              
1599             # -- Returns the note's step (A, B, c ...) and the octave
1600             sub _step_dump {
1601             my $pits = shift;
1602             my $abc;
1603             my $j;
1604              
1605             if ( $pits >= 23 ) { # notes below c included
1606             $abc .= chr( ord('a') + ( $pits - 23 + 2 ) % 7 );
1607             $j = ( $pits - 23 ) / 7;
1608             while ( --$j >= 0 ) { $abc .= q{'} } # octaves
1609             } else { # notes above c excluded
1610             $abc .= chr( ord('A') + ( $pits + 49 ) % 7 );
1611             $j = ( 22 - $pits ) / 7;
1612             while ( --$j >= 0 ) { $abc .= q{,} } # octaves
1613             }
1614              
1615             return $abc;
1616             }
1617              
1618             # -- returns the abc for the grace note symbol if it is one
1619             sub _pre_note_to_abc {
1620             my($new_abc, $sym) = @_;
1621              
1622             if ( !( $sym->{flags} & ABC_F_GRACE ) ) { # not a grace note
1623             if ( not defined $ly_st ) { $ly_st = $sym } # set $ly_st if not defined
1624             } else { # grace note
1625             if ( !$in_grace ) {
1626             #NOTE when there's something like ({AB} c), because this function is called
1627             #before _note_to_abc - where slurs are dumped - it changes the order of the first 2 characters
1628             $in_grace = 1;
1629             $gbr = $brhythm;
1630             $brhythm = 0;
1631             $new_abc .= '{';
1632             if ( $sym->{flags} & ABC_F_SAPPO ) { $new_abc .= q{/} } #short appoggiatura
1633             }
1634             }
1635              
1636             return $new_abc;
1637             }
1638              
1639             # -- returns the abc for the info field and the new line flag
1640             sub _pscom_to_abc {
1641             my ( $new_abc, $sym, $c ) = @_;
1642              
1643             my $nl_new = 1;
1644             if ( $sym->{text} ne q{} ) {
1645             if ( $c ne "\n" ) { $new_abc .= "\\\n" }
1646              
1647             # _lyrics_dump($new_abc, $sym) if ($new_abc ne "");
1648             $new_abc .= $sym->{text};
1649             }
1650              
1651             return ( $new_abc, $nl_new );
1652             }
1653              
1654             # -- dumps rests and additional spacings to abc
1655             sub _rest_to_abc {
1656             my ( $sym, $new_abc ) = @_;
1657              
1658             if ( $sym->{info}->{lens}->[0] ) {
1659              
1660             # rests
1661             $new_abc .= $sym->{flags} & ABC_F_INVIS ? 'x' : 'z';
1662             $new_abc =
1663             _length_dump( $new_abc, _broken_rhythm( $sym->{info}->{lens}->[0] ) );
1664             } else {
1665              
1666             # additional spacing
1667             $new_abc .= 'y';
1668             if ( $sym->{info}->{lens}->[1] >= 0 ) {
1669             $new_abc .= $sym->{info}->{lens}->[1];
1670             }
1671             }
1672              
1673             return $new_abc;
1674             }
1675              
1676             # -- set the duration of all notes/rests/mrests
1677             sub _set_durations {
1678             my ( $tunes_ref, $tune ) = @_;
1679             my $n_symbols = scalar( @{ ${$tunes_ref}->{$tune}->{symbols} } ) - 1;
1680             my %v_i = (); # current voice's info
1681             my $c = $IMPLICIT_VOICE; # current voice
1682              
1683             #FIXME ver se consigo deixar de usar o ${$s} e passar a usar so $s
1684             # sets the duration of all notes/rests without regard for tuplets - this is needed for tuplets
1685             for my $ix ( 0 .. $n_symbols ) {
1686             my $s = \${$tunes_ref}->{$tune}->{symbols}->[$ix];
1687             given ( ${$s}->{type} ) {
1688             when (ABC_T_INFO) {
1689             given ( substr ${$s}->{text}, 0, 1 ) {
1690             when ('V') { # Voice
1691             if ( ${$s}->{state} ~~ [ABC_S_TUNE, ABC_S_EMBED] ) {
1692             $c = ${$s}->{info}->{voice};
1693             $v_i{$c}{meter}{wmeasure} ||= BASE_LEN;
1694             }
1695             }
1696             when ('M') { # Meter
1697             if ( ${$s}->{state} ~~ [ ABC_S_HEAD, ABC_S_TUNE ] ) {
1698             $v_i{$c}{meter}{wmeasure} = ${$s}->{info}->{wmeasure};
1699             }
1700             }
1701             }
1702             }
1703             when ( [ ABC_T_NOTE, ABC_T_REST ] ) {
1704             ${$s}->{info}->{dur} = ${$s}->{info}->{lens}->[0]
1705             }
1706             when (ABC_T_MREST) {
1707             my $dur = $v_i{$c}{meter}{wmeasure} * ${$s}->{info}->{len};
1708             ${$s}->{info}->{dur} = $dur;
1709             }
1710             }
1711             }
1712              
1713             return;
1714             }
1715              
1716             # sets the real duration for notes and rests inside a tuplet
1717             # updates the time offset
1718             # sets bar numbers on notes, rests, mrests and bars
1719             sub _set_tuplet_time_and_bars {
1720             my ( $tunes_ref, $tune ) = @_;
1721             my $n_symbols = scalar( @{ ${$tunes_ref}->{$tune}->{symbols} } ) - 1;
1722             my $c = $IMPLICIT_VOICE; # current voice
1723             my %v_i = (); # current voice's info
1724             $v_i{$c}{meter}{wmeasure} ||= BASE_LEN;
1725             $v_i{$c}{bar}{num} ||= int $FIRST_MEASURE;
1726             $v_i{$c}{bar}{time} ||= 0;
1727             $v_i{$c}{time} ||= 0;
1728              
1729             for my $ix ( 0 .. $n_symbols ) {
1730             my $s = ${$tunes_ref}->{$tune}->{symbols}->[$ix];
1731              
1732             given ( $s->{type} ) {
1733             when (ABC_T_INFO) {
1734             given ( substr $s->{text}, 0, 1 ) {
1735             when ('V') { # Voice
1736             if ( $s->{state} ~~ [ABC_S_TUNE, ABC_S_EMBED] ) {
1737             $c = $s->{info}->{voice};
1738             $v_i{$c}{meter}{wmeasure} ||= BASE_LEN;
1739             $v_i{$c}{bar}{num} ||= int $FIRST_MEASURE;
1740             $v_i{$c}{bar}{time} ||= 0;
1741             $v_i{$c}{time} ||= 0;
1742             }
1743             }
1744             when ('M') { # Meter
1745             if ( $s->{state} ~~ [ ABC_S_HEAD, ABC_S_TUNE ] ) {
1746             $v_i{$c}{meter}{wmeasure} = $s->{info}->{wmeasure};
1747             }
1748             }
1749             }
1750             }
1751             when (ABC_T_TUPLET) {
1752             _set_tuplet( $tunes_ref, $tune, $ix, $s );
1753             }
1754             }
1755              
1756             # sets the time offset on notes/rest/mrests/bars
1757             _set_time_offset(\$s, \$v_i{$c}{bar}{time});
1758              
1759             given ( $s->{type} ) {
1760             when (ABC_T_BAR) {
1761             # for incomplete measures
1762             $v_i{$c}{bar}{time} ||= $v_i{$c}{meter}{wmeasure};
1763              
1764             # increments bar number only if it isn't an incomplete measure
1765             if ( $s->{info}->{type} != B_OBRA and $s->{info}->{time} >= $v_i{$c}{bar}{time} ) { $v_i{$c}{bar}{num}++ }
1766             $s->{info}->{bar_num} = $v_i{$c}{bar}{num};
1767              
1768             # updates the new measure's bar time
1769             $v_i{$c}{bar}{time} = $s->{info}->{time} + $v_i{$c}{meter}{wmeasure};
1770             }
1771             when ( [ ABC_T_NOTE, ABC_T_REST ] ) {
1772             $s->{info}->{bar_num} = $v_i{$c}{bar}{num};
1773             }
1774             when (ABC_T_MREST) {
1775             $s->{info}->{bar_num} = $v_i{$c}{bar}{num};
1776             $v_i{$c}{bar}{num} += ($s->{info}->{len} - 1);
1777             }
1778             }
1779             }
1780              
1781             return;
1782             }
1783              
1784             # -- set the duration of notes/rests in a tuplet
1785             # FIXME: KO if voice change
1786             # FIXME: KO if in a grace sequence
1787             # TODO : finish nested tuples (there's a detail in the C version that i don't understand)
1788             sub _set_tuplet {
1789             my ( $tunes_ref, $tune, $sym_ix, $sym ) = @_;
1790              
1791             my $as;
1792             my $s;
1793             my $lplet;
1794             my $r = $sym->{info}->{r_plet};
1795             my $grace = $sym->{flags} & ABC_F_GRACE;
1796             my $c_tune_local = ${$tunes_ref}->{$tune};
1797              
1798             my $l = 0;
1799             my $ix = $sym_ix + 1;
1800             for ( $as = $c_tune_local->{symbols}->[$ix] ;
1801             ref $as ;
1802             $as = $c_tune_local->{symbols}->[ ++$ix ] )
1803             {
1804             # nested tuplet
1805             # if ( $as->{info}->{type} == ABC_T_TUPLET ) {
1806             # my $as2;
1807             # my $r2 = $as->{info}->{r_plet};
1808             # my $l2 = 0;
1809             # my $ix2 = $ix;
1810              
1811             # for ( $as2 = $c_tune_local->{symbols}->[$ix2] ;
1812             # ref $as2 ;
1813             # $as2 = $c_tune_local->{symbols}->[ ++$ix2 ] )
1814             # {
1815             # # checks for EOL in a tuplet
1816             # # switch (as2->type) {
1817             # # case ABC_T_NOTE:
1818             # # case ABC_T_REST:
1819             # # last;
1820             # # case ABC_T_EOLN:
1821             # # if (as2->u.eoln.type != 1) {
1822             # # error(1, t, "End of line found inside a nested tuplet");
1823             # # return;
1824             # # }
1825             # # continue;
1826             # # default:
1827             # # continue;
1828             # # }
1829             # next if ($as2->{info}->{lens}->[0] == 0); # space ('y')
1830             # next if ($grace ^ ($as2->{flags} & ABC_F_GRACE));
1831             # $s = $as2;
1832             # $l2 += $s->{info}->{dur};
1833             # last if (--$r2 <= 0);
1834             # }
1835             # $l2 = $l2 * $as->{info}->{q_plet} / $as->{info}->{p_plet};
1836             # #FIXME nao percebi o que faz a linha seguinte
1837             #((struct SYMBOL *) as)->u = l2;
1838             # $as->{info} = $l2;
1839             # $l += $l2;
1840             # #FIXME nao percebi a linha seguinte. O $as->u nao é um inteiro neste momento?
1841             #r -= as->u.tuplet.r_plet;
1842             # $r -= $as->{info}->{r_plet};
1843             # last if ($r == 0);
1844             # # if ($r < 0) {
1845             # # error(1, t, "Bad nested tuplet");
1846             # # last;
1847             # # }
1848             # $as = $as2;
1849             # next;
1850             # }
1851             # checks for eol inside of tuplet
1852             # switch (as->type) {
1853             # case ABC_T_NOTE:
1854             # case ABC_T_REST:
1855             # last;
1856             # case ABC_T_EOLN:
1857             # if (as->u.eoln.type != 1) {
1858             # error(1, t, "End of line found inside a tuplet");
1859             # return;
1860             # }
1861             # continue;
1862             # default:
1863             # continue;
1864             # }
1865             next if ($as->{info}->{lens}->[0] == 0); # space ('y')
1866             next if ($grace ^ ($as->{flags} & ABC_F_GRACE));
1867             $s = $as;
1868             $l += $s->{info}->{dur};
1869             last if (--$r <= 0);
1870             }
1871             # if ( not ref $as ) {
1872             # error(1, t, "End of tune found inside a tuplet");
1873             # return;
1874             # }
1875             # if (t->u != 0) # if nested tuplet */
1876             # lplet = t->u;
1877             # else
1878             $lplet = ($l * $sym->{info}->{q_plet}) / $sym->{info}->{p_plet};
1879              
1880             $r = $sym->{info}->{r_plet};
1881             $ix = $sym_ix + 1;
1882             for ( $as = $c_tune_local->{symbols}->[$ix] ;
1883             ref $as ;
1884             $as = $c_tune_local->{symbols}->[ ++$ix ] )
1885             {
1886             my $olddur;
1887              
1888             # nested tuplet
1889             # if ($as->{type} == ABC_T_TUPLET) {
1890             # int r2;
1891              
1892             # r2 = as->u.tuplet.r_plet;
1893             # s = (struct SYMBOL *) as;
1894             # olddur = s->u;
1895             # s->u = (olddur * lplet) / l;
1896             # l -= olddur;
1897             # lplet -= s->u;
1898             # r -= r2;
1899             # for (;;) {
1900             # as = as->next;
1901             # if (as->type != ABC_T_NOTE && as->type != ABC_T_REST)
1902             # continue;
1903             # if (as->u.note.lens[0] == 0)
1904             # continue;
1905             # if (grace ^ (as->flags & ABC_F_GRACE))
1906             # continue;
1907             # if (--r2 <= 0)
1908             # last;
1909             # }
1910             # if (r <= 0)
1911             # goto done;
1912             # continue;
1913             # }
1914             next if ( $as->{type} != ABC_T_NOTE && $as->{type} != ABC_T_REST );
1915             next if ( $as->{info}->{lens}->[0] == 0 ); # space ('y')
1916             next if ( $grace ^ ( $as->{flags} & ABC_F_GRACE ) );
1917              
1918             $s = $as;
1919             $olddur = $s->{info}->{dur};
1920             $s->{info}->{dur} = ( $olddur * $lplet ) / $l;
1921              
1922             #updates the real symbol
1923             ${ $tunes_ref }->{$tune}->{symbols}->[$ix]->{info}->{dur} = $s->{info}->{dur};
1924              
1925             last if ( --$r <= 0 );
1926              
1927             $l -= $olddur;
1928             $lplet -= $s->{info}->{dur};
1929             }
1930             # done:
1931             if ($grace) {
1932             # error(1, t, "Tuplets in grace note sequence not yet treated");
1933             }
1934              
1935             return;
1936             }
1937              
1938              
1939             # -- dump the slurs --
1940             sub _slur_dump {
1941             my ( $abc, $sl ) = @_;
1942             # FIXME when the slur is '(.(' it prints wrong, in other words, $sl is 31 so ($sl & SL_DOTTED = 4)
1943             # and it prints the '.' before the first '(';
1944             # moreover when the slur is '.((' then $sl = 59 and it prints '(.('
1945             do {
1946             if ( $sl & SL_DOTTED ) { $abc .= q{.} }
1947             $abc .= '(';
1948             given ( $sl & 0x03 ) {
1949             when (SL_ABOVE) { $abc .= q{'} }
1950             when (SL_BELOW) { $abc .= q{,} }
1951             }
1952             $sl >>= 3; # in case there's more than are consecutive slurs
1953             } while ($sl);
1954              
1955             return $abc;
1956             }
1957              
1958             # -- dump chord start's symbol
1959             sub _start_chord {
1960             my ( $sym, $new_abc, $all_tie ) = @_;
1961              
1962             if ( $sym->{info}->{nhd} > 0 ) { # the current symbol is a chord
1963             my $i;
1964             # for each note in the chord
1965             for ( $i = $sym->{info}->{nhd} ; $i >= 0 ; $i-- ) {
1966             # for my $i ( reverse 0 .. $sym->{info}->{nhd} ) {
1967              
1968             # exits loop if there are no ties starting at the note
1969             last if ( !$sym->{info}->{ti1}->[$i] );
1970             }
1971              
1972             # ties all notes from the chord if there are no ties starting in an individual note
1973             if ( $i < 0 ) { $all_tie = $sym->{info}->{ti1}->[0] }
1974             $new_abc .= '[';
1975             }
1976              
1977             return ( $new_abc, $all_tie );
1978             }
1979              
1980             # -- dump tempo
1981             sub _tempo_header_dump {
1982             my ( $abc, $sym ) = @_;
1983              
1984             # FIXME PARSER when Q: is defined in the header, length and value of the generated structure are
1985             # not being set. they are only when Q: is defined in the body like [Q: "Allegro" 1/4=120]
1986             # FIXME ver o que acontece quando se deixa um espaco entre Q: e o resto
1987             $abc .= 'Q:';
1988              
1989             #prints string before
1990             if ( $sym->{info}->{str1} ne q{} ) {
1991             $abc .= sprintf '"%s" ', $sym->{info}->{str1};
1992             }
1993              
1994             #prints tempo value
1995             if ( $sym->{info}->{value} ne q{} ) {
1996             my ( $top, $bot );
1997              
1998             foreach my $i ( 0 .. ( scalar @{ $sym->{info}->{length} } ) - 1 ) {
1999             next if ( ( $top = $sym->{info}->{length}->[$i] ) == 0 );
2000             $bot = 1;
2001             while (1) {
2002             if ( $top % BASE_LEN == 0 ) {
2003             $top /= BASE_LEN;
2004             last;
2005             }
2006             $top *= 2;
2007             $bot *= 2;
2008             }
2009             $abc .= sprintf '%d/%d ', $top, $bot; # prints top/bot
2010             }
2011              
2012             # removes last character if it is a white space
2013             if ( substr( $abc, length($abc) - 1, 1 ) eq q{ } ) {
2014             $abc = substr $abc, 0, -1;
2015             }
2016             $abc .= sprintf '=%s ', $sym->{info}->{value};
2017             }
2018              
2019             # prints string after
2020             if ( $sym->{info}->{str2} ne q{} ) {
2021             $abc .= sprintf '"%s"', $sym->{info}->{str2};
2022             } elsif ( substr( $abc, length($abc) - 1, 1 ) eq q{ } ) {
2023              
2024             # erases white space at the end
2025             $abc = substr $abc, 0, -1;
2026             }
2027              
2028             return $abc;
2029             }
2030              
2031             # -- return abc of tuplet
2032             sub _tuplet_to_abc {
2033             my ( $new_abc, $sym ) = @_;
2034              
2035             my ( $pp, $qp, $rp ) =
2036             ( $sym->{info}->{p_plet}, $sym->{info}->{q_plet}, $sym->{info}->{r_plet} );
2037              
2038             $new_abc .= sprintf '(%d', $pp;
2039              
2040             if ( ( $pp != 2 || $qp != 3 || $rp != 2 ) # (2ab <=> (2:3:2ab
2041             && ( $pp != 3 || $qp != 2 || $rp != 3 ) ) # (3abc <=> (3:2:3abc
2042             {
2043             $new_abc .= sprintf ':%d:%d', $qp, $rp;
2044             }
2045              
2046             return $new_abc;
2047             }
2048              
2049             # -- update global variables of the score (voice, key, tempo, length and meter)
2050             sub _update_score_variables {
2051             my ( $tunes_ref, $tune, $sym ) = @_;
2052              
2053             given ( $sym->{type} ) {
2054             when (ABC_T_INFO) {
2055             _get_info($sym);
2056             }
2057             when (ABC_T_MREP) {
2058             #Moine: mrep was an experimental extension done by "|/|" or "|//|". It does not appear in any
2059             #ABC standard and should be removed.
2060             }
2061             when (ABC_T_V_OVER) {
2062             #abcm2ps-7.3.4/parse.c:3011
2063             #TODO fazer vover
2064             }
2065             default {}
2066             }
2067              
2068             return;
2069             }
2070              
2071             # Sets the time offset into the symbol
2072             sub _set_time_offset {
2073             my ( $s, $time ) = @_;
2074              
2075             given ( ${$s}->{type} ) {
2076             when ( [ ABC_T_NOTE, ABC_T_REST ] ) {
2077             if ( !( ${$s}->{flags} & ABC_F_GRACE ) ) {
2078             ${$s}->{info}->{time} = $$time;
2079             $$time += ${$s}->{info}->{dur};
2080             }
2081             # FIXME atencao ao v_over, nao pode contar da mesma maneira
2082             }
2083             when (ABC_T_MREST) {
2084             #abcm2ps-7.3.4/parse.c:2953
2085             ${$s}->{info}->{time} = $$time;
2086             $$time += ${$s}->{info}->{dur};
2087             }
2088             when (ABC_T_BAR) {
2089             ${$s}->{info}->{time} = $$time;
2090             }
2091             }
2092              
2093             return;
2094             }
2095              
2096             # Updates the time offset for voice $c_voice
2097             sub _update_time_offset {
2098             if ( $sym->{type} ~~ [ ABC_T_NOTE, ABC_T_REST ] ) {
2099             if ( !( $sym->{flags} & ABC_F_GRACE ) ) {
2100             $voice_struct{$c_voice}{time} += $sym->{info}->{dur};
2101             }
2102             # FIXME atencao ao v_over, nao pode contar da mesma maneira
2103             }
2104             if ( $sym->{type} == ABC_T_MREST ) {
2105             #abcm2ps-7.3.4/parse.c:2953
2106             $voice_struct{$c_voice}{time} += $sym->{info}->{dur};
2107             }
2108              
2109             return;
2110             }
2111              
2112             # -- dump voice
2113             sub _voice_header_dump {
2114             my ( $abc, $sym ) = @_;
2115              
2116             # FIXME PARSER quando no abc a voz de uma melodia está no formato "V: id\nABCD|z4" (note-se o espaço
2117             # entre "V:" e id), a voz nao é identificada logo o id e a voice nao sao definidos
2118             $abc .= sprintf 'V:%s', $sym->{info}->{id};
2119             if ( $sym->{info}->{fname} ne q{} ) {
2120             $abc .= sprintf ' name="%s"', $sym->{info}->{fname};
2121             }
2122             if ( $sym->{info}->{nname} ne q{} ) {
2123             $abc .= sprintf ' sname="%s"', $sym->{info}->{nname};
2124             }
2125             if ( $sym->{info}->{merge} ) { $abc .= ' merge' }
2126             if ( $sym->{info}->{stem} ) {
2127             $abc .= sprintf ' stem=%s', _head_par( $sym->{info}->{stem} );
2128             }
2129             if ( $sym->{info}->{gstem} ) {
2130             $abc .= sprintf ' gstem=%s', _head_par( $sym->{info}->{gstem} );
2131             }
2132             if ( $sym->{info}->{dyn} ) {
2133             $abc .= sprintf ' dyn=%s', _head_par( $sym->{info}->{dyn} );
2134             }
2135             if ( $sym->{info}->{lyrics} ) {
2136             $abc .= sprintf ' lyrics=%s', _head_par( $sym->{info}->{lyrics} );
2137             }
2138             if ( $sym->{info}->{gchord} ) {
2139             $abc .= sprintf ' gchord=%s', _head_par( $sym->{info}->{gchord} );
2140             }
2141             if ( $sym->{info}->{scale} ) {
2142             $abc .= sprintf ' scale=%.2f', $sym->{info}->{scale};
2143             }
2144              
2145             # print next symbol if it is a clef
2146             if ( ref( $c_tune->{symbols}->[ $c_sym_ix + 1 ] )
2147             && $c_tune->{symbols}->[ $c_sym_ix + 1 ]->{type} == ABC_T_CLEF )
2148             {
2149             $abc = _clef_dump( $abc, $c_tune->{symbols}->[ $c_sym_ix + 1 ] );
2150             }
2151              
2152             return $abc;
2153             }
2154              
2155             # -- return abc of voice overlay
2156             sub _vover_to_abc {
2157             my ( $new_abc, $sym ) = @_;
2158              
2159             given ( $sym->{info}->{type} ) {
2160             when (V_OVER_V) { $new_abc .= q{&}; }
2161             when (V_OVER_S) { $new_abc .= '(&'; }
2162             when (V_OVER_E) { $new_abc .= '&)'; }
2163             }
2164              
2165             return $new_abc;
2166             }
2167              
2168              
2169             ################################### Chord.pm ################################
2170              
2171             # -- Returns the (first) pitch at the provided scaleDegree (chordStep)
2172             # Returns undef if none can be found.
2173             sub get_chord_step {
2174             my ( $sym, $chord_step, $test_root ) = @_;
2175              
2176             if ( !$test_root ) {
2177             $test_root = root($sym);
2178             if ( !$test_root ) {
2179             die "Cannot run get_chord_step without a root\n";
2180             }
2181             }
2182              
2183             for my $note_ref ( _get_chord_notes($sym) ) {
2184             my ( $d_int, $c_int ) = _notes_to_interval( $test_root, $note_ref );
2185             my $g_int_info = _get_generic_info( $d_int->{generic} );
2186             if ( $g_int_info->{mod7} == $chord_step ) {
2187             return $note_ref;
2188             }
2189             }
2190              
2191             return;
2192             }
2193              
2194             # -- Shortcut for getChordStep(5)
2195             sub get_fifth {
2196             my $sym = shift;
2197              
2198             return get_chord_step($sym, 5);
2199             }
2200              
2201             # -- Shortcut for getChordStep(7)
2202             sub get_seventh {
2203             my $sym = shift;
2204              
2205             return get_chord_step($sym, 7);
2206             }
2207              
2208             # -- Shortcut for getChordStep(3)
2209             sub get_third {
2210             my $sym = shift;
2211              
2212             return get_chord_step($sym, 3);
2213             }
2214              
2215              
2216             # -- Returns True if chord is a Dominant Seventh, that is, if it contains only notes that are
2217             # either in unison with the root, a major third above the root, a perfect fifth, or a major
2218             # seventh above the root. Additionally, must contain at least one of each third and fifth
2219             # above the root. Chord must be spelled correctly. Otherwise returns false.
2220             sub is_dominant_seventh {
2221             my $sym = shift;
2222              
2223             my $third = get_third($sym);
2224             my $fifth = get_fifth($sym);
2225             my $seventh = get_seventh($sym);
2226              
2227             return 0 if ( not $third or not $fifth or not $seventh );
2228              
2229             for my $note_ref ( _get_chord_notes($sym) ) {
2230             my ( $d_int, $c_int ) = _notes_to_interval( root($sym), $note_ref );
2231             my $c_int_info = _get_chromatic_info($c_int);
2232             # if there's a note that doesn't belong to a dominant seventh (root:0, major third:4, a perfect
2233             # fifth:7 and a minor seventh:10) then returns false
2234             if ( ( $c_int_info->{mod12} != 0 )
2235             && ( $c_int_info->{mod12} != 4 )
2236             && ( $c_int_info->{mod12} != 7 )
2237             && ( $c_int_info->{mod12} != 10 ) )
2238             {
2239             return 0;
2240             }
2241             }
2242              
2243             return 1;
2244             }
2245              
2246             # -- Returns True if chord is a Minor Triad, that is, if it contains only notes that are
2247             # either in unison with the root, a minor third above the root, or a perfect fifth above the
2248             # root. Additionally, must contain at least one of each third and fifth above the root.
2249             # Chord must be spelled correctly. Otherwise returns false.
2250             sub is_minor_triad {
2251             my $sym = shift;
2252              
2253             my $third = get_third($sym);
2254             my $fifth = get_fifth($sym);
2255              
2256             return 0 if ( not $third or not $fifth );
2257              
2258             for my $note_ref ( _get_chord_notes($sym) ) {
2259             my ( $d_int, $c_int ) = _notes_to_interval( root($sym), $note_ref );
2260             my $c_int_info = _get_chromatic_info($c_int);
2261             # if there's a note that doesn't belong to a major triad (root:0, minor third:3 and a perfect
2262             # fifth:7) then returns false
2263             if ( ( $c_int_info->{mod12} != 0 )
2264             && ( $c_int_info->{mod12} != 3 )
2265             && ( $c_int_info->{mod12} != 7 ) )
2266             {
2267             return 0;
2268             }
2269             }
2270              
2271             return 1;
2272             }
2273              
2274             # -- Returns True if chord is a Major Triad, that is, if it contains only notes that are
2275             # either in unison with the root, a major third above the root, or a perfect fifth above the
2276             # root. Additionally, must contain at least one of each third and fifth above the root.
2277             # Chord must be spelled correctly. Otherwise returns false.
2278             sub is_major_triad {
2279             my $sym = shift;
2280              
2281             my $third = get_third($sym);
2282             my $fifth = get_fifth($sym);
2283              
2284             return 0 if ( not $third or not $fifth );
2285              
2286             for my $note_ref ( _get_chord_notes($sym) ) {
2287             my ( $d_int, $c_int ) = _notes_to_interval( root($sym), $note_ref );
2288             my $c_int_info = _get_chromatic_info($c_int);
2289             # if there's a note that doesn't belong to a major triad (root:0, major third:4 and a perfect
2290             # fifth:7) then returns false
2291             if ( ( $c_int_info->{mod12} != 0 )
2292             && ( $c_int_info->{mod12} != 4 )
2293             && ( $c_int_info->{mod12} != 7 ) )
2294             {
2295             return 0;
2296             }
2297             }
2298              
2299             return 1;
2300             }
2301              
2302             # -- Looks for the root by finding the note with the most 3rds above it
2303             sub root {
2304             my $sym = shift;
2305             my @old_roots = _get_chord_notes($sym); # note_refs
2306             my @new_roots = ();
2307             my $roots = 0;
2308             my $n = 3;
2309              
2310             while (1) {
2311             if ( scalar @old_roots == 1 ) {
2312             return $old_roots[0];
2313             } elsif ( scalar @old_roots == 0 ) {
2314             die "No notes in chord\n";
2315             }
2316             for my $test_root (@old_roots) {
2317             if ( get_chord_step( $sym, $n, $test_root ) ) { ##n>7 = bug
2318             push @new_roots, $test_root;
2319             $roots++;
2320             }
2321             }
2322             if ( $roots == 1 ) { return pop @new_roots; }
2323             elsif ( $roots == 0 ) { return $old_roots[0]; }
2324             @old_roots = @new_roots;
2325             @new_roots = ();
2326             $n += 2;
2327             if ( $n > 7 ) { $n -= 7; }
2328             if ( $n == 6 ) {
2329             die "looping chord with no root: comprises all notes in the scale\n";
2330             }
2331             $roots = 0;
2332             }
2333              
2334             return;
2335             }
2336              
2337             ########## Chord.pm PRIVATE FUNCTIONS ##########
2338              
2339             # -- Returns an array containing a chord's notes
2340             # Each note is composed of its pits and accs
2341             sub _get_chord_notes {
2342             my $sym = shift;
2343              
2344             my @notes = ();
2345             for my $ix ( 0 .. $sym->{info}->{nhd} ) {
2346             push @notes,
2347             {
2348             pits => $sym->{info}->{pits}->[$ix],
2349             accs => $sym->{info}->{accs}->[$ix]
2350             };
2351             }
2352              
2353             return @notes;
2354             }
2355              
2356             ################################### Interval.pm ################################
2357              
2358             our %STEPREF = (
2359             'C' => 0,
2360             'D' => 2,
2361             'E' => 4,
2362             'F' => 5,
2363             'G' => 7,
2364             'A' => 9,
2365             'B' => 11,
2366             );
2367             our @STEPNAMES = qw(C D E F G A B);
2368             our @PREFIXSPECS =
2369             ( undef, 'P', 'M', 'm', 'A', 'd', 'AA', 'dd', 'AAA', 'ddd', 'AAAA', 'dddd' );
2370              
2371             Readonly our $OBLIQUE => 0;
2372             Readonly our $ASCENDING => 1;
2373             Readonly our $DESCENDING => -1;
2374              
2375             # constants provide the common numerical representation of an interval.
2376             # this is not the number of half tone shift.
2377              
2378             Readonly our $PERFECT => 1;
2379             Readonly our $MAJ => 2;
2380             Readonly our $MIN => 3;
2381             Readonly our $AUGMENTED => 4;
2382             Readonly our $DIMINISHED => 5;
2383             Readonly our $DBLAUG => 6;
2384             Readonly our $DBLDIM => 7;
2385             Readonly our $TRPAUG => 8;
2386             Readonly our $TRPDIM => 9;
2387             Readonly our $QUADAUG => 10;
2388             Readonly our $QUADDIM => 11;
2389              
2390             # ordered list of perfect specifiers
2391             our @PERFSPECIFIERS = (
2392             $QUADDIM, $TRPDIM, $DBLDIM,
2393             $DIMINISHED, $PERFECT, $AUGMENTED,
2394             $DBLAUG, $TRPAUG, $QUADAUG,
2395             );
2396             Readonly our $PERFOFFSET => 4; # that is, Perfect is third on the list.s
2397              
2398             # ordered list of imperfect specifiers
2399             our @IMPERFSPECIFIERS = (
2400             $QUADDIM, $TRPDIM, $DBLDIM, $DIMINISHED,
2401             $MIN, $MAJ, $AUGMENTED, $DBLAUG,
2402             $TRPAUG, $QUADAUG,
2403             );
2404             Readonly our $MAJOFFSET => 5; # index of Major
2405              
2406             # -- Returns an integer of the generic interval number
2407             # (P5 = 5, M3 = 3, minor 3 = 3 also) etc. from the given staff distance
2408             sub _convert_staff_distance_to_interval {
2409             my $staff_dist = shift;
2410              
2411             my $gen_dist = $staff_dist == 0 ? 1
2412             : $staff_dist > 0 ? $staff_dist + 1
2413             : $staff_dist - 1;
2414              
2415             return $gen_dist;
2416             }
2417              
2418             # -- Returns a diatonic interval, composed of a specifier followed by a generic interval
2419             sub _diatonic_interval {
2420             my ( $specifier, $generic ) = @_;
2421             my $name = q{};
2422              
2423             if ( $specifier && $generic ) {
2424             $name = "$PREFIXSPECS[$specifier]" . abs $generic;
2425             }
2426              
2427             my $d_int = { name => $name, specifier => $specifier, generic => $generic };
2428              
2429             return $d_int;
2430             }
2431              
2432             # -- Returns the pitch alteration as a numeric value, where 1 is the space of one half step and all
2433             # base pitch values are given by step alone.
2434             sub _get_alter {
2435             my $acc = shift;
2436             my $alter;
2437              
2438             given ($acc) {
2439             when ( [ 0, 2 ] ) { $alter = 0; }
2440             when (1) { $alter = 1; }
2441             when (3) { $alter = -1; }
2442             when (4) { $alter = 2; }
2443             when (5) { $alter = -2; }
2444             }
2445              
2446             return $alter;
2447             }
2448              
2449             # -- Extracts information related to a chromatic interval
2450             sub _get_chromatic_info {
2451             my $c_int = shift;
2452             my $c_int_info = {};
2453              
2454             my $directed = $c_int;
2455             my $undirected = abs $c_int;
2456              
2457             $c_int_info->{semitones} = $directed;
2458             $c_int_info->{directed} = $directed;
2459             $c_int_info->{undirected} = $undirected;
2460              
2461             my $direction = $directed == 0 ? $OBLIQUE
2462             : $directed == $undirected ? $ASCENDING
2463             : $DESCENDING;
2464             $c_int_info->{direction} = $direction;
2465              
2466             $c_int_info->{mod12} = $c_int_info->{semitones} % 12;
2467              
2468             return $c_int_info;
2469             }
2470              
2471             # -- Extracts information related to a generic interval
2472             sub _get_generic_info {
2473             my $g_int = shift;
2474             my $g_int_info = {};
2475              
2476             my $directed = $g_int;
2477             my $undirected = abs $g_int;
2478             $g_int_info->{directed} = $directed;
2479             $g_int_info->{undirected} = $undirected;
2480              
2481             if ( $directed == 0 ) { die "The Zeroth is not an interval\n"; }
2482             my $direction = $directed == 1 ? $OBLIQUE
2483             : $directed == $undirected ? $ASCENDING
2484             : $DESCENDING;
2485             $g_int_info->{direction} = $direction;
2486              
2487             # unisons (even augmented) are neither steps nor skips.
2488             my ( $steps, $octaves ) = POSIX::modf( $undirected / 7 );
2489             $steps = int( $steps * 7 + .001 );
2490             $octaves = int $octaves;
2491             if ( $steps == 0 ) {
2492             $octaves--;
2493             $steps = 7;
2494             }
2495             $g_int_info->{simpleUndirected} = $steps;
2496              
2497             # semiSimpleUndirected, same as simple, but P8 != P1
2498             $g_int_info->{semiSimpleUndirected} = $steps;
2499             $g_int_info->{undirectedOctaves} = $octaves;
2500              
2501             if ($steps == 1 and $octaves >= 1) {
2502             $g_int_info->{semiSimpleUndirected} = 8;
2503             }
2504              
2505             if ($g_int_info->{direction} == $DESCENDING) {
2506             $g_int_info->{octaves} = -1 * $octaves;
2507             if ($steps != 1) {
2508             $g_int_info->{simpleDirected} = -1 * $steps;
2509             } else {
2510             $g_int_info->{simpleDirected} = 1; # no descending unisons...
2511             }
2512             $g_int_info->{semiSimpleDirected} = -1 * $g_int_info->{semiSimpleUndirected};
2513             } else {
2514             $g_int_info->{octaves} = $octaves;
2515             $g_int_info->{simpleDirected} = $steps;
2516             $g_int_info->{semiSimpleDirected} = $g_int_info->{semiSimpleUndirected};
2517             }
2518              
2519             my $perfectable;
2520             if ( $steps == 1 || $steps == 4 || $steps == 5 ) {
2521             $perfectable = 1;
2522             } else {
2523             $perfectable = 0;
2524             }
2525             $g_int_info->{perfectable} = $perfectable;
2526              
2527             # 2 -> 7; 3 -> 6; 8 -> 1 etc.
2528             $g_int_info->{mod7inversion} = 9 - $g_int_info->{semiSimpleUndirected};
2529              
2530             $g_int_info->{mod7} =
2531             $g_int_info->{direction} == $DESCENDING
2532             ? $g_int_info->{mod7inversion}
2533             : $g_int_info->{simpleDirected};
2534              
2535             return $g_int_info;
2536             }
2537              
2538             # -- Given a generic interval and a chromatic interval (scalar values),
2539             # returns a specifier (i.e. MAJ, MIN, etc...).
2540             sub _get_specifier_from_generic_chromatic {
2541             my ( $g_int, $c_int ) = @_;
2542             my $specifier;
2543              
2544             my $g_int_info = _get_generic_info($g_int);
2545             my $c_int_info = _get_chromatic_info($c_int);
2546              
2547             my @note_vals = (undef, 0, 2, 4, 5, 7, 9, 11);
2548             my $normal_semis = $note_vals[ $g_int_info->{simpleUndirected} ] + 12 * $g_int_info->{undirectedOctaves};
2549              
2550             my $these_semis;
2551             if ( $g_int_info->{direction} != $c_int_info->{direction}
2552             && $g_int_info->{direction} != $OBLIQUE
2553             && $c_int_info->{direction} != $OBLIQUE )
2554             {
2555             # intervals like d2 (second diminished) and dd2 (second double diminished) etc. (the last test
2556             # doesn't matter, since -1*0 == 0, but in theory it should be there)
2557             $these_semis = -1 * $c_int_info->{undirected};
2558             } else {
2559             # all normal intervals
2560             $these_semis = $c_int_info->{undirected};
2561             }
2562              
2563             # round out microtones
2564             my $semis_rounded = int( sprintf( '%.0f', $these_semis ) );
2565              
2566             if ( $g_int_info->{perfectable} ) {
2567             $specifier = $PERFSPECIFIERS[ $PERFOFFSET + $semis_rounded - $normal_semis ];
2568             # raise IntervalException("cannot get a specifier for a note with this many semitones off of Perfect: " + str(these_semis - normal_semis))
2569             } else {
2570             $specifier = $IMPERFSPECIFIERS[ $MAJOFFSET + $semis_rounded - $normal_semis ];
2571             # raise IntervalException("cannot get a specifier for a note with this many semitones off of Major: " + str(these_semis - normal_semis))
2572             }
2573              
2574             return $specifier;
2575             }
2576              
2577             # -- Given a generic interval and a chromatic interval, returns a diatonic interval and a chromatic interval
2578             sub _interval_from_generic_and_chromatic {
2579             my ( $g_int, $c_int ) = @_;
2580              
2581             my $specifier = _get_specifier_from_generic_chromatic( $g_int, $c_int );
2582             my $d_int = _diatonic_interval( $specifier, $g_int );
2583              
2584             return ($d_int, $c_int);
2585             }
2586              
2587             # -- Given two notes, it returns the chromatic interval
2588             # It treats interval spaces in half-steps. So Major 3rd and Diminished 4th are the same.
2589             sub _notes_to_chromatic {
2590             my ( $note1_ref, $note2_ref ) = @_;
2591              
2592             my $ps1 = _get_ps($note1_ref);
2593             my $ps2 = _get_ps($note2_ref);
2594              
2595             # returns chromatic interval in ps
2596             return $ps2 - $ps1;
2597             }
2598              
2599             # -- Given two notes, it returns the generic interval
2600             sub _notes_to_generic {
2601             my ( $note1_ref, $note2_ref ) = @_;
2602              
2603             my $pits1 = $note1_ref->{pits};
2604             my $pits2 = $note2_ref->{pits};
2605              
2606             return _convert_staff_distance_to_interval( $pits2 - $pits1 );
2607             }
2608              
2609             # -- Given two notes, it returns an interval
2610             sub _notes_to_interval {
2611             my ( $note1_ref, $note2_ref ) = @_;
2612              
2613             if (!ref $note2_ref) {
2614             #default note => C
2615             $note2_ref->{pits} = 16;
2616             $note2_ref->{accs} = 0;
2617             }
2618             my $g_int = _notes_to_generic($note1_ref, $note2_ref);
2619             my $c_int = _notes_to_chromatic($note1_ref, $note2_ref);
2620              
2621             # returns ( diatonic_interval, chromatic_interval)
2622             return _interval_from_generic_and_chromatic($g_int, $c_int);
2623             }
2624              
2625             # -- Creates a simpler note structure than abc's
2626             sub _simplify_note {
2627             my $abc_note = shift;
2628             my $simplified_note = {
2629             pits => $abc_note->{info}->{pits}->[0],
2630             accs => $abc_note->{info}->{accs}->[0]
2631             };
2632              
2633             return $simplified_note;
2634             }
2635              
2636              
2637             ######################## Pitch.pm #########################
2638              
2639             # basic accidental code and string definitions
2640             our %ACCIDENTAL_NAME_TO_MODIFIER = (
2641             -4 => 'quadruple-flat',
2642             -3 => 'triple-flat',
2643             -2 => 'double-flat',
2644             -1.5 => 'one-and-a-half-flat',
2645             -1 => 'flat',
2646             -0.5 => 'half-flat',
2647             0 => 'natural',
2648             0.5 => 'half-sharp',
2649             1 => 'sharp',
2650             1.5 => 'one-and-a-half-sharp',
2651             2 => 'double-sharp',
2652             3 => 'triple-sharp',
2653             4 => 'quadruple-sharp',
2654             );
2655              
2656             # How many significant digits to keep in pitch space resolution where 1 is a half
2657             # step. this means that 4 significant digits of cents will be kept
2658             Readonly our $PITCH_SPACE_SIG_DIGITS => 6;
2659              
2660              
2661             # -- Returns the pitch class of the note.
2662             # The pitch_class is a number from 0-11, where 0 = C, 1 = C#/D-, etc.
2663             sub get_pitch_class {
2664             my $note_ref = shift;
2665              
2666             my $pitch_class = _get_ps($note_ref);
2667              
2668             return $pitch_class % 12;
2669             }
2670              
2671              
2672             # Returns the pitch name of a note: A-flat, C-sharp
2673             sub get_pitch_name {
2674             my $note = shift;
2675              
2676             my ( $step, $acc, $micro ) = _convert_ps_to_step( _get_ps($note) );
2677              
2678             my $pitch_name = "$step-$ACCIDENTAL_NAME_TO_MODIFIER{$acc}";
2679              
2680             return $pitch_name;
2681             }
2682              
2683              
2684             ########## Chord.pm PRIVATE FUNCTIONS ##########
2685              
2686             sub _calculate_alter_micro {
2687             my $micro = shift;
2688             my $alter;
2689              
2690             # if close enough to a quarter tone
2691             if ( sprintf( '%.1f', $micro ) == 0.5 ) {
2692             # if can round to .5, than this is a quartertone accidental
2693             $alter = 0.5;
2694             # need to find microtonal alteration around this value
2695             # of alter is 0.5 and micro is .7 than micro should be .2
2696             # of alter is 0.5 and micro is .4 than micro should be -.1
2697             $micro = $micro - $alter;
2698             }
2699             # if greater than .5
2700             elsif ( $micro > 0.25 and $micro < 0.75 ) {
2701             $alter = 0.5;
2702             $micro = $micro - $alter;
2703             }
2704             # if closer to 1, than go to the higher alter and get negative micro
2705             elsif ( $micro >= 0.75 and $micro < 1 ) {
2706             $alter = 1;
2707             $micro = $micro - $alter;
2708             }
2709             # not greater than .25
2710             elsif ( $micro > 0 ) {
2711             $alter = 0;
2712             $micro = $micro; # no change necessary
2713             } else {
2714             $alter = 0;
2715             $micro = 0;
2716             }
2717              
2718             return ( $alter, $micro );
2719             }
2720              
2721             sub _calculate_name_acc {
2722             my ( $pc, $alter ) = @_;
2723             my $pc_name = 0;
2724             my $acc = 0;
2725              
2726             # check for unnecessary enharmonics
2727             if ( ( any { $_ == $pc } ( 4, 11 ) ) and $alter == 1 ) {
2728             $acc = 0;
2729             $pc_name = ( $pc + 1 ) % 12;
2730             }
2731             # its a natural; nothing to do
2732             elsif ( ( any { $_ == $pc } values %STEPREF ) ) {
2733             $acc = $alter;
2734             $pc_name = $pc;
2735             }
2736             # if we take the pc down a half-step, do we get a stepref (natural) value
2737             elsif ( ( any { $_ == ( $pc - 1 ) } ( 0, 5, 7 ) ) ) { # c, f, g: can be sharped
2738             # then we need an accidental to accommodate; here, a sharp
2739             $acc = 1 + $alter;
2740             $pc_name = $pc - 1;
2741             }
2742             # if we take the pc up a half-step, do we get a stepref (natural) value
2743             elsif ( ( any { $_ == ( $pc + 1 ) } ( 11, 4 ) ) ) { # b, e: can be flattened
2744             # then we need an accidental to accommodate; here, a flat
2745             $acc = (-1) + $alter;
2746             $pc_name = $pc + 1;
2747             }
2748             else {die "cannot match condition for pc: $pc\t($sym->{linenum}:$sym->{colnum})\n";}
2749              
2750             return ( $acc, $pc_name );
2751             }
2752              
2753             # Takes in a pitch space floating-point value
2754             # Returns a tuple of Step, an Accidental and a Microtone
2755             sub _convert_ps_to_step {
2756             my $ps = shift;
2757             my $alter;
2758             my $name;
2759              
2760             # rounding here is essential
2761             $ps = sprintf q{%.}.$PITCH_SPACE_SIG_DIGITS.'f', $ps;
2762             my $pc_real = $ps % 12;
2763              
2764             # micro here will be between 0 and 1
2765             my ( $pc, $micro ) = ( $pc_real / 1, POSIX::fmod( $pc_real, 1 ) );
2766              
2767             ( $alter, $micro ) = _calculate_alter_micro($micro);
2768              
2769             $pc = int $pc;
2770              
2771             my ( $acc, $pc_name ) = _calculate_name_acc( $pc, $alter );
2772              
2773             for my $key ( keys %STEPREF ) {
2774             if ( $pc_name == $STEPREF{$key} ) {
2775             $name = $key;
2776             last;
2777             }
2778             }
2779              
2780             # if a micro is present, create object, else return None
2781             $micro = $micro ? $micro * 100 # provide cents value; these are alter values
2782             : 0;
2783              
2784             return ($name, $acc, $micro);
2785             }
2786              
2787             # -- Calculates the pitch space number.
2788             # Returns a pitch space value as a floating point MIDI note number.
2789             sub _get_ps {
2790             my $note_ref = shift;
2791              
2792             # Simplifies the note symbol
2793             if ( $note_ref->{info} ) { $note_ref = _simplify_note($note_ref); }
2794              
2795             my $step_oct = _step_dump( $note_ref->{pits} ); # eg: C' g,,
2796             my $step = uc $step_oct;
2797             $step =~ s/[',]//gxms; # removes octave
2798              
2799             # default octave is 4 <=> C (pits 16)
2800             # if it's upper case ('C') then octave 4, else 5
2801             my $octave = $step_oct !~ /\p{IsLower}/xms ? 4 : 5;
2802             my @down = $step_oct =~ /,/gxms;
2803             $octave -= scalar @down;
2804             my @up = $step_oct =~ /'/gxms;
2805             $octave += scalar @up;
2806              
2807             my $ps = ( ( $octave + 1 ) * 12 ) + $STEPREF{$step};
2808              
2809             my $acc = $note_ref->{accs};
2810             if ($acc) { $ps += _get_alter($acc); }
2811             #FIXME ver como é com os microtones
2812             # if self.microtone is not None:
2813             # ps = ps + self.microtone.alter
2814              
2815             # FIXME ter em atencao os acidentes provenientes da armacao de clave (key) (usar info->{sf}),
2816             # compasso (talvez usar current measure in voice) e notas ligadas
2817             # TODO ver _key_header_dump para ver como lidar com explicit accidentals
2818              
2819             return $ps;
2820             }
2821              
2822             1; # End of Music::Abc::DT
2823              
2824             __END__