From dda8fcdc6f378ad4b4e2e5135b9c266088feaf7c Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Fri, 18 Aug 2000 00:25:13 +0200 Subject: [PATCH] release: 1.3.79 =========== * Made simple fix to get part-combiner to recognise different rhythms * Unhardcoded part combiner and a2-engraver; introduced properties splitInterval, soloADue, soloText, soloIIText, aDueText. * Fixed part combiner's too-late problem (Dankje!) * Fixed a2-devnull-engraver, a2-engraver. * Renamed part combiner music names to "one", "two". * Cleaned up decision making in part combiner, renamed to "solo"/"unison" 1.3.77. --- CHANGES | 26 +- Documentation/user/glossary.tely | 4 +- Documentation/user/properties.itely | 4 +- Documentation/user/refman.itely | 11 +- NEWS | 3 + VERSION | 4 +- input/bugs/almost.ly | 18 + input/test/hymn.ly | 16 +- lily/include/slur.hh | 1 - lily/mark-engraver.cc | 2 +- lily/slur.cc | 29 +- ly/engraver.ly | 5 +- make/out/lilypond.lsm | 8 +- make/out/lilypond.spec | 4 +- mutopia/Coriolan/flauti-part.ly | 25 +- mutopia/Coriolan/flauti.ly | 19 +- scm/slur.scm | 3 - scripts/etf2ly.py | 730 ++++++++++++++++++++++++++++ 18 files changed, 804 insertions(+), 108 deletions(-) create mode 100644 scripts/etf2ly.py diff --git a/CHANGES b/CHANGES index ebf487e4b0..e13358409d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,37 +1,30 @@ -1.3.78.jcn5 -=========== - -* Made crude fixes so unbound slurs don't crash, bezier still crashes on - infinitely steep slurs (eg flauti-part *with* slur_engraver). +* etf2ly.py: Finale to LilyPond conversion. 1.3.78.jcn4 =========== * Made simple fix to get part-combiner to recognise different rhythms -1.3.78.jcn3 -=========== - * Unhardcoded part combiner and a2-engraver; introduced properties splitInterval, soloADue, soloText, soloIIText, aDueText. -1.3.78.jcn2 -=========== - * Fixed part combiner's too-late problem (Dankje!) -1.3.78.jcn1 -=========== - * Fixed a2-devnull-engraver, a2-engraver. * Renamed part combiner music names to "one", "two". * Cleaned up decision making in part combiner, renamed to "solo"/"unison" +1.3.77.mb1 +========== + +* Minor documentation updates -1.3.77.jcn5 -=========== +* Mark_engraver: has been broken since .73, fixed! + +1.3.78 +====== * Added a devnull engraver, that junks (notehead) requests of the second stem when combined parts are in unison (a2). @@ -45,7 +38,6 @@ * First try at combining parts: part-combine* - * fixed problem with HaraKiri * fixed problem with dynamicDirection diff --git a/Documentation/user/glossary.tely b/Documentation/user/glossary.tely index 81e16eb771..96f04614f2 100644 --- a/Documentation/user/glossary.tely +++ b/Documentation/user/glossary.tely @@ -147,10 +147,10 @@ the duration of the long a. is proportionate to that of the main note. \key d \major; \time 4/4; r -{ \property Grace.stemStyle = "" +{ \property Grace.flagStyle = "" \grace g16 } fis8 e16 fis -{ \property Grace.stemStyle = "" +{ \property Grace.flagStyle = "" \grace a16 } g8 fis16 g | a4 \bar "||"; } \notes\relative c'' { diff --git a/Documentation/user/properties.itely b/Documentation/user/properties.itely index bf638acd41..670b0add03 100644 --- a/Documentation/user/properties.itely +++ b/Documentation/user/properties.itely @@ -87,7 +87,7 @@ TODO: @table @samp - @item @code{stemStyle}@indexcode{flagStyle} @propertytype{string} + @item @code{flagStyle}@indexcode{flagStyle} @propertytype{string} By default set to @code{"grace"} meaning that all unbeamed notes with flags are typeset with a slash through the flag. Setting to @code{""} gives standard flags. @@ -120,7 +120,7 @@ c'8 \property Voice.flagStyle = "grace" c'8 directions marked with `@code{^}' or `@code{_}' in the mudela file. @item @code{noAutoBeaming}@indexcode{beamAuto} @propertytype{boolean} - If set to 1 then beams are not generated automatically. + If set to true then beams are not generated automatically. @item @code{beamAutoEnd}@indexcode{beamAutoEnd} @propertytype{?} Specifies when automatically generated beams can end. See diff --git a/Documentation/user/refman.itely b/Documentation/user/refman.itely index 3d98430b1a..c606b6cb5b 100644 --- a/Documentation/user/refman.itely +++ b/Documentation/user/refman.itely @@ -1624,14 +1624,14 @@ example) have a separate time signature within grace notes. While in this score-within-a-score, you can create notes, beams, slurs, etc. Unbeamed eighth notes and shorter by default have a slash through the stem. This behavior can be controlled with the -@code{stemStyle}@indexcode{stemStyle} property. +@code{flagStyle}@indexcode{flagStyle} property. @quotation @mudela[fragment,verbatim] \relative c'' { \grace c8 c4 \grace { [c16 c16] } c4 - \grace { \property Grace.stemStyle = "" c16 } c4 + \grace { \property Grace.flagStyle = "" c16 } c4 } @end mudela @@ -2487,13 +2487,6 @@ Nevertheless, here are some variables you may want to use or change: @item @code{interscoreline}@indexcode{interscoreline} Sets the spacing between the score lines. Defaults to 16 pt. - @item @code{textheight}@indexcode{textheight} - Sets the total height of the music on each page. Only used by - ly2dvi. - - @item @code{interscoreline}@indexcode{interscoreline} - Sets the spacing between the score lines. Defaults to 16 pt. - @item @code{output}@indexcode{output} Specifies an alternate name for the the output @file{s}. A @file{.tex}, @file{.midi} or @file{.ps} extension will be diff --git a/NEWS b/NEWS index 27aaa06194..9297c95a40 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,6 @@ +* Finale (.etf) import program. + +* Point and click functionality using emacs and Xdvi. * Improved design and implementation: Complete rewrite of the internals: LilyPond is smaller, cleaner, more flexible, etc. diff --git a/VERSION b/VERSION index fe8e60a2d4..cfec8508be 100644 --- a/VERSION +++ b/VERSION @@ -1,8 +1,8 @@ PACKAGE_NAME=LilyPond MAJOR_VERSION=1 MINOR_VERSION=3 -PATCH_LEVEL=78 -MY_PATCH_LEVEL=jcn5 +PATCH_LEVEL=79 +MY_PATCH_LEVEL= # use the above to send patches: MY_PATCH_LEVEL is always empty for a # released version. diff --git a/input/bugs/almost.ly b/input/bugs/almost.ly index e69de29bb2..7276562718 100644 --- a/input/bugs/almost.ly +++ b/input/bugs/almost.ly @@ -0,0 +1,18 @@ +\score{ + \context Staff < + \context Voice=one { \skip 1; } + \context Voice=two { \skip 1; } + + \context Voice=one \partcombine Voice + \context Thread=one \notes\relative c'' { + a4 c4.()g8 a4 + } + \context Thread=two \notes\relative c'' { + g4 e4.()d8 c4 + } + > + \paper{ + linewidth=60.\mm; + } +} + diff --git a/input/test/hymn.ly b/input/test/hymn.ly index cecbbfc731..8003c4dbd4 100644 --- a/input/test/hymn.ly +++ b/input/test/hymn.ly @@ -1,15 +1,17 @@ \score{ \context Staff < - \time 4/4; + \context Voice=one { \skip 1; } + \context Voice=two { \skip 1; } + \context Voice=one \partcombine Voice \context Thread=one \notes\relative c'' { - a4 c4.()g8 a4 | + %a4 c4.()g8 a4 | g4 e' g()f | b, a c2 } \context Thread=two \notes\relative c'' { - g4 e4.()d8 c4 | - g'4 c, e()f | + %g4 e4.()d8 c4 | + g4 c, e()f | d2 a } > @@ -18,7 +20,13 @@ \translator { \VoiceContext soloADue = ##f + %\remove Slur_engraver; + \consists Slur_engraver; } +% \translator { +% \ThreadContext +% \consists Slur_engraver; +% } } } diff --git a/lily/include/slur.hh b/lily/include/slur.hh index 096d96ab2e..83d0900834 100644 --- a/lily/include/slur.hh +++ b/lily/include/slur.hh @@ -21,7 +21,6 @@ public: static Array get_encompass_offset_arr (Score_element*me) ; static Bezier get_curve (Score_element*me) ; static Direction get_default_dir (Score_element*me) ; - static SCM before_line_breaking (SCM); static SCM after_line_breaking (SCM); static SCM set_spacing_rods (SCM); private: diff --git a/lily/mark-engraver.cc b/lily/mark-engraver.cc index 0f2df8fcdf..b57bafa3e2 100644 --- a/lily/mark-engraver.cc +++ b/lily/mark-engraver.cc @@ -163,7 +163,7 @@ Mark_engraver::do_process_music () */ SCM m = mark_req_l_->get_mus_property ("label"); - if (gh_string_p (m)) + if (!gh_string_p (m)) m = get_property ("rehearsalMark"); ; diff --git a/lily/slur.cc b/lily/slur.cc index 40985d33c0..ccad52d81c 100644 --- a/lily/slur.cc +++ b/lily/slur.cc @@ -150,28 +150,14 @@ Slur::encompass_offset (Score_element*me, return o; } -MAKE_SCHEME_CALLBACK (Slur, before_line_breaking); -SCM -Slur::before_line_breaking (SCM smob) -{ - Score_element *me = unsmob_element (smob); - if (Pointer_group_interface__extract_elements (me, (Score_element*)0, "note-columns").size () < 2) - me->suicide (); - return SCM_UNSPECIFIED; -} - MAKE_SCHEME_CALLBACK(Slur,after_line_breaking); + SCM Slur::after_line_breaking (SCM smob) { Score_element *me = unsmob_element (smob); - if (Pointer_group_interface__extract_elements (me, (Score_element*)0, "note-columns").size () < 2) - me->suicide (); - else - { - set_extremities (me); - set_control_points (me); - } + set_extremities (me); + set_control_points (me); return SCM_UNSPECIFIED; } @@ -295,7 +281,7 @@ Slur::get_attachment (Score_element*me,Direction dir, Array Slur::get_encompass_offset_arr (Score_element*me) { - Spanner*sp = dynamic_cast(me); + Spanner*sp = dynamic_cast(me); SCM eltlist = me->get_elt_property ("note-columns"); Score_element *common[] = {me->common_refpoint (eltlist,X_AXIS), me->common_refpoint (eltlist,Y_AXIS)}; @@ -390,16 +376,11 @@ SCM Slur::brew_molecule (SCM smob) { Score_element * me = unsmob_element (smob); - Molecule a; - if (Pointer_group_interface__extract_elements (me, (Score_element*)0, "note-columns").size () < 2) - { - me->suicide (); - return a.create_scheme (); - } Real thick = me->paper_l ()->get_var ("stafflinethickness") * gh_scm2double (me->get_elt_property ("thickness")); Bezier one = get_curve (me); + Molecule a; SCM d = me->get_elt_property ("dashed"); if (gh_number_p (d)) a = me->lookup_l ()->dashed_slur (one, thick, thick * gh_scm2double (d)); diff --git a/ly/engraver.ly b/ly/engraver.ly index 1829e5f378..6c7ab190c5 100644 --- a/ly/engraver.ly +++ b/ly/engraver.ly @@ -615,7 +615,7 @@ ScoreContext = \translator { ) basicScriptProperties = #`( (molecule-callback . ,Script::brew_molecule) - (interface . (script-interface)) + (interfaces . (script-interface)) ) basicScriptColumnProperties = #`( (before-line-breaking-callback . ,Script_column::before_line_breaking) @@ -625,7 +625,6 @@ ScoreContext = \translator { (thickness . 1.2) (spacing-procedure . ,Slur::set_spacing_rods) e (minimum-length . 1.5) - (before-line-breaking-callback . ,Slur::before_line_breaking) (after-line-breaking-callback . ,Slur::after_line_breaking) ) basicSpacingSpannerProperties =#`( @@ -671,7 +670,7 @@ ScoreContext = \translator { (no-spacing-rods . #t) (molecule-callback . ,Sustain_pedal::brew_molecule) (self-alignment-X . 0) - (interface . (sustain-pedal-interface)) + (interfaces . (sustain-pedal-interface)) ) staffSymbolBasicProperties = #`( (molecule-callback . ,Staff_symbol::brew_molecule) diff --git a/make/out/lilypond.lsm b/make/out/lilypond.lsm index f2b42a8d39..d0c09e90e5 100644 --- a/make/out/lilypond.lsm +++ b/make/out/lilypond.lsm @@ -1,15 +1,15 @@ Begin3 Title: LilyPond -Version: 1.3.78 -Entered-date: 14AUG00 +Version: 1.3.79 +Entered-date: 18AUG00 Description: Keywords: music notation typesetting midi fonts engraving Author: hanwen@cs.uu.nl (Han-Wen Nienhuys) janneke@gnu.org (Jan Nieuwenhuizen) Maintained-by: hanwen@stack.nl (Han-Wen Nienhuys) Primary-site: sunsite.unc.edu /pub/Linux/apps/sound/convert - 1000k lilypond-1.3.78.tar.gz + 1000k lilypond-1.3.79.tar.gz Original-site: ftp.cs.uu.nl /pub/GNU/LilyPond/development/ - 1000k lilypond-1.3.78.tar.gz + 1000k lilypond-1.3.79.tar.gz Copying-policy: GPL End diff --git a/make/out/lilypond.spec b/make/out/lilypond.spec index 2f5db13803..71c92279a7 100644 --- a/make/out/lilypond.spec +++ b/make/out/lilypond.spec @@ -1,9 +1,9 @@ Name: lilypond -Version: 1.3.78 +Version: 1.3.79 Release: 1 Copyright: GPL Group: Applications/Publishing -Source0: ftp.cs.uu.nl:/pub/GNU/LilyPond/development/lilypond-1.3.78.tar.gz +Source0: ftp.cs.uu.nl:/pub/GNU/LilyPond/development/lilypond-1.3.79.tar.gz Summary: A program for printing sheet music. URL: http://www.cs.uu.nl/~hanwen/lilypond # Icon: lilypond-icon.gif diff --git a/mutopia/Coriolan/flauti-part.ly b/mutopia/Coriolan/flauti-part.ly index 182f57d232..64c15a8d61 100644 --- a/mutopia/Coriolan/flauti-part.ly +++ b/mutopia/Coriolan/flauti-part.ly @@ -16,30 +16,7 @@ copyright = "public domain"; \score{ \$flauti_staff - %\include "coriolan-part-paper.ly" - - \paper { - textheight = 295.0\mm; - linewidth = 180.0\mm; - - % slurs are never beautiful (no steep slurs) - slur_beautiful = 0.0; - - \translator { - \VoiceContext - \remove Slur_engraver; - } - \translator { - \ScoreContext skipBars = ##t - %% URG: this changes dynamics too - %%textStyle = #"italic" - timeSignatureStyle = #"C" - instrumentScriptPadding = #60 %% urg, this is in pt - instrScriptPadding = #40 %% urg, this is in pt - marginScriptHorizontalAlignment = #1 - maximumRestCount = #1 - } - } + \include "coriolan-part-paper.ly" \include "coriolan-midi.ly" } diff --git a/mutopia/Coriolan/flauti.ly b/mutopia/Coriolan/flauti.ly index 9fe7ce0674..4de0fdd353 100644 --- a/mutopia/Coriolan/flauti.ly +++ b/mutopia/Coriolan/flauti.ly @@ -14,18 +14,17 @@ copyright = "public domain"; \include "flauto-1.ly" \include "flauto-2.ly" -$flauti_staff = \notes \context Staff = flauti < +$flauti_staff = \context Staff = flauti < \property Staff.midiInstrument = #"flute" \property Staff.instrument = #"2 Flauti" \property Staff.instr = #"Fl." - - \global - - \context Voice=one { \skip 1; } - \context Voice=two { \skip 1; } - - \context Voice=one \partcombine Voice - \context Thread=one \$flauto1 - \context Thread=two \$flauto2 + %\notes \context Voice=flauti < + \notes \context Staff=flauti < + \global + \context VoiceOne=flautoi + \$flauto1 + \context VoiceTwo=flautoii + \$flauto2 + > > diff --git a/scm/slur.scm b/scm/slur.scm index 8863368991..dadeb3056c 100644 --- a/scm/slur.scm +++ b/scm/slur.scm @@ -28,9 +28,6 @@ ;; (cons (lambda (slur dir) (begin (display "before head") (newline))#f) #f) - (cons (lambda (slur dir) - (< (length (ly-get-elt-property slur 'note-columns)) 2)) 'head) - (cons (lambda (slur dir) ;; urg, code dup (let* ((note-columns (ly-get-elt-property slur 'note-columns)) diff --git a/scripts/etf2ly.py b/scripts/etf2ly.py new file mode 100644 index 0000000000..7a99aeeb6d --- /dev/null +++ b/scripts/etf2ly.py @@ -0,0 +1,730 @@ +#!@PYTHON@ + +# info taken from + +# * convertmusic (see sourceforge) +# * Margaret Cahill's thesis +# * http://www.codamusic.com/coda/Fin2000_pdk_download.asp (you have +# to register, but you don't need to have bought a code product + +# +# This supports + +# +# * notes +# * rests +# * ties +# * slurs +# + +# todo: +# * slur/stem directions +# * articulation +# * voices (2nd half of frame?) +# * lyrics +# * beams (better use autobeam?) + +program_name = 'etf2ly' +version = '@TOPLEVEL_VERSION@' +if version == '@' + 'TOPLEVEL_VERSION' + '@': + version = '(unknown version)' # uGUHGUHGHGUGH + +import __main__ +import getopt +import sys +import re +import string +import os + + + +finale_clefs= ['treble', 'alto', 'tenor', 'bass', 'percussion', 'treble8vb', 'bass8vb', 'baritone'] + + +def lily_clef (fin): + return finale_clefs[fin] + +def gulp_file(f): + return open (f).read () + +# notename 0 == central C +distances = [0, 2, 4, 5, 7, 9, 11, 12] +def semitones (name, acc): + return (name / 7 ) * 12 + distances[name % 7] + acc + +def transpose(orig, delta): + (oname, oacc) = orig + (dname, dacc) = delta + + old_pitch =semitones (oname, oacc) + delta_pitch = semitones (dname, dacc) + nname = (oname + dname) + nacc = oacc + new_pitch = semitones (nname, nacc) + + nacc = nacc - (new_pitch - old_pitch - delta_pitch) + + return (nname, nacc) + + + +# find transposition of C-major scale that belongs here. +def interpret_finale_key_sig (finale_id): + p = (0,0) + if 0 <= finale_id < 7: + while finale_id > 0: + p = transpose (p, (4,0)) # a fifth up + finale_id = finale_id - 1 + elif 248 < finale_id <= 255: + while finale_id < 256: + p = transpose (p, (3,0)) + finale_id = finale_id + 1 + + p = (p[0] % 7, p[1]) + return p + +# should cache this. +def find_scale (transposition): + cscale = map (lambda x: (x,0), range (0,7)) + trscale = map(lambda x, k=transposition: transpose(x, k), cscale) + + return trscale + +def gcd (a,b): + if b == 0: + return a + c = a + while c: + c = a % b + a = b + b = c + return a + + +def rat_simplify (r): + (n,d) = r + if d < 0: + d = -d + n = -n + if n == 0: + return (0,1) + else: + g = gcd (n, d) + return (n/g, d/g) + +def rat_multiply (a,b): + (x,y) = a + (p,q) = b + + return rat_simplify ((x*p, y*q)) + +def rat_add (a,b): + (x,y) = a + (p,q) = b + + return rat_simplify ((x*q + p*y, y*q)) + +def rat_neg (a): + (p,q) = a + return (-p,q) + +def rat_subtract (a,b ): + return rat_add (a, rat_neg (b)) + +def lily_notename (tuple2): + (n, a) = tuple2 + nn = chr ((n+ 2)%7 + ord ('a')) + + if a == -1: + nn = nn + 'es' + elif a == -2: + nn = nn + 'eses' + elif a == 1: + nn = nn + 'is' + elif a == 2: + nn = nn + 'isis' + + return nn + +class Slur: + def __init__ (self, number): + self.number = number + self.finale = [] + + def append_entry (self, finale_e): + self.finale.append (finale_e) + + def calculate (self, chords): + startnote = self.finale[0][5] + endnote = self.finale[3][2] + + cs = chords[startnote] + cs.suffix = '(' + cs.suffix + ce = chords[endnote] + ce.prefix = ce.prefix + ')' + +class Global_measure: + def __init__ (self, number): + self.timesig = '' + self.number = number + self.keysignature = None + self.scale = None + + self.finale = [] + + def set_timesig (self, finale): + (beats, fdur) = finale + (log, dots) = EDU_to_duration (fdur) + assert dots == 0 + self.timesig = (beats, log) + + def length (self): + return self.timesig + + def set_keysig (self, finale): + k = interpret_finale_key_sig (finale) + self.keysignature = k + self.scale = find_scale (k) + + + +class Measure: + def __init__(self, no): + self.number = no + self.frames = [] + self.flags = 0 + self.clef = 0 + self.finale = [] + self.global_measure = None + + def add_finale_entry (self, entry): + self.finale.append (entry) + + def calculate (self): + f0 = self.finale[0] + f1 = self.finale[1] + + self.clef = string.atoi (f0[0]) + self.flags = string.atoi (f0[1]) + fs = map (string.atoi, list (f0[2:]) + [f1[0]]) + + self.frames = fs + +class Frame: + def __init__ (self, finale): + self.measure = None + self.finale = finale + (number, start, end ) = finale + self.number = number + self.start = start + self.end = end + self.chords = [] + + def set_measure (self, m): + self.measure = m + + def dump (self): + str = '' + left = self.measure.global_measure.length () + for c in self.chords: + str = str + c.ly_string () + ' ' + left = rat_subtract (left, c.length ()) + if left[0] < 0: + print self.number + print self.start, self.end + print left + raise 'bla' + if left[0]: + str = str + 's*%d/%d' % left + + str = str + '\n' + return str + +class Staff: + def __init__ (self, number): + self.number = number + self.measures = [] + + def get_measure (self, no): + if len (self.measures) <= no: + self.measures = self.measures + [None]* (1 + no - len (self.measures)) + + if self.measures[no] == None: + self.measures [no] = Measure (no) + + return self.measures[no] + def staffid (self): + return 'staff%s'% chr (self.number - 1 +ord ('A')) + def layerid (self, l): + return self.staffid() + 'layer%s' % chr (l -1 + ord ('A')) + + def dump_time_key_sigs (self): + k = '' + last_key = None + last_time = None + last_clef = None + gap = (0,1) + for m in self.measures[1:]: + g = m.global_measure + e = '' + if last_key <> g.keysignature: + e = e + "\\key %s \\major; " % lily_notename (g.keysignature) + last_key = g.keysignature + if last_time <> g.timesig : + e = e + "\\time %d/%d; " % g.timesig + last_time = g.timesig + if last_clef <> m.clef : + e = e + '\\clef %s;' % lily_clef (m.clef) + last_clef = m.clef + if e: + if gap <> (0,1): + k = k +' s1*%d/%d \n ' % gap + gap = (0,1) + k = k + e + + gap = rat_add (gap, g.length ()) + + + k = '%sglobal = \\notes { %s }\n\n ' % (self.staffid (), k) + return k + + def dump (self): + str = '' + + + layerids = [] + for x in range (1,5): # 4 layers. + laystr = '' + last_frame = None + first_frame = None + gap = (0,1) + for m in self.measures[1:]: + fr = m.frames[x] + if fr: + first_frame = fr + if gap <> (0,1): + laystr = laystr +'} s1*%d/%d {\n ' % gap + gap = (0,1) + laystr = laystr + fr.dump () + else: + gap = rat_add (gap, m.global_measure.length ()) + + if first_frame: + l = self.layerid (x) + laystr = '%s = \\notes { { %s } }\n\n' % (l, laystr) + str = str + laystr + layerids.append (l) + + str = str + self.dump_time_key_sigs () + stafdef = '\\%sglobal' % self.staffid () + for i in layerids: + stafdef = stafdef + ' \\' + i + + + str = str + '%s = \\context Staff = %s <\n %s\n >\n' % \ + (self.staffid (), self.staffid (), stafdef) + return str + + +def EDU_to_duration (edu): + log = 1 + d = 4096 + while d > edu: + d = d >> 1 + log = log << 1 + + edu = edu - d + dots = 0 + if edu == d /2: + dots = 1 + elif edu == d*3/4: + dots = 2 + return (log, dots) + +class Chord: + def __init__ (self, finale_entry): + self.pitches = [] + self.frame = None + self.finale = finale_entry + self.duration = None + self.next = None + self.prev = None + self.prefix= '' + self.suffix = '' + def measure (self): + if not self.frame: + return None + return self.frame.measure + + def length (self): + l = (1, self.duration[0]) + + d = 1 << self.duration[1] + + dotfact = rat_subtract ((2,1), (1,d)) + return rat_multiply (dotfact, l) + + def number (self): + return self.finale[0][0] + def set_duration (self): + ((no, prev, next, dur, pos, entryflag, extended, follow), + notelist) = self.finale + self.duration = EDU_to_duration(dur) + def find_realpitch (self): + + ((no, prev, next, dur, pos, entryflag, extended, follow), notelist) = self.finale + + meas = self.measure () + tiestart = 0 + if not meas or not meas.global_measure : + print 'note %d not in measure' % self.number () + elif not meas.global_measure.scale: + print 'note %d: no scale in this measure.' % self.number () + else: + for p in notelist: + (pitch, flag) = p + + nib1 = pitch & 0x0f + if nib1 > 8: + nib1 = -(nib1 - 8) + rest = pitch / 16 + + scale = meas.global_measure.scale + (sn, sa) =scale[rest % 7] + sn = sn + (rest - (rest%7)) + 7 + acc = sa + nib1 + self.pitches.append ((sn, acc)) + tiestart = tiestart or (flag & Chord.TIE_START_MASK) + if tiestart : + self.suffix = self.suffix + ' ~ ' + + REST_MASK = 0x40000000L + TIE_START_MASK = 0x40000000L + def ly_string (self): + s = '' + + rest = '' + + if not (self.finale[0][5] & Chord.REST_MASK): + rest = 'r' + + for p in self.pitches: + (n,a) = p + o = n/ 7 + n = n % 7 + + nn = lily_notename ((n,a)) + + if o < 0: + nn = nn + (',' * -o) + elif o > 0: + nn = nn + ('\'' * o) + + if s: + s = s + ' ' + + if rest: + nn = rest + + s = s + '%s%d%s' % (nn, self.duration[0], '.'* self.duration[1]) + + if len (self.pitches) > 1: + s = '<%s>' % s + elif not self.pitches: + s = 'r%d%s' % (self.duration[0] , '.'* self.duration[1]) + + s = self.prefix + s + self.suffix + return s + +GFre = re.compile(r"""^\^GF\(([0-9-]+),([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") +BCre = re.compile (r"""^\^BC\(([0-9-]+)\) ([0-9-]+) .*$""") +eEre = re.compile(r"""^\^eE\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) \$([0-9A-Fa-f]+) ([0-9-]+) ([0-9-]+)""") +FRre = re.compile (r"""^\^FR\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") +MSre = re.compile (r"""^\^MS\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") +note_re = re.compile (r"""^ +([0-9-]+) \$([A-Fa-f0-9]+)""") +Sxre = re.compile (r"""^\^Sx\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") + +class Etf_file: + def __init__ (self, name): + self.measures = [None] + self.entries = [None] + self.chords = [None] + self.frames = [None] + self.staffs = [None] + self.slurs = [None] + + ## do it + self.parse (name) + + def get_global_measure (self, no): + if len (self.measures) <= no: + self.measures = self.measures + [None]* (1 + no - len (self.measures)) + + if self.measures[no] == None: + self.measures [no] = Global_measure (no) + + return self.measures[no] + + + def get_staff(self,staffno): + if len (self.staffs) <= staffno: + self.staffs = self.staffs + [None] * (1 + staffno - len (self.staffs)) + + if self.staffs[staffno] == None: + self.staffs[staffno] = Staff (staffno) + + return self.staffs[staffno] + + # staff-spec + def try_IS (self, l): + pass + + def try_BC (self, l): + m = BCre.match (l) + if m: + bn = string.atoi (m.group (1)) + where = string.atoi (m.group (2)) / 1024.0 + return m + + def try_eE (self, l): + m = eEre.match (l) + if m: + tup = m.groups() + (no, prev, next, dur, pos, entryflag, extended, follow) = tup + (no, prev, next, dur, pos,extended, follow) \ + = tuple (map (string.atoi, [no,prev,next,dur,pos,extended,follow])) + + entryflag = string.atol (entryflag,16) + assert (no==len (self.entries)) + current_entry = ((no, prev, next, dur, pos, entryflag, extended, follow), []) + self.entries.append (current_entry) + return m + + def try_Sx(self,l): + m = Sxre.match (l) + if m: + slurno = string.atoi (m.group (1)) + + if len (self.slurs) == slurno: + self.slurs.append (Slur (slurno)) + + params = list (m.groups ()[1:]) + params = map (string.atoi, params) + self.slurs[-1].append_entry (params) + + return m + def try_GF(self, l): + m = GFre.match (l) + if m: + (staffno,measno) = m.groups ()[0:2] + s = string.atoi (staffno) + me = string.atoi (measno) + + entry = m.groups () [2:] + st = self.get_staff (s) + meas = st.get_measure (me) + meas.add_finale_entry (entry) + + # frame ? + def try_FR(self, l): + m = FRre.match (l) + if m: + (frameno, startnote, endnote, foo, bar) = m.groups () + (frameno, startnote, endnote) = tuple (map (string.atoi, [frameno, startnote, endnote])) + self.frames.append (Frame ((frameno, startnote, endnote))) + + return m + def try_MS (self, l): + m = MSre.match (l) + if m: + measno = string.atoi (m.group (1)) + keynum = string.atoi (m.group (3)) + meas =self. get_global_measure (measno) + meas.set_keysig (keynum) + + beats = string.atoi (m.group (4)) + beatlen = string.atoi (m.group (5)) + meas.set_timesig ((beats, beatlen)) + + return m + + def try_note (self, l): + m = note_re.match (l) + if m: + (pitch, flag) = m.groups () + pitch = string.atoi (pitch) + flag = string.atol (flag,16) + self.entries[-1][1].append ((pitch,flag)) + + def parse (self, name): + sys.stderr.write ('parsing ...') + sys.stderr.flush () + + ls = open (name).readlines () + for l in ls: + m = None + if not m: + m = self.try_MS (l) + if not m: + m = self.try_FR (l) + if not m: + m = self.try_GF (l) + if not m: + m = self.try_note (l) + if not m: + m = self.try_eE (l) + if not m: + m = self.try_Sx (l) + + sys.stderr.write ('processing ...') + sys.stderr.flush () + + self.unthread_entries () + + for st in self.staffs[1:]: + if not st: + continue + mno = 1 + for m in st.measures[1:]: + m.global_measure = self.measures[mno] + m.calculate() + + frame_obj_list = [None] + for frno in m.frames: + fr = self.frames[frno] + frame_obj_list.append (fr) + + m.frames = frame_obj_list + for fr in frame_obj_list[1:]: + if not fr: + continue + + fr.set_measure (m) + + fr.chords = self.get_thread (fr.start, fr.end) + for c in fr.chords: + c.frame = fr + mno = mno + 1 + + for c in self.chords[1:]: + c.find_realpitch () + c.set_duration () + + for s in self.slurs [1:]: + s.calculate (self.chords) + + def get_thread (self, startno, endno): + + thread = [] + c = self.chords[startno] + while c and c.number () <> endno: + thread.append (c) + c = c.next + + if c: + thread.append (c) + + return thread + + def dump (self): + str = '' + staffs = [] + for s in self.staffs[1:]: + if s: + str = str + '\n\n' + s.dump () + staffs.append ('\\' + s.staffid ()) + + if staffs: + str = str + '\\score { < %s > } ' % string.join (staffs) + + + return str + + + def __str__ (self): + return self.dump () + + def unthread_entries (self): + self.chords = [None] + for e in self.entries[1:]: + self.chords.append (Chord (e)) + + for e in self.chords[1:]: + e.prev = self.chords[e.finale[0][1]] + e.next = self.chords[e.finale[0][2]] + + + + + + +def identify(): + sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version)) + +def help (): + print r""" +Convert ETF to LilyPond. + +Usage: etf2ly [OPTION]... ETF-FILE + +Options: + -h, --help this help + -o, --output=FILE set output filename to FILE + -v, --version version information + +Enigma Transport Format is a format used by Coda Music Technology's +Finale product. This program will convert a subset of ETF to a +ready-to-use lilypond file. + + +""" + +def print_version (): + print r"""etf2ly (GNU lilypond) %s""" % version + + + +(options, files) = getopt.getopt (sys.argv[1:], 'vo:h', ['help','version', 'output=']) +out_filename = None + +for opt in options: + o = opt[0] + a = opt[1] + if o== '--help' or o == '-h': + help () + sys.exit (0) + if o == '--version' or o == '-v': + print_version () + sys.exit(0) + + if o == '--output' or o == '-o': + out_filename = a + else: + print o + raise getopt.error + +identify() + +# header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version +for f in files: + if f == '-': + f = '' + + sys.stderr.write ('Processing `%s\'\n' % f) + e = Etf_file(f) + if not out_filename: + out_filename = os.path.basename (re.sub ('.etf$', '.ly', f)) + + if out_filename == f: + out_filename = os.path.basename (f + '.ly') + + sys.stderr.write ('Writing `%s\'' % out_filename) + ly = e.dump() + + + + fo = open (out_filename, 'w') + fo.write ('%% lily was here -- automatically converted by etf2ly from %s\n' % f) + fo.write(ly) + fo.close () + -- 2.39.2