LilyPond. The sources for the font are found in mf/*.mf.
The feta font is merged from a number of subfonts. Each subfont
-can contain at most 223 glyphs.
+can contain at most 224 glyphs. This is because each subfont is
+limited to a one-byte address space (256 glyphs maximum) and we
+avoid the first 32 points in that address space, since they are
+non-printing control characters in ASCII.
In LilyPond, glyphs are accessed by glyph name, rather than by code point.
Therefore, the naming of glyphs is significant.
mf2pt1 is used to create type 1 fonts from the metafont sources.
-FontForge is used to display the resulting glyph shapes.
+FontForge is used to postprocess the output of mf2pt1 and clean up
+details of the font. It can also be used by a developer to
+display the resulting glyph shapes.
@node Adding a new font section
@section Adding a new font section
The font is divided into sections, each of which contains less
-than 223 glyphs. If more than 223 glyphs are included in a section,
+than 224 glyphs. If more than 224 glyphs are included in a section,
an error will be generated.
Each of the sections is contained in a separate @code{.mf} file. The
generic file
@item
If necessary, new entries in the GNUmakefile
+@item
+An entry in @file{scripts/build/gen-emmentaler-scripts.py}
@end itemize
See the examples in @code{mf/} for more information.
rm mf/out/*
make
@end example
-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; glissando
-(define-public (glissando::before-line-breaking grob)
- "Establishes the Y terminus points of the glissando based on the pre-broken
-positions of its left and right bounds."
- (let ((bound-details (ly:grob-property grob 'bound-details))
+(define-public (glissando::make-simple-y grob)
+ "Establishes the Y terminus points of the glissando based on the
+pre-broken positions of its left and right bounds."
+ (let* ((siblings (ly:spanner-broken-into (ly:grob-original grob)))
+ (bound-details (ly:grob-property grob 'bound-details))
(extra-dy (ly:grob-property grob 'extra-dy 0.0)))
- (for-each (lambda (dir-sym)
- (let* ((details (assoc-get dir-sym bound-details))
- (dir (if (eq? dir-sym 'left) LEFT RIGHT))
- (bound (ly:spanner-bound grob dir))
- (common-y (ly:grob-common-refpoint grob bound Y))
- (y (+ (interval-center (ly:grob-extent bound common-y Y))
- (/ (* dir extra-dy)
- 2))))
- (if (not (assoc-get 'Y details))
- (set! bound-details (assoc-set! bound-details dir-sym
- (acons 'Y y details))))))
- '(left right))
-
- (set! (ly:grob-property grob 'bound-details) bound-details)))
+ (and (pair? siblings)
+ (for-each (lambda (dir-sym)
+ (let* ((details (assoc-get dir-sym bound-details))
+ (dir (if (eq? dir-sym 'left) LEFT RIGHT))
+ (good-grob (if (eq? dir-sym 'left)
+ (first siblings)
+ (last siblings)))
+ (bound (ly:spanner-bound good-grob dir))
+ (common-y (ly:grob-common-refpoint good-grob
+ bound
+ Y))
+ (y (+ (interval-center (ly:grob-extent bound
+ common-y
+ Y))
+ (/ (* dir extra-dy)
+ 2))))
+ (if (not (assoc-get 'Y details))
+ (set! bound-details (assoc-set! bound-details dir-sym
+ (acons 'Y y details))))))
+ '(left right))
+
+ (set! (ly:grob-property grob 'bound-details) bound-details))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
help="prefix filtered track numbers with PREFIX")
parser.add_option ('', '--dump', action='store_true', dest='dump',
help="just dump parsed contents of the MIDI file")
+ parser.add_option ('', '--pretty', action='store_true', dest='pretty',
+ help="dump parsed contents of the MIDI file in human-readable form (implies --dump)")
parser.usage = parser.usage + " FILE"
options, args = parser.parse_args (args)
if len (args) != 1:
track_info.append ((i, track_name (tracks[i])))
return track_info
+
+class formatter:
+ def __init__ (self, txt = ""):
+ self.text = txt
+ def format_vals (self, val1, val2 = ""):
+ return str (val1) + str(val2)
+ def format (self, val1, val2 = ""):
+ return self.text + self.format_vals (val1, val2)
+class none_formatter (formatter):
+ def format_vals (self, val1, val2 = ""):
+ return ''
+class meta_formatter (formatter):
+ def format_vals (self, val1, val2):
+ return str (val2);
+class tempo_formatter (formatter):
+ def format_vals (self, val1, val2):
+ return str (val2) + " msec/quarter"
+
+class time_signature_formatter (formatter):
+ def format_vals (self, val1, val2 = ""):
+ return str (val2) # TODO
+class key_signature_formatter (formatter):
+ def format_vals (self, val1, val2):
+ return str (val2) # TODO
+class channel_formatter (formatter):
+ def __init__ (self, txt, ch):
+ formatter.__init__ (self, txt)
+ self.channel = ch
+ def format (self, val1, val2 = ""):
+ return self.text + "Channel " + str (self.channel) + ", " + \
+ self.format_vals (val1, val2)
+class control_mode_formatter (formatter):
+ def __init__ (self, txt, ch):
+ formatter.__init__ (self, txt)
+ self.mode = ch
+ def format (self, val1, val2 = ""):
+ return self.text + str (self.mode) + ", " + \
+ self.format_vals (val1, val2)
+class note_formatter (channel_formatter):
+ def pitch (self, val):
+ pitch_names = ['C', 'Cis', 'D', 'Dis', 'E', 'F', 'Fis', 'G', 'Gis', 'A', 'Ais', 'B'];
+ p = val % 12;
+ oct = val / 12 -1;
+ return pitch_names[p] + str(oct) + "(" + str(val) + ")"
+ def velocity (self, val):
+ #01 #10 #20 #30 #40 #50 #60 #70 #7F
+ pass;
+ def format_vals (self, val1, val2):
+ return self.pitch (val1)
+
+
+meta_dict = {0x00: meta_formatter ("Seq.Nr.: "),
+ 0x01: meta_formatter ("Text: "),
+ 0x02: meta_formatter ("Copyright: "),
+ 0x03: meta_formatter ("Track name: "),
+ 0x04: meta_formatter ("Instrument: "),
+ 0x05: meta_formatter ("Lyric: "),
+ 0x06: meta_formatter ("Marker: "),
+ 0x07: meta_formatter ("Cue point: "),
+ 0x2F: none_formatter ("End of Track"),
+ 0x51: tempo_formatter ("Tempo: "),
+ 0x54: meta_formatter ("SMPTE Offs.:"),
+ 0x58: time_signature_formatter ("Time signature: "),
+ 0x59: key_signature_formatter ("Key signature: ")
+}
+
+def dump_event (ev, time, padding):
+ ch = ev[0] & 0x0F;
+ func = ev[0] & 0xF0;
+ f = None
+ if (ev[0] == 0xFF):
+ f = meta_dict.get (ev[1], formatter ())
+ if (func == 0x80):
+ f = note_formatter ("Note off: ", ch)
+ elif (func == 0x90):
+ if (ev[2] == 0):
+ desc = "Note off: "
+ else:
+ desc = "Note on: "
+ f = note_formatter (desc, ch)
+ elif (func == 0xA0):
+ f = note_formatter ("Polyphonic aftertouch: ", ch, "Aftertouch pressure: ")
+ elif (func == 0xB0):
+ f = control_mode_formatter ("Control mode change: ", ch)
+ elif (func == 0xC0):
+ f = channel_formatter ("Program Change: ", ch)
+ elif (func == 0xD0):
+ f = channel_formatter ("Channel aftertouch: ", ch)
+ elif (ev[0] in [0xF0, 0xF7]):
+ f = meta_formatter ("System-exclusive event: ")
+
+ if f:
+ if len (ev) > 2:
+ print padding + f.format (ev[1], ev[2])
+ elif len (ev) > 1:
+ print padding + f.format (ev[1])
+ else:
+ print padding + f.format ()
+ else:
+ print padding + "Unrecognized MIDI event: " + str (ev);
+
+def dump_midi (data, midi_file, options):
+ if not options.pretty:
+ print data
+ return
+ # First, dump general info, #tracks, etc.
+ print "Filename: " + midi_file;
+ i = data[0];
+ m_formats = {0: 'single multi-channel track',
+ 1: "one or more simultaneous tracks",
+ 2: "one or more sequentially independent single-track patterns"}
+ print "MIDI format: " + str (i[0]) + " (" + m_formats.get (i[0], "") + ")";
+ print "Divisions: " + str (i[1]) + " per whole note";
+ print "#Tracks: " + str ( len (data[1]))
+ n = 0;
+ for tr in data[1]:
+ time = 0;
+ n += 1;
+ print
+ print "Track " + str(n) + ":"
+ print " Time 0:"
+ for ev in tr:
+ if ev[0]>time:
+ time = ev[0]
+ print " Time " + str(time) + ": "
+ dump_event (ev[1], time, " ");
+
+
+
def go ():
options, args = process_options (sys.argv[1:])
midi_file = args[0]
midi_data = read_midi (midi_file)
info = track_info (midi_data)
- if options.dump:
- print midi_data
+ if (options.dump or options.pretty):
+ dump_midi (midi_data, midi_file, options);
elif options.regexp:
import re
regexp = re.compile (options.regexp)