From: W. Trevor King Date: Wed, 8 Dec 2010 13:09:32 +0000 (-0500) Subject: Extend abcplay.py to also handle LilyPond input. X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=f5579ae88d18dc18a1b8a071a560e0a65d6a7044;p=blog.git Extend abcplay.py to also handle LilyPond input. --- diff --git a/posts/abcplay.mdwn b/posts/abcplay.mdwn index 6df247c..9d65958 100644 --- a/posts/abcplay.mdwn +++ b/posts/abcplay.mdwn @@ -20,7 +20,9 @@ search tools and repositories listed on the [ABC homepage][ABC] and To make playing ABC files as easy as possible, I've written a little script, [[abcplay.py]], which uses `abc2midi` to generate a MIDI file -for each tune and then plays them with [timidity++][]. +for each tune and then plays them with [timidity++][]. Because the +processing is so similar, `abcplay.py` also plays [LilyPond][] files, +using `lilypond` to convert them to MIDI. [Bill Whipple]: http://www.wiplstix.com/ [ABC]: http://abcnotation.com/ @@ -31,6 +33,7 @@ for each tune and then plays them with [timidity++][]. [Sunrise overlay]: http://overlays.gentoo.org/proj/sunrise [The Session]: http://www.thesession.org/ [timidity++]: http://timidity.sourceforge.net/ +[LilyPond]: http://lilypond.org/ [[!tag tags/fun]] [[!tag tags/python]] diff --git a/posts/abcplay/abcplay.py b/posts/abcplay/abcplay.py index 8464164..b708209 100755 --- a/posts/abcplay/abcplay.py +++ b/posts/abcplay/abcplay.py @@ -31,10 +31,16 @@ SIGINT (usually ^C) will kill the current tune and proceed the next one. Two SIGINTs in less than one second will kill the abcplay.py process. This should be familiar to users of mpg123_ or ogg123_. +You can also play LilyPond_ files (converted to MIDI via ``lilypond``) +by using the ``--ly`` option:: + + abcplay.py --ly somefile.ly anotherfile.ly + .. _abc2midi: http://abc.sourceforge.net/abcMIDI/ .. _timidity: http://timidity.sourceforge.net/ .. _mpg123: http://www.mpg123.org/ .. _ogg123: http://www.vorbis.com +.. _LilyPond: http://lilypond.org/ """ import shlex @@ -42,16 +48,16 @@ from subprocess import Popen from tempfile import mkstemp from time import time from os import remove +import os.path -REFNUM_SEP = ':' - -class ABCPlayer (object): - def __init__(self, abc2midi_options=None, timidity_options=None): +class MIDIPlayer (object): + def __init__(self, to_midi_program=None, + to_midi_options=None, timidity_options=None): f,self._tempfile = mkstemp(prefix='abcplay-', suffix='.midi') - self._abc2midi = ['abc2midi'] - if abc2midi_options: - self._abc2midi.extend(abc2midi_options) + self._to_midi = [to_midi_program] + if to_midi_options: + self._to_midi.extend(to_midi_options) self._timidity = ['timidity'] if timidity_options: self._timidity.extend(timidity_options) @@ -62,21 +68,7 @@ class ABCPlayer (object): remove(self._tempfile) def play_files(self, filenames): - for filename in filenames: - if REFNUM_SEP in filename: - fields = filename.split(REFNUM_SEP) - filename = fields[0] - refnums = fields[1:] - else: - refnums = list(self._refnums(filename)) - while len(refnums) > 0: - refnum = refnums.pop(0) - try: - self.play(filename, refnum) - except KeyboardInterrupt: - self._kill_p() - if self._return_after_interrupt(): - return + raise NotImplementedError() def _return_after_interrupt(self): t = time() @@ -84,21 +76,12 @@ class ABCPlayer (object): self._last_interrupt = t return ret - def _refnums(self, filename): - with open(filename, 'r') as f: - for line in f: - if line.startswith('X:'): - yield int(line[len('X:'):]) - - def play(self, filename, refnum): - self._convert_to_midi(filename, refnum) + def play(self, filename, **kwargs): + self._convert_to_midi(filename, **kwargs) self._play_midi() - def _convert_to_midi(self, filename, refnum): - self._p = Popen( - self._abc2midi + [filename, str(refnum), '-o', self._tempfile]) - self._p.wait() - self._p = None + def _convert_to_midi(self, filename, **kwargs): + raise NotImplementedError() def _play_midi(self): self._p = Popen(self._timidity + [self._tempfile]) @@ -118,6 +101,76 @@ class ABCPlayer (object): self._p.wait() self._p = None + +class ABCPlayer (MIDIPlayer): + refnum_sep = ':' + + def __init__(self, abc2midi_options=None, timidity_options=None): + super(ABCPlayer, self).__init__( + 'abc2midi', abc2midi_options, timidity_options) + + def play_files(self, filenames): + for filename in filenames: + if self.refnum_sep in filename: + fields = filename.split(self.refnum_sep) + filename = fields[0] + refnums = fields[1:] + else: + refnums = list(self._refnums(filename)) + while len(refnums) > 0: + refnum = refnums.pop(0) + try: + self.play(filename, refnum=refnum) + except KeyboardInterrupt: + self._kill_p() + if self._return_after_interrupt(): + return + + def _refnums(self, filename): + with open(filename, 'r') as f: + for line in f: + if line.startswith('X:'): + yield int(line[len('X:'):]) + + def _convert_to_midi(self, filename, refnum): + self._p = Popen( + self._to_midi + [filename, str(refnum), '-o', self._tempfile]) + self._p.wait() + self._p = None + + +class LilyPondPlayer (MIDIPlayer): + def __init__(self, lilypond_options=None, timidity_options=None): + default_lilypond_options = [ + '-dbackend=null', # don't create a typeset version + '-ddelete-intermediate-files', # clean up after ourselves + ] + if lilypond_options: + lilypond_options = default_lilypond_options + lilypond_options + else: + lilypond_options = default_lilypond_options + super(LilyPondPlayer, self).__init__( + 'lilypond', lilypond_options, timidity_options) + + def play_files(self, filenames): + for filename in filenames: + try: + self.play(filename) + except KeyboardInterrupt: + self._kill_p() + if self._return_after_interrupt(): + return + + def _convert_to_midi(self, filename): + ofilename,ext = os.path.splitext(self._tempfile) + assert ext == '.midi', ext # lilypond adds suffix automatically + #-dmidi-extension=midi + self._p = Popen( + self._to_midi + ['-o', ofilename, filename]) + self._p.wait() + self._p = None + + if __name__ == '__main__': import sys import optparse @@ -130,6 +183,10 @@ if __name__ == '__main__': help='Extra options to pass to abc2midi.') p.add_option('-t', '--timidity', dest='timidity', help='Extra options to pass to timidity.') + p.add_option('-l', '--ly', dest='use_lilypond', action='store_true', + help='Use LilyPond input instead of ABC.') + p.add_option('--lilypond', dest='lilypond', + help='Extra options to pass to LilyPond.') options,args = p.parse_args() del p @@ -139,8 +196,14 @@ if __name__ == '__main__': timidity = options.timidity if timidity: timidity = shlex.split(timidity) - - p = ABCPlayer(abc2midi, timidity) + lilypond = options.lilypond + if lilypond: + lilypond = shlex.split(lilypond) + + if options.use_lilypond: + p = LilyPondPlayer(lilypond, timidity) + else: + p = ABCPlayer(abc2midi, timidity) try: p.play_files(args) finally: